class Aws::Signers::V4
Attributes
credentials[R]
@return [Credentials]
region[R]
@return [String]
service_name[R]
@return [String]
Public Class Methods
new(credentials, service_name, region)
click to toggle source
@param [Credentials] credentials @param [String] #service_name The name used by the service in
signing signature version 4 requests. This is generally the endpoint prefix.
@param [String] region The region (e.g. 'us-west-1') the request
will be made to.
# File lib/aws-sdk-core/signers/v4.rb, line 22 def initialize(credentials, service_name, region) @service_name = service_name @credentials = credentials @region = region end
sign(context)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 8 def self.sign(context) new( context.config.credentials, context.config.sigv4_name, context.config.sigv4_region ).sign(context.http_request) end
Public Instance Methods
canonical_header_value(value)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 179 def canonical_header_value(value) value.match(/^".*"$/) ? value : value.gsub(/\s+/, ' ').strip end
canonical_headers(request)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 169 def canonical_headers(request) headers = [] request.headers.each_pair do |k,v| k = k.downcase headers << [k,v] unless k == 'authorization' end headers = headers.sort_by(&:first) headers.map{|k,v| "#{k}:#{canonical_header_value(v.to_s)}" }.join("\n") end
canonical_request(request, body_digest)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 122 def canonical_request(request, body_digest) [ request.http_method, path(request.endpoint), normalized_querystring(request.endpoint.query || ''), canonical_headers(request) + "\n", signed_headers(request), body_digest ].join("\n") end
credential(datetime)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 91 def credential(datetime) "#{credentials.access_key_id}/#{credential_scope(datetime)}" end
credential_scope(datetime)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 113 def credential_scope(datetime) parts = [] parts << datetime[0,8] parts << region parts << service_name parts << 'aws4_request' parts.join("/") end
hexdigest(value)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 183 def hexdigest(value) digest = OpenSSL::Digest::SHA256.new if value.respond_to?(:read) chunk = nil chunk_size = 1024 * 1024 # 1 megabyte digest.update(chunk) while chunk = value.read(chunk_size) value.rewind else digest.update(value) end digest.hexdigest end
hexhmac(key, value)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 200 def hexhmac(key, value) OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), key, value) end
hmac(key, value)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 196 def hmac(key, value) OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, value) end
normalized_querystring(querystring)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 142 def normalized_querystring(querystring) params = querystring.split('&') params = params.map { |p| p.match(/=/) ? p : p + '=' } # We have to sort by param name and preserve order of params that # have the same name. Default sort <=> in JRuby will swap members # occasionally when <=> is 0 (considered still sorted), but this # causes our normalized query string to not match the sent querystring. # When names match, we then sort by their original order params = params.each.with_index.sort do |a, b| a, a_offset = a a_name = a.split('=')[0] b, b_offset = b b_name = b.split('=')[0] if a_name == b_name a_offset <=> b_offset else a_name <=> b_name end end.map(&:first).join('&') end
path(uri)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 133 def path(uri) path = uri.path == '' ? '/' : uri.path if @service_name == 's3' path else path.gsub(/[^\/]+/) { |segment| Seahorse::Util.uri_escape(segment) } end end
presigned_url(request, options = {})
click to toggle source
Generates an returns a presigned URL. @param [Seahorse::Client::Http::Request] request @option options [required, Integer<Seconds>] :expires_in @option options [optional, String] :body_digest The SHA256 hexdigest of
the payload to sign. For S3, this should be the string literal `UNSIGNED-PAYLOAD`.
@return [Seahorse::Client::Http::Request] the signed request. @api private
# File lib/aws-sdk-core/signers/v4.rb, line 50 def presigned_url(request, options = {}) now = Time.now.utc.strftime("%Y%m%dT%H%M%SZ") body_digest = options[:body_digest] || hexdigest(request.body) params = Query::ParamList.new request.headers['Host'] ||= request.endpoint.host request.headers.each do |header_name, header_value| if header_name.match(/^x-amz/) params.set(header_name, header_value) end unless %w(host content-md5).include?(header_name) request.headers.delete(header_name) end end params.set("X-Amz-Algorithm", "AWS4-HMAC-SHA256") params.set("X-Amz-Date", now) params.set("X-Amz-SignedHeaders", signed_headers(request)) params.set("X-Amz-Expires", options[:expires_in].to_s) params.set('X-Amz-Security-Token', credentials.session_token) if credentials.session_token params.set("X-Amz-Credential", credential(now)) endpoint = request.endpoint if endpoint.query endpoint.query += '&' + params.to_s else endpoint.query = params.to_s end endpoint.to_s + '&X-Amz-Signature=' + signature(request, now, body_digest) end
sign(req)
click to toggle source
@param [Seahorse::Client::Http::Request] req @return [Seahorse::Client::Http::Request] the signed request.
# File lib/aws-sdk-core/signers/v4.rb, line 30 def sign(req) datetime = Time.now.utc.strftime("%Y%m%dT%H%M%SZ") body_digest = req.headers['X-Amz-Content-Sha256'] || hexdigest(req.body) req.headers['X-Amz-Date'] = datetime req.headers['Host'] = req.endpoint.host req.headers['X-Amz-Security-Token'] = credentials.session_token if credentials.session_token req.headers['X-Amz-Content-Sha256'] ||= body_digest req.headers['Authorization'] = authorization(req, datetime, body_digest) req end
signature(request, datetime, body_digest)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 95 def signature(request, datetime, body_digest) k_secret = credentials.secret_access_key k_date = hmac("AWS4" + k_secret, datetime[0,8]) k_region = hmac(k_date, region) k_service = hmac(k_region, service_name) k_credentials = hmac(k_service, 'aws4_request') hexhmac(k_credentials, string_to_sign(request, datetime, body_digest)) end
signed_headers(request)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 163 def signed_headers(request) headers = request.headers.keys headers.delete('authorization') headers.sort.join(';') end
string_to_sign(request, datetime, body_digest)
click to toggle source
# File lib/aws-sdk-core/signers/v4.rb, line 104 def string_to_sign(request, datetime, body_digest) parts = [] parts << 'AWS4-HMAC-SHA256' parts << datetime parts << credential_scope(datetime) parts << hexdigest(canonical_request(request, body_digest)) parts.join("\n") end