require 'base64' require 'digest' require 'openssl' require 'time' require 'uri' # gem 'httparty', '~> 0.13.0' require 'httparty' # Version 1.0.1 class Client include HTTParty attr_reader :signer def initialize(key_id, private_key) @signer = Signer.new(key_id, private_key) end # nothing to sign for :options [:get, :head, :delete].each do |method| define_method(method) do |uri, headers: {}| self.signer.sign(method, uri, headers, body: nil) self.class.send(method, uri, :headers => headers) end end [:put, :post].each do |method| define_method(method) do |uri, headers: {}, body: ""| self.signer.sign(method, uri, headers, body) self.class.send(method, uri, :headers => headers, :body => body) end end end class Signer class << self attr_reader :headers end attr_reader :key_id, :private_key generic_headers = [:"date", :"(request-target)", :"host"] body_headers = [ :"content-length", :"content-type", :"x-content-sha256"] @headers = { get: generic_headers, head: generic_headers, delete: generic_headers, put: generic_headers + body_headers, post: generic_headers + body_headers } def initialize(key_id, private_key) @key_id = key_id @private_key = private_key end def sign(method, uri, headers, body) uri = URI(uri) path = uri.query.nil? ? uri.path : "#{uri.path}?#{uri.query}" self.inject_missing_headers(headers, method, body, uri) signature = self.compute_signature(headers, method, path) unless signature.nil? self.inject_authorization_header(headers, method, signature) end end def inject_missing_headers(headers, method, body, uri) headers["content-type"] ||= "application/json" headers["date"] ||= Time.now.utc.httpdate headers["accept"] ||= "*/*" headers["host"] ||= uri.host if method == :put or method == :post body ||= "" headers["content-length"] ||= body.length.to_s headers["x-content-sha256"] ||= Digest::SHA256.base64digest(body) end end def inject_authorization_header(headers, method, signature) signed_headers = self.class.headers[method].map(&:to_s).join(" ") headers["authorization"] = [ %(Signature version="1"), %(headers="#{signed_headers}"), %(keyId="#{self.key_id}"), %(algorithm="rsa-sha256"), %(signature="#{signature}") ].join(",") end def compute_signature(headers, method, path) return if self.class.headers[method].empty? signing_string = self.class.headers[method].map do |header| if header == :"(request-target)" "#{header}: #{method.downcase} #{path}" else "#{header}: #{headers[header.to_s]}" end end.join("\n") signature = self.private_key.sign( OpenSSL::Digest::SHA256.new, signing_string.encode("ascii")) Base64.strict_encode64(signature) end end api_key = [ "ocid1.tenancy.oc1..aaaaaaaaba3pv6wkcr4jqae5f15p2b2m2yt2j6rx32uzr4h25vqstifsfdsq", "ocid1.user.oc1..aaaaaaaat5nvwcna5j6aqzjcaty5eqbb6qt2jvpkanghtgdaqedqw3rynjq", "20:3b:97:13:55:1c:5b:0d:d3:37:d8:50:4e:c5:3a:34" ].join("/") private_key = OpenSSL::PKey::RSA.new(File.read("../sample-private-key")) client = Client.new(api_key, private_key) headers = { # Uncomment to use a fixed date # "date" => "Thu, 05 Jan 2014 21:31:40 GMT" } # GET with query parameters uri = "https://iaas.us-ashburn-1.oraclecloud.com/20160918/instances?availabilityDomain=%{availability_domain}&compartmentId=%{compartment_id}&displayName=%{display_name}&volumeId=%{volume_id}" uri = uri % { :availability_domain => "Pjwf%3A%20PHX-AD-1", # Older ocid formats included ":" which must be escaped :compartment_id => "ocid1.compartment.oc1..aaaaaaaam3we6vgnherjq5q2idnccdflvjsnog7mlr6rtdb25gilchfeyjxa".sub(":", "%3A"), :display_name => "TeamXInstances", :volume_id => "ocid1.volume.oc1.phx.abyhqljrgvttnlx73nmrwfaux7kcvzfs3s66izvxf2h4lgvyndsdsnoiwr5q".sub(":", "%3A") } response = client.get(uri, headers: headers) puts uri puts response.request.options[:headers]["authorization"] puts response.response # POST with body uri = "https://iaas.us-ashburn-1.oraclecloud.com/20160918/volumeAttachments" body = %q({ "compartmentId": "ocid1.compartment.oc1..aaaaaaaam3we6vgnherjq5q2idnccdflvjsnog7mlr6rtdb25gilchfeyjxa", "instanceId": "ocid1.instance.oc1.phx.abuw4ljrlsfiqw6vzzxb43vyypt4pkodawglp3wqxjqofakrwvou52gb6s5a", "volumeId": "ocid1.volume.oc1.phx.abyhqljrgvttnlx73nmrwfaux7kcvzfs3s66izvxf2h4lgvyndsdsnoiwr5q" }) response = client.post(uri, headers: headers, body: body) puts "\n" + uri puts response.request.options[:headers]["authorization"] puts response.response