mirror of
https://github.com/fog/fog.git
synced 2022-11-09 13:51:43 -05:00
Merge pull request #3303 from fcheung/s3_v2_signature
[AWS|Storage] allow signature version to be set to 2 (WIP)
This commit is contained in:
commit
f4a04fe7b8
1 changed files with 100 additions and 24 deletions
|
@ -44,7 +44,7 @@ module Fog
|
||||||
]
|
]
|
||||||
|
|
||||||
requires :aws_access_key_id, :aws_secret_access_key
|
requires :aws_access_key_id, :aws_secret_access_key
|
||||||
recognizes :endpoint, :region, :host, :port, :scheme, :persistent, :use_iam_profile, :aws_session_token, :aws_credentials_expire_at, :path_style, :instrumentor, :instrumentor_name
|
recognizes :endpoint, :region, :host, :port, :scheme, :persistent, :use_iam_profile, :aws_session_token, :aws_credentials_expire_at, :path_style, :instrumentor, :instrumentor_name, :aws_signature_version
|
||||||
|
|
||||||
secrets :aws_secret_access_key, :hmac
|
secrets :aws_secret_access_key, :hmac
|
||||||
|
|
||||||
|
@ -421,7 +421,8 @@ module Fog
|
||||||
@instrumentor_name = options[:instrumentor_name] || 'fog.aws.storage'
|
@instrumentor_name = options[:instrumentor_name] || 'fog.aws.storage'
|
||||||
@connection_options = options[:connection_options] || {}
|
@connection_options = options[:connection_options] || {}
|
||||||
@persistent = options.fetch(:persistent, false)
|
@persistent = options.fetch(:persistent, false)
|
||||||
|
@signature_version = options.fetch(:aws_signature_version, 4)
|
||||||
|
validate_signature_version!
|
||||||
@path_style = options[:path_style] || false
|
@path_style = options[:path_style] || false
|
||||||
|
|
||||||
if @endpoint = options[:endpoint]
|
if @endpoint = options[:endpoint]
|
||||||
|
@ -445,13 +446,23 @@ module Fog
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def validate_signature_version!
|
||||||
|
unless @signature_version == 2 || @signature_version == 4
|
||||||
|
raise "Unknown signature version #{@signature_version}; valid versions are 2 or 4"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def setup_credentials(options)
|
def setup_credentials(options)
|
||||||
@aws_access_key_id = options[:aws_access_key_id]
|
@aws_access_key_id = options[:aws_access_key_id]
|
||||||
@aws_secret_access_key = options[:aws_secret_access_key]
|
@aws_secret_access_key = options[:aws_secret_access_key]
|
||||||
@aws_session_token = options[:aws_session_token]
|
@aws_session_token = options[:aws_session_token]
|
||||||
@aws_credentials_expire_at = options[:aws_credentials_expire_at]
|
@aws_credentials_expire_at = options[:aws_credentials_expire_at]
|
||||||
|
|
||||||
@signer = Fog::AWS::SignatureV4.new( @aws_access_key_id, @aws_secret_access_key, @region, 's3')
|
if @signature_version == 4
|
||||||
|
@signer = Fog::AWS::SignatureV4.new( @aws_access_key_id, @aws_secret_access_key, @region, 's3')
|
||||||
|
elsif @signature_version == 2
|
||||||
|
@hmac = Fog::HMAC.new('sha1', @aws_secret_access_key)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def connection(scheme, host, port)
|
def connection(scheme, host, port)
|
||||||
|
@ -478,19 +489,11 @@ module Fog
|
||||||
|
|
||||||
params[:headers]['x-amz-security-token'] = @aws_session_token if @aws_session_token
|
params[:headers]['x-amz-security-token'] = @aws_session_token if @aws_session_token
|
||||||
|
|
||||||
if params[:body].respond_to?(:read)
|
if @signature_version == 2
|
||||||
# See http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html
|
expires = date.to_date_header
|
||||||
params[:headers]['x-amz-content-sha256'] = 'STREAMING-AWS4-HMAC-SHA256-PAYLOAD'
|
params[:headers]['Date'] = expires
|
||||||
params[:headers]['x-amz-decoded-content-length'] = params[:headers].delete 'Content-Length'
|
params[:headers]['Authorization'] = "AWS #{@aws_access_key_id}:#{signature_v2(params, expires)}"
|
||||||
|
|
||||||
encoding = "aws-chunked"
|
|
||||||
|
|
||||||
encoding += ", #{params[:headers]['Content-Encoding']}" if params[:headers]['Content-Encoding']
|
|
||||||
params[:headers]['Content-Encoding'] = encoding
|
|
||||||
else
|
|
||||||
params[:headers]['x-amz-content-sha256'] ||= Digest::SHA256.hexdigest(params[:body] || '')
|
|
||||||
end
|
end
|
||||||
params[:headers]['x-amz-date'] = date.to_iso8601_basic
|
|
||||||
|
|
||||||
params = request_params(params)
|
params = request_params(params)
|
||||||
scheme = params.delete(:scheme)
|
scheme = params.delete(:scheme)
|
||||||
|
@ -498,16 +501,32 @@ module Fog
|
||||||
port = params.delete(:port) || DEFAULT_SCHEME_PORT[scheme]
|
port = params.delete(:port) || DEFAULT_SCHEME_PORT[scheme]
|
||||||
params[:headers]['Host'] = host
|
params[:headers]['Host'] = host
|
||||||
|
|
||||||
signature_components = @signer.signature_components(params, date, params[:headers]['x-amz-content-sha256'])
|
|
||||||
params[:headers]['Authorization'] = @signer.components_to_header(signature_components)
|
if @signature_version == 4
|
||||||
|
params[:headers]['x-amz-date'] = date.to_iso8601_basic
|
||||||
|
if params[:body].respond_to?(:read)
|
||||||
|
# See http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html
|
||||||
|
params[:headers]['x-amz-content-sha256'] = 'STREAMING-AWS4-HMAC-SHA256-PAYLOAD'
|
||||||
|
params[:headers]['x-amz-decoded-content-length'] = params[:headers].delete 'Content-Length'
|
||||||
|
|
||||||
|
encoding = "aws-chunked"
|
||||||
|
|
||||||
|
encoding += ", #{params[:headers]['Content-Encoding']}" if params[:headers]['Content-Encoding']
|
||||||
|
params[:headers]['Content-Encoding'] = encoding
|
||||||
|
else
|
||||||
|
params[:headers]['x-amz-content-sha256'] ||= Digest::SHA256.hexdigest(params[:body] || '')
|
||||||
|
end
|
||||||
|
signature_components = @signer.signature_components(params, date, params[:headers]['x-amz-content-sha256'])
|
||||||
|
params[:headers]['Authorization'] = @signer.components_to_header(signature_components)
|
||||||
|
|
||||||
|
if params[:body].respond_to?(:read)
|
||||||
|
body = params.delete :body
|
||||||
|
params[:request_block] = S3Streamer.new(body, signature_components['X-Amz-Signature'], @signer, date)
|
||||||
|
end
|
||||||
|
end
|
||||||
# FIXME: ToHashParser should make this not needed
|
# FIXME: ToHashParser should make this not needed
|
||||||
original_params = params.dup
|
original_params = params.dup
|
||||||
|
|
||||||
if params[:body].respond_to?(:read)
|
|
||||||
body = params.delete :body
|
|
||||||
params[:request_block] = S3Streamer.new(body, signature_components['X-Amz-Signature'], @signer, date)
|
|
||||||
end
|
|
||||||
|
|
||||||
if @instrumentor
|
if @instrumentor
|
||||||
@instrumentor.instrument("#{@instrumentor_name}.request", params) do
|
@instrumentor.instrument("#{@instrumentor_name}.request", params) do
|
||||||
_request(scheme, host, port, params, original_params, &block)
|
_request(scheme, host, port, params, original_params, &block)
|
||||||
|
@ -537,8 +556,10 @@ module Fog
|
||||||
else
|
else
|
||||||
%r{s3-([^\.]*).amazonaws.com}.match(new_params[:host]).captures.first
|
%r{s3-([^\.]*).amazonaws.com}.match(new_params[:host]).captures.first
|
||||||
end
|
end
|
||||||
@signer = Fog::AWS::SignatureV4.new(@aws_access_key_id, @aws_secret_access_key, @region, 's3')
|
if @signature_version == 4
|
||||||
original_params[:headers].delete('Authorization')
|
@signer = Fog::AWS::SignatureV4.new(@aws_access_key_id, @aws_secret_access_key, @region, 's3')
|
||||||
|
original_params[:headers].delete('Authorization')
|
||||||
|
end
|
||||||
response = request(original_params.merge(new_params), &block)
|
response = request(original_params.merge(new_params), &block)
|
||||||
@region, @signer = original_region, original_signer
|
@region, @signer = original_region, original_signer
|
||||||
response
|
response
|
||||||
|
@ -603,6 +624,61 @@ DATA
|
||||||
hmac.sign(string_to_sign.strip).unpack('H*').first
|
hmac.sign(string_to_sign.strip).unpack('H*').first
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def signature_v2(params, expires)
|
||||||
|
headers = params[:headers] || {}
|
||||||
|
|
||||||
|
string_to_sign =
|
||||||
|
<<-DATA
|
||||||
|
#{params[:method].to_s.upcase}
|
||||||
|
#{headers['Content-MD5']}
|
||||||
|
#{headers['Content-Type']}
|
||||||
|
#{expires}
|
||||||
|
DATA
|
||||||
|
|
||||||
|
amz_headers, canonical_amz_headers = {}, ''
|
||||||
|
for key, value in headers
|
||||||
|
if key[0..5] == 'x-amz-'
|
||||||
|
amz_headers[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
amz_headers = amz_headers.sort {|x, y| x[0] <=> y[0]}
|
||||||
|
for key, value in amz_headers
|
||||||
|
canonical_amz_headers << "#{key}:#{value}\n"
|
||||||
|
end
|
||||||
|
string_to_sign << canonical_amz_headers
|
||||||
|
|
||||||
|
query_string = ''
|
||||||
|
if params[:query]
|
||||||
|
query_args = []
|
||||||
|
for key in params[:query].keys.sort
|
||||||
|
if VALID_QUERY_KEYS.include?(key)
|
||||||
|
value = params[:query][key]
|
||||||
|
if value
|
||||||
|
query_args << "#{key}=#{value}"
|
||||||
|
else
|
||||||
|
query_args << key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if query_args.any?
|
||||||
|
query_string = '?' + query_args.join('&')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
canonical_path = (params[:path] || object_to_path(params[:object_name])).to_s
|
||||||
|
canonical_path = '/' + canonical_path if canonical_path[0..0] != '/'
|
||||||
|
|
||||||
|
if params[:bucket_name]
|
||||||
|
canonical_resource = "/#{params[:bucket_name]}#{canonical_path}"
|
||||||
|
else
|
||||||
|
canonical_resource = canonical_path
|
||||||
|
end
|
||||||
|
canonical_resource << query_string
|
||||||
|
string_to_sign << canonical_resource
|
||||||
|
signed_string = @hmac.sign(string_to_sign)
|
||||||
|
Base64.encode64(signed_string).chomp!
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue