require'set'require'time'require'openssl'require'webrick/httputils'moduleAwsmoduleSignersclassS3SIGNED_QUERYSTRING_PARAMS=Set.new(%w(
acl delete cors lifecycle location logging notification partNumber
policy requestPayment restore tagging torrent uploadId uploads
versionId versioning versions website replication requestPayment
accelerate
response-content-type response-content-language
response-expires response-cache-control
response-content-disposition response-content-encoding
))defself.sign(context)new(context.config.credentials,context.params,context.config.force_path_style).sign(context.http_request)end# @param [CredentialProvider] credentialsdefinitialize(credentials,params,force_path_style)@credentials=credentials.credentials@params=Query::ParamList.newparams.each_pairdo|param_name,param_value|@params.set(param_name,param_value)end@force_path_style=force_path_styleendattr_reader:credentials,:paramsdefsign(request)iftoken=credentials.session_tokenrequest.headers["X-Amz-Security-Token"]=tokenendrequest.headers['Authorization']=authorization(request)enddefauthorization(request)"AWS #{credentials.access_key_id}:#{signature(request)}"enddefsignature(request)string_to_sign=string_to_sign(request)signature=digest(credentials.secret_access_key,string_to_sign)uri_escape(signature)enddefdigest(secret,string_to_sign)Base64.encode64(hmac(secret,string_to_sign)).stripenddefhmac(key,value)OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'),key,value)end# From the S3 developer guide:## StringToSign =# HTTP-Verb ` "\n" `# content-md5 ` "\n" `# content-type ` "\n" `# date ` "\n" `# CanonicalizedAmzHeaders + CanonicalizedResource;#defstring_to_sign(request)[request.http_method,request.headers.values_at('Content-Md5','Content-Type').join("\n"),signing_string_date(request),canonicalized_headers(request),canonicalized_resource(request.endpoint),].flatten.compact.join("\n")enddefsigning_string_date(request)# if a date is provided via x-amz-date then we should omit the# Date header from the signing string (should appear as a blank line)ifrequest.headers.detect{|k,v|k.to_s=~/^x-amz-date$/i}''elserequest.headers['Date']=Time.now.httpdateendend# CanonicalizedAmzHeaders## See the developer guide for more information on how this element# is generated.#defcanonicalized_headers(request)x_amz=request.headers.select{|k,v|k=~/^x-amz-/i}x_amz=x_amz.collect{|k,v|[k.downcase,v]}x_amz=x_amz.sort_by{|k,v|k}x_amz=x_amz.collect{|k,v|"#{k}:#{v.to_s.strip}"}.join("\n")x_amz==''?nil:x_amzend# From the S3 developer guide## CanonicalizedResource =# [ "/" ` Bucket ] `# <HTTP-Request-URI, protocol name up to the querystring> +# [ sub-resource, if present. e.g. "?acl", "?location",# "?logging", or "?torrent"];## @api privatedefcanonicalized_resource(endpoint)parts=[]# virtual hosted-style requests require the hostname to appear# in the canonicalized resource prefixed by a forward slash.ifbucket=params[:bucket]bucket=bucket.valuessl=endpoint.scheme=='https'ifPlugins::S3BucketDns.dns_compatible?(bucket,ssl)&&!@force_path_styleparts<<"/#{bucket}"endend# append the path name (no querystring)parts<<endpoint.path# lastly any sub resource querystring params need to be appened# in lexigraphical ordered joined by '&' and prefixed by '?'params=signed_querystring_params(endpoint)unlessparams.empty?parts<<'?'parts<<params.sort.collect{|p|p.to_s}.join('&')endparts.joinenddefsigned_querystring_params(endpoint)endpoint.query.to_s.split('&').selectdo|p|SIGNED_QUERYSTRING_PARAMS.include?(p.split('=')[0])end.map{|p|CGI.unescape(p)}enddefuri_escape(s)#URI.escape(s)# URI.escape is deprecated, replacing it with escape from webrick# to squelch the massive number of warnings generated from Ruby.# The following script was used to determine the differences# between the various escape methods available. The webrick# escape only had two differences and it is available in the# standard lib.## (0..255).each {|c|# s = [c].pack("C")# e = [# CGI.escape(s),# ERB::Util.url_encode(s),# URI.encode_www_form_component(s),# WEBrick::HTTPUtils.escape_form(s),# WEBrick::HTTPUtils.escape(s),# URI.escape(s),# ]# next if e.uniq.length == 1# puts("%5s %5s %5s %5s %5s %5s %5s" % ([s.inspect] + e))# }#WEBrick::HTTPUtils.escape(s).gsub('%5B','[').gsub('%5D',']')endendendend