class ApiSigv2::Signer

The signer requires secret key.

signer = ApiSigv2::Signer.new('access key', 'secret key', uri_escape_path: true)

Constants

NAME

Public Class Methods

new(access_key, secret_key, options = {}) click to toggle source

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_sigv2/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

sign_request(request) click to toggle source

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_sigv2/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

apply_checksum_header?() click to toggle source
# File lib/api_sigv2/signer.rb, line 85
def apply_checksum_header?
  @options[:apply_checksum_header] == true
end
build_signature(builder) click to toggle source
# File lib/api_sigv2/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
build_signature_header(builder, signature) click to toggle source
# File lib/api_sigv2/signer.rb, line 122
def build_signature_header(builder, signature)
  [
    "#{NAME} Credential=#{credential(builder.date)}",
    "SignedHeaders=#{builder.signed_headers_names}",
    "Signature=#{signature}"
  ].join(', ')
end
build_unsigned_headers() click to toggle source
# File lib/api_sigv2/signer.rb, line 101
def build_unsigned_headers
  Set.new(@options.fetch(:unsigned_headers, []).map(&:downcase)) << signature_header_name
end
credential(date) click to toggle source
# File lib/api_sigv2/signer.rb, line 146
def credential(date)
  "#{@access_key}/#{date}/#{service}/api_request"
end
service() click to toggle source
# File lib/api_sigv2/signer.rb, line 93
def service
  @options[:service] || ApiSigv2.configuration.service
end
signature(date, string_to_sign) click to toggle source
# File lib/api_sigv2/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
signature_header_name() click to toggle source
# File lib/api_sigv2/signer.rb, line 89
def signature_header_name
  @options[:signature_header] || ApiSigv2.configuration.signature_header
end
string_to_sign(datetime, canonical_request) click to toggle source
# File lib/api_sigv2/signer.rb, line 130
def string_to_sign(datetime, canonical_request)
  [
    NAME,
    datetime,
    Utils.sha256_hexdigest(canonical_request)
  ].join(Builder::SPLITTER)
end
unsigned_headers() click to toggle source
# File lib/api_sigv2/signer.rb, line 97
def unsigned_headers
  @unsigned_headers ||= build_unsigned_headers
end
uri_escape_path?() click to toggle source
# File lib/api_sigv2/signer.rb, line 81
def uri_escape_path?
  @options[:uri_escape_path] == true || !@options.key?(:uri_escape_path)
end