class ApiSignature::Signer
The signer requires secret key.
signer = ApiSignature::Signer.new('access key', 'secret key', uri_escape_path: true)
Constants
- NAME
Public Class Methods
Options: @option options [Array<String>] :unsigned_headers ([]) A list of
headers that should not be signed. This is useful when a proxy modifies headers, such as 'User-Agent', invalidating a signature.
@option options [Boolean] :uri_escape_path (true) When `true`,
the request URI path is uri-escaped as part of computing the canonical request string.
@option options [Boolean] :apply_checksum_header (false) When `true`,
the computed content checksum is returned in the hash of signature headers.
@option options [String] :signature_header (authorization) Header name
for signature
@option options [String] :service (web) Service name
# File lib/api_signature/signer.rb, line 31 def initialize(access_key, secret_key, options = {}) @access_key = access_key @secret_key = secret_key @options = options end
Public Instance Methods
Computes a signature. Returns the resultant signature as a hash of headers to apply to your HTTP request. The given request is not modified.
signature = signer.sign_request( http_method: 'PUT', url: 'https://domain.com', headers: { 'Abc' => 'xyz', }, body: 'body' # String or IO object )
@param [Hash] request
@option request [required, String] :http_method One of
'GET', 'HEAD', 'PUT', 'POST', 'PATCH', or 'DELETE'
@option request [required, String, URI::HTTPS, URI::HTTP] :url
The request URI. Must be a valid HTTP or HTTPS URI.
@option request [optional, Hash] :headers ({}) A hash of headers
to sign. If the 'X-Amz-Content-Sha256' header is set, the `:body` is optional and will not be read.
@option request [optional, String, IO] :body ('') The HTTP request body.
A sha256 checksum is computed of the body unless the 'X-Amz-Content-Sha256' header is set.
@return [Signature] Return an instance of {Signature} that has
a `#headers` method. The headers must be applied to your request.
# File lib/api_signature/signer.rb, line 67 def sign_request(request) builder = Builder.new(request, unsigned_headers) sig_headers = builder.build_sign_headers(apply_checksum_header?) data = build_signature(builder) # apply signature sig_headers[signature_header_name] = data[:header] # Returning the signature components. Signature.new(data.merge!(headers: sig_headers)) end
Private Instance Methods
# File lib/api_signature/signer.rb, line 85 def apply_checksum_header? @options[:apply_checksum_header] == true end
# File lib/api_signature/signer.rb, line 105 def build_signature(builder) path = Utils.url_path(builder.uri.path, uri_escape_path?) # compute signature parts creq = builder.canonical_request(path) sts = string_to_sign(builder.datetime, creq) sig = signature(builder.date, sts) { header: build_signature_header(builder, sig), content_sha256: builder.content_sha256, string_to_sign: sts, canonical_request: creq, signature: sig } end
# File lib/api_signature/signer.rb, line 122 def build_signature_header(builder, signature) [ "#{NAME} Credential=#{credential(builder.date)}", "SignedHeaders=#{builder.signed_headers_names}", "Signature=#{signature}" ].join(', ') end
# File lib/api_signature/signer.rb, line 101 def build_unsigned_headers Set.new(@options.fetch(:unsigned_headers, []).map(&:downcase)) << signature_header_name end
# File lib/api_signature/signer.rb, line 146 def credential(date) "#{@access_key}/#{date}/#{service}/api_request" end
# File lib/api_signature/signer.rb, line 93 def service @options[:service] || ApiSignature.configuration.service end
# File lib/api_signature/signer.rb, line 138 def signature(date, string_to_sign) k_date = Utils.hmac("API#{@secret_key}", date) k_service = Utils.hmac(k_date, service) k_credentials = Utils.hmac(k_service, 'api_request') Utils.hexhmac(k_credentials, string_to_sign) end
# File lib/api_signature/signer.rb, line 89 def signature_header_name @options[:signature_header] || ApiSignature.configuration.signature_header end
# File lib/api_signature/signer.rb, line 130 def string_to_sign(datetime, canonical_request) [ NAME, datetime, Utils.sha256_hexdigest(canonical_request) ].join(Builder::SPLITTER) end
# File lib/api_signature/signer.rb, line 97 def unsigned_headers @unsigned_headers ||= build_unsigned_headers end
# File lib/api_signature/signer.rb, line 81 def uri_escape_path? @options[:uri_escape_path] == true || !@options.key?(:uri_escape_path) end