Skip to content

Commit

Permalink
Version 1.3.8 of the AWS SDK for Ruby
Browse files Browse the repository at this point in the history
  • Loading branch information
trevorrowe committed Mar 16, 2012
1 parent 998cce8 commit 4defc17
Show file tree
Hide file tree
Showing 11 changed files with 260 additions and 31 deletions.
3 changes: 2 additions & 1 deletion lib/aws/core.rb
Expand Up @@ -59,7 +59,7 @@
module AWS

# Current version of the AWS SDK for Ruby
VERSION = "1.3.7"
VERSION = "1.3.8"

register_autoloads(self) do
autoload :Errors, 'errors'
Expand All @@ -72,6 +72,7 @@ module Core
autoload :AsyncHandle, 'async_handle'
autoload :AuthorizeV2, 'authorize_v2'
autoload :AuthorizeV3, 'authorize_v3'
autoload :AuthorizeV4, 'authorize_v4'
autoload :AuthorizeWithSessionToken, 'authorize_with_session_token'
autoload :Cacheable, 'cacheable'
autoload :Client, 'client'
Expand Down
2 changes: 1 addition & 1 deletion lib/aws/core/authorize_v2.rb
Expand Up @@ -14,7 +14,7 @@
module AWS
module Core

# Mixed into clients that use v2 authorization.
# Mixed into clients that use signature v2 authorization.
# @private
module AuthorizeV2

Expand Down
2 changes: 1 addition & 1 deletion lib/aws/core/authorize_v3.rb
Expand Up @@ -16,7 +16,7 @@
module AWS
module Core

# Mixed into clients that use v3 authorization.
# Mixed into clients that use signature v3 authorization.
# @private
module AuthorizeV3

Expand Down
149 changes: 149 additions & 0 deletions lib/aws/core/authorize_v4.rb
@@ -0,0 +1,149 @@
# Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

# - docs don't match in task 2 between the sample and the detailed
# instructions, is the canonical request hex/hashed or just hexed?
# - the hashing method is never defined, is it a md5 hash?
# - the documentation does not discuss how to add the authorization
# to your get or post request, 2 simple examples are provided, but with
# no information about how to join the parts in the post request
# (not clear about spaces vs newlines, etc)
# - document does not displ

require 'time'
require 'openssl'
require 'digest'

# bug resigning

module AWS
module Core

# Mixed into clients that use signature v4 authorization.
module AuthorizeV4

def add_authorization! signer
self.access_key_id = signer.access_key_id
datetime = Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
headers['content-type'] ||= 'application/x-www-form-urlencoded'
headers['host'] = host
headers['x-amz-date'] = datetime
headers['x-amz-security-token'] = signer.session_token if signer.session_token
headers['authorization'] = authorization(signer, datetime)
end

protected

def authorization signer, datetime
parts = []
parts << "AWS4-HMAC-SHA256 Credential=#{access_key_id}/#{credential_string(datetime)}"
parts << "SignedHeaders=#{signed_headers}"
parts << "Signature=#{hex16(signature(signer, datetime))}"
parts.join(', ')
end

def signature signer, datetime
k_secret = signer.secret_access_key
k_date = hmac("AWS4" + k_secret, datetime[0,8])
k_region = hmac(k_date, region)
k_service = hmac(k_region, service)
k_credentials = hmac(k_service, 'aws4_request')
hmac(k_credentials, string_to_sign(datetime))
end

def string_to_sign datetime
parts = []
parts << 'AWS4-HMAC-SHA256'
parts << datetime
parts << credential_string(datetime)
parts << hex16(hash(canonical_request))
parts.join("\n")
end

def credential_string datetime
parts = []
parts << datetime[0,8]
parts << region
parts << service
parts << 'aws4_request'
parts.join("/")
end

def canonical_request
parts = []
parts << action_name
parts << canonical_uri
parts << canonical_querystring
parts << canonical_headers + "\n"
parts << signed_headers
parts << hex16(hash(payload))
parts.join("\n")
end

def service
# this method is implemented in the request class for each service
raise NotImplementedError
end

def action_name
http_method.to_s.upcase
end

def canonical_uri
path
end

def payload
body || ''
end

def canonical_querystring
http_method.to_s.upcase == 'GET' ? url_encoded_params : ''
end

def signed_headers
to_sign = headers.keys.map{|k| k.to_s.downcase }
to_sign.delete('authorization')
to_sign.sort.join(";")
end

def canonical_headers
headers = []
self.headers.each_pair do |k,v|
header = [k.to_s.downcase, v]
headers << header unless header.first == 'authorization'
end
headers = headers.sort_by(&:first)
headers.map{|k,v| "#{k}:#{canonical_header_values(v)}" }.join("\n")
end

def canonical_header_values values
values = [values] unless values.is_a?(Array)
values.map(&:to_s).map(&:strip).join(',')
end

def hex16 string
string.unpack('H*').first
end

def hmac key, string
OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), key, string)
end

def hash string
Digest::SHA256.digest(string)
end

end
end
end
44 changes: 31 additions & 13 deletions lib/aws/core/client.rb
Expand Up @@ -54,20 +54,28 @@ class Client
# HTTP requests that this client constructs.
#
def initialize options = {}

options = options.dup # so we don't modify the options passed in

@service_ruby_name = self.class.service_ruby_name

