class Rawscsi::RequestSignature

Attributes

access_key_id[RW]
debug_mode[RW]
endpoint[RW]
headers[RW]
host[RW]
method[RW]
payload[RW]
query[RW]
region_name[RW]
secret_key[RW]
service_name[RW]

Public Class Methods

new(options) click to toggle source
# File lib/rawscsi/request_signature.rb, line 3
def initialize(options)
  required_attributes_missing = []
  require_attribute = lambda do |name|
    return options[name] if options.has_key?(name) 
    required_attributes_missing << name
    nil 
  end
  
  self.secret_key = require_attribute[:secret_key]
  self.access_key_id = require_attribute[:access_key_id]
  self.region_name = require_attribute[:region_name]
  self.endpoint = require_attribute[:endpoint]
  self.method = require_attribute[:method]
  self.host = require_attribute[:host]

  unless required_attributes_missing.size == 0
    raise "#{required_attributes_missing.join(',')} attributes required for a request signature"
  end

  self.debug_mode = options[:debug] || false  
  self.payload = options[:payload] || ''
  self.service_name = options[:service_name] || 'cloudsearch'
  self.headers = extract_headers(options)
  self.query = options[:query] || ''
end

Public Instance Methods

build() click to toggle source
# File lib/rawscsi/request_signature.rb, line 29
def build
  result_headers = default_headers.dup
  result_headers['Authorization'] = "#{algo} Credential=#{access_key_id}/#{credential}, SignedHeaders=#{canonical_headers_names_string}, Signature=#{signature}"

  result = { 
    headers: result_headers, 
  }

  if debug_mode
    result[:debug] = debug_data
  end

  result
end

Private Instance Methods

algo() click to toggle source
# File lib/rawscsi/request_signature.rb, line 79
def algo
  'AWS4-HMAC-SHA256'
end
amz_datetime() click to toggle source
# File lib/rawscsi/request_signature.rb, line 103
def amz_datetime
  datetime.strftime('%Y%m%dT%H%M%SZ')
end
canonical_headers() click to toggle source
# File lib/rawscsi/request_signature.rb, line 133
def canonical_headers
  @canonical_headers ||= headers.to_a.group_by(&:first).map do |name, values|
    canonical_values = values.map(&:last).map do |value|
      value.to_s.first == '"' ? value : value.squeeze(' ')
    end
    [ name.to_s.downcase, canonical_values.join(',') ]
  end.sort { |a, b| a.first <=> b.first }
end
canonical_headers_names_string() click to toggle source
# File lib/rawscsi/request_signature.rb, line 129
def canonical_headers_names_string
  canonical_headers.map(&:first).join(';')
end
canonical_headers_string() click to toggle source
# File lib/rawscsi/request_signature.rb, line 142
def canonical_headers_string
  canonical_headers.map { |header| header.join(':') }.join("\n") + "\n"
end
canonical_query_string() click to toggle source
# File lib/rawscsi/request_signature.rb, line 124
def canonical_query_string
  # @NOTE gsubs are here because AWS expects us to escape everything but #encode_www_form encodes space as "+"
  @canonical_query_string = URI.encode_www_form(CGI::parse(query).to_a.sort { |a, b| a.first <=> b.first }).gsub('+', '%20').gsub('*', '%2A')
end
canonical_request() click to toggle source
# File lib/rawscsi/request_signature.rb, line 150
def canonical_request
  @canonical_request ||= [ 
    method,
    endpoint,
    canonical_query_string,
    canonical_headers_string,
    canonical_headers_names_string,
    payload_digest,
  ].join("\n")
end
canonical_request_digest() click to toggle source
# File lib/rawscsi/request_signature.rb, line 161
def canonical_request_digest
  @canonical_request_digest ||= OpenSSL::Digest::SHA256.hexdigest(canonical_request)
end
credential() click to toggle source
# File lib/rawscsi/request_signature.rb, line 111
def credential
  "#{date}/#{region_name}/cloudsearch/aws4_request"
end
date() click to toggle source
# File lib/rawscsi/request_signature.rb, line 107
def date
  datetime.strftime('%Y%m%d')
end
datetime() click to toggle source
# File lib/rawscsi/request_signature.rb, line 99
def datetime
  @datetime ||= Time.now.utc
end
debug_data() click to toggle source
# File lib/rawscsi/request_signature.rb, line 51
def debug_data
  {
    canonical_request: canonical_request,
    payload_digest: payload_digest,
    canonical_request_digest: canonical_request_digest,
    string_to_sign: string_to_sign,
    signature: signature,
    signed_headers: canonical_headers_names_string,
    payload: payload,
  }
end
default_headers() click to toggle source
# File lib/rawscsi/request_signature.rb, line 83
def default_headers
  {
    'X-Amz-Date' => amz_datetime,
    'Host' => host,
  }
end
extract_headers(options) click to toggle source
# File lib/rawscsi/request_signature.rb, line 46
def extract_headers(options)
  return default_headers unless opt_headers = options[:headers] 
  opt_headers.to_h.merge(default_headers)
end
payload_digest() click to toggle source
# File lib/rawscsi/request_signature.rb, line 146
def payload_digest
  @payload_digest ||= OpenSSL::Digest::SHA256.hexdigest(payload) 
end
signature() click to toggle source
# File lib/rawscsi/request_signature.rb, line 75
def signature 
  OpenSSL::HMAC.hexdigest('sha256', signature_key, string_to_sign)
end
signature_key() click to toggle source
# File lib/rawscsi/request_signature.rb, line 90
def signature_key
  k_date    = OpenSSL::HMAC.digest('sha256', "AWS4" + secret_key, date)
  k_region  = OpenSSL::HMAC.digest('sha256', k_date, region_name)
  k_service = OpenSSL::HMAC.digest('sha256', k_region, service_name)
  k_signing = OpenSSL::HMAC.digest('sha256', k_service, "aws4_request")

  k_signing
end
string_to_sign() click to toggle source
# File lib/rawscsi/request_signature.rb, line 115
def string_to_sign
  [
    algo,
    amz_datetime,
    credential,
    canonical_request_digest,
  ].join("\n")
end