if options[:endpoint]
options[:"#{self.class.service_ruby_name}_endpoint"] =
options.delete(:endpoint)
# translate these into service specific configuration options,
# e.g. :endpoint into :s3_endpoint
[:endpoint, :region, :port].each do |opt|
if options[opt]
options[:"#{service_ruby_name}_#{opt}"] = options.delete(opt)
end
end

options_without_config = options.dup
@config = options_without_config.delete(:config)
@config = options.delete(:config)
@config ||= AWS.config
@config = @config.with(options_without_config)
@config = @config.with(options)

@signer = @config.signer
@http_handler = @config.http_handler
@stubs = {}

@endpoint = config.send(:"#{service_ruby_name}_endpoint")
@port = config.send(:"#{service_ruby_name}_port")

end

# @return [Configuration] This clients configuration.
Expand All @@ -77,11 +85,17 @@ def initialize options = {}
# This is normally a DefaultSigner, but it can be configured to
# an other object.
attr_reader :signer

# @return [String] the configured endpoint for this client.
def endpoint
config.send(:"#{self.class.service_ruby_name}_endpoint")
end

# @return [String] The snake-cased ruby name for the service
# (e.g. 's3', 'iam', 'dynamo_db', etc).
attr_reader :service_ruby_name

# @return [Integer] What port this client makes requests via.
attr_reader :port

# @return [String] Returns the service endpoint (hostname) this client
# makes requests against.
attr_reader :endpoint

# Returns a copy of the client with a different HTTP handler.
# You can pass an object like BuiltinHttpHandler or you can
Expand Down Expand Up @@ -125,6 +139,7 @@ def with_config config
# @see new_stub_for
# @private
def stub_for method_name
@stubs ||= {}
@stubs[method_name] ||= new_stub_for(method_name)
end

Expand Down Expand Up @@ -381,7 +396,10 @@ def build_request(name, options, &block)
http_request = new_request

# configure the http request
http_request.service_ruby_name = service_ruby_name
http_request.host = endpoint
http_request.port = port
http_request.region = config.send(:"#{service_ruby_name}_region")
http_request.proxy_uri = config.proxy_uri
http_request.use_ssl = config.use_ssl?
http_request.ssl_verify_peer = config.ssl_verify_peer?
Expand Down
28 changes: 26 additions & 2 deletions lib/aws/core/configuration.rb
Expand Up @@ -302,7 +302,7 @@ def add_option name, default_value = nil, options = {}, &transform
default_value
end

transform ? transform.call(value) : value
transform ? transform.call(self, value) : value

end

Expand Down Expand Up @@ -346,6 +346,7 @@ def add_service name, ruby_name, default_endpoint
:signer,
:http_handler,
:"#{ruby_name}_endpoint",
:"#{ruby_name}_port",
:max_retries,
:stub_requests?,
:proxy_uri,
Expand All @@ -359,6 +360,29 @@ def add_service name, ruby_name, default_endpoint

add_option :"#{ruby_name}_endpoint", default_endpoint

add_option(:"#{ruby_name}_port") do |config,value|
value || (config.use_ssl? ? 443 : 80)
end

# users only need to specify service regions when they use
# a test endpoint with a sigv4 service
add_option(:"#{ruby_name}_region") do |config,value|
value || begin
endpoint = config.send("#{ruby_name}_endpoint")
if endpoint =~ /us-gov/
if matches = enpoint.match(/(us-gov-west-\d+)/)
matches[1]
else
'us-gov-west-1' # e.g. iam.us-gov.amazonaws.com
end
elsif matches = endpoint.match(/^.+\.(.+)\.amazonaws.com$/)
matches[1]
else
'us-east-1'
end
end
end

add_option_with_needs :"#{ruby_name}_client", needs, &create_block

end
Expand All @@ -376,7 +400,7 @@ def add_service name, ruby_name, default_endpoint

add_option :max_retries, 3

add_option :proxy_uri do |uri| uri ? URI.parse(uri.to_s) : nil end
add_option :proxy_uri do |config,uri| uri ? URI.parse(uri.to_s) : nil end

add_option :secret_access_key,
ENV['AWS_SECRET_ACCESS_KEY'] || ENV['AMAZON_SECRET_ACCESS_KEY']
Expand Down
9 changes: 4 additions & 5 deletions lib/aws/core/http/curb_handler.rb
Expand Up @@ -86,11 +86,10 @@ def start_processor

private
def make_easy_handle request, response, thread = nil

url = request.use_ssl? ?
"https://#{request.host}:443#{request.uri}" :
"http://#{request.host}#{request.uri}"


protocol = request.use_ssl? ? 'https' : 'http'
url = "#{protocol}://#{request.host}:#{request.port}#{request.uri}"

curl = Curl::Easy.new(url)
# curl.verbose = true
request.headers.each {|k, v| curl.headers[k] = v}
Expand Down
9 changes: 5 additions & 4 deletions lib/aws/core/http/httparty_handler.rb
Expand Up @@ -68,12 +68,13 @@ def handle(request, response)
end

if request.use_ssl?
url = "https://#{request.host}:443#{request.uri}"
opts[:ssl_ca_file] = request.ssl_ca_file if
request.ssl_verify_peer?
protocol = 'https'
opts[:ssl_ca_file] = request.ssl_ca_file if request.ssl_verify_peer?
else
url = "http://#{request.host}#{request.uri}"
protocol = 'http'
end

url = "#{protocol}://#{request.host}:#{request.port}#{request.uri}"

# get, post, put, delete, head
method = request.http_method.downcase
Expand Down

0 comments on commit 4defc17

Please sign in to comment.