aws-sdk 1.3.7 → 1.3.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -59,7 +59,7 @@ require 'aws/core/autoloader'
59
59
  module AWS
60
60
 
61
61
  # Current version of the AWS SDK for Ruby
62
- VERSION = "1.3.7"
62
+ VERSION = "1.3.8"
63
63
 
64
64
  register_autoloads(self) do
65
65
  autoload :Errors, 'errors'
@@ -72,6 +72,7 @@ module AWS
72
72
  autoload :AsyncHandle, 'async_handle'
73
73
  autoload :AuthorizeV2, 'authorize_v2'
74
74
  autoload :AuthorizeV3, 'authorize_v3'
75
+ autoload :AuthorizeV4, 'authorize_v4'
75
76
  autoload :AuthorizeWithSessionToken, 'authorize_with_session_token'
76
77
  autoload :Cacheable, 'cacheable'
77
78
  autoload :Client, 'client'
@@ -14,7 +14,7 @@
14
14
  module AWS
15
15
  module Core
16
16
 
17
- # Mixed into clients that use v2 authorization.
17
+ # Mixed into clients that use signature v2 authorization.
18
18
  # @private
19
19
  module AuthorizeV2
20
20
 
@@ -16,7 +16,7 @@ require 'time'
16
16
  module AWS
17
17
  module Core
18
18
 
19
- # Mixed into clients that use v3 authorization.
19
+ # Mixed into clients that use signature v3 authorization.
20
20
  # @private
21
21
  module AuthorizeV3
22
22
 
@@ -0,0 +1,149 @@
1
+ # Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ # - docs don't match in task 2 between the sample and the detailed
15
+ # instructions, is the canonical request hex/hashed or just hexed?
16
+ # - the hashing method is never defined, is it a md5 hash?
17
+ # - the documentation does not discuss how to add the authorization
18
+ # to your get or post request, 2 simple examples are provided, but with
19
+ # no information about how to join the parts in the post request
20
+ # (not clear about spaces vs newlines, etc)
21
+ # - document does not displ
22
+
23
+ require 'time'
24
+ require 'openssl'
25
+ require 'digest'
26
+
27
+ # bug resigning
28
+
29
+ module AWS
30
+ module Core
31
+
32
+ # Mixed into clients that use signature v4 authorization.
33
+ module AuthorizeV4
34
+
35
+ def add_authorization! signer
36
+ self.access_key_id = signer.access_key_id
37
+ datetime = Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
38
+ headers['content-type'] ||= 'application/x-www-form-urlencoded'
39
+ headers['host'] = host
40
+ headers['x-amz-date'] = datetime
41
+ headers['x-amz-security-token'] = signer.session_token if signer.session_token
42
+ headers['authorization'] = authorization(signer, datetime)
43
+ end
44
+
45
+ protected
46
+
47
+ def authorization signer, datetime
48
+ parts = []
49
+ parts << "AWS4-HMAC-SHA256 Credential=#{access_key_id}/#{credential_string(datetime)}"
50
+ parts << "SignedHeaders=#{signed_headers}"
51
+ parts << "Signature=#{hex16(signature(signer, datetime))}"
52
+ parts.join(', ')
53
+ end
54
+
55
+ def signature signer, datetime
56
+ k_secret = signer.secret_access_key
57
+ k_date = hmac("AWS4" + k_secret, datetime[0,8])
58
+ k_region = hmac(k_date, region)
59
+ k_service = hmac(k_region, service)
60
+ k_credentials = hmac(k_service, 'aws4_request')
61
+ hmac(k_credentials, string_to_sign(datetime))
62
+ end
63
+
64
+ def string_to_sign datetime
65
+ parts = []
66
+ parts << 'AWS4-HMAC-SHA256'
67
+ parts << datetime
68
+ parts << credential_string(datetime)
69
+ parts << hex16(hash(canonical_request))
70
+ parts.join("\n")
71
+ end
72
+
73
+ def credential_string datetime
74
+ parts = []
75
+ parts << datetime[0,8]
76
+ parts << region
77
+ parts << service
78
+ parts << 'aws4_request'
79
+ parts.join("/")
80
+ end
81
+
82
+ def canonical_request
83
+ parts = []
84
+ parts << action_name
85
+ parts << canonical_uri
86
+ parts << canonical_querystring
87
+ parts << canonical_headers + "\n"
88
+ parts << signed_headers
89
+ parts << hex16(hash(payload))
90
+ parts.join("\n")
91
+ end
92
+
93
+ def service
94
+ # this method is implemented in the request class for each service
95
+ raise NotImplementedError
96
+ end
97
+
98
+ def action_name
99
+ http_method.to_s.upcase
100
+ end
101
+
102
+ def canonical_uri
103
+ path
104
+ end
105
+
106
+ def payload
107
+ body || ''
108
+ end
109
+
110
+ def canonical_querystring
111
+ http_method.to_s.upcase == 'GET' ? url_encoded_params : ''
112
+ end
113
+
114
+ def signed_headers
115
+ to_sign = headers.keys.map{|k| k.to_s.downcase }
116
+ to_sign.delete('authorization')
117
+ to_sign.sort.join(";")
118
+ end
119
+
120
+ def canonical_headers
121
+ headers = []
122
+ self.headers.each_pair do |k,v|
123
+ header = [k.to_s.downcase, v]
124
+ headers << header unless header.first == 'authorization'
125
+ end
126
+ headers = headers.sort_by(&:first)
127
+ headers.map{|k,v| "#{k}:#{canonical_header_values(v)}" }.join("\n")
128
+ end
129
+
130
+ def canonical_header_values values
131
+ values = [values] unless values.is_a?(Array)
132
+ values.map(&:to_s).map(&:strip).join(',')
133
+ end
134
+
135
+ def hex16 string
136
+ string.unpack('H*').first
137
+ end
138
+
139
+ def hmac key, string
140
+ OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), key, string)
141
+ end
142
+
143
+ def hash string
144
+ Digest::SHA256.digest(string)
145
+ end
146
+
147
+ end
148
+ end
149
+ end
@@ -54,20 +54,28 @@ module AWS
54
54
  # HTTP requests that this client constructs.
55
55
  #
56
56
  def initialize options = {}
57
+
58
+ options = options.dup # so we don't modify the options passed in
59
+
60
+ @service_ruby_name = self.class.service_ruby_name
57
61
 
58
- if options[:endpoint]
59
- options[:"#{self.class.service_ruby_name}_endpoint"] =
60
- options.delete(:endpoint)
62
+ # translate these into service specific configuration options,
63
+ # e.g. :endpoint into :s3_endpoint
64
+ [:endpoint, :region, :port].each do |opt|
65
+ if options[opt]
66
+ options[:"#{service_ruby_name}_#{opt}"] = options.delete(opt)
67
+ end
61
68
  end
62
69
 
63
- options_without_config = options.dup
64
- @config = options_without_config.delete(:config)
70
+ @config = options.delete(:config)
65
71
  @config ||= AWS.config
66
- @config = @config.with(options_without_config)
72
+ @config = @config.with(options)
73
+
67
74
  @signer = @config.signer
68
75
  @http_handler = @config.http_handler
69
- @stubs = {}
70
-
76
+ @endpoint = config.send(:"#{service_ruby_name}_endpoint")
77
+ @port = config.send(:"#{service_ruby_name}_port")
78
+
71
79
  end
72
80
 
73
81
  # @return [Configuration] This clients configuration.
@@ -77,11 +85,17 @@ module AWS
77
85
  # This is normally a DefaultSigner, but it can be configured to
78
86
  # an other object.
79
87
  attr_reader :signer
80
-
81
- # @return [String] the configured endpoint for this client.
82
- def endpoint
83
- config.send(:"#{self.class.service_ruby_name}_endpoint")
84
- end
88
+
89
+ # @return [String] The snake-cased ruby name for the service
90
+ # (e.g. 's3', 'iam', 'dynamo_db', etc).
91
+ attr_reader :service_ruby_name
92
+
93
+ # @return [Integer] What port this client makes requests via.
94
+ attr_reader :port
95
+
96
+ # @return [String] Returns the service endpoint (hostname) this client
97
+ # makes requests against.
98
+ attr_reader :endpoint
85
99
 
86
100
  # Returns a copy of the client with a different HTTP handler.
87
101
  # You can pass an object like BuiltinHttpHandler or you can
@@ -125,6 +139,7 @@ module AWS
125
139
  # @see new_stub_for
126
140
  # @private
127
141
  def stub_for method_name
142
+ @stubs ||= {}
128
143
  @stubs[method_name] ||= new_stub_for(method_name)
129
144
  end
130
145
 
@@ -381,7 +396,10 @@ module AWS
381
396
  http_request = new_request
382
397
 
383
398
  # configure the http request
399
+ http_request.service_ruby_name = service_ruby_name
384
400
  http_request.host = endpoint
401
+ http_request.port = port
402
+ http_request.region = config.send(:"#{service_ruby_name}_region")
385
403
  http_request.proxy_uri = config.proxy_uri
386
404
  http_request.use_ssl = config.use_ssl?
387
405
  http_request.ssl_verify_peer = config.ssl_verify_peer?
@@ -302,7 +302,7 @@ module AWS
302
302
  default_value
303
303
  end
304
304
 
305
- transform ? transform.call(value) : value
305
+ transform ? transform.call(self, value) : value
306
306
 
307
307
  end
308
308
 
@@ -346,6 +346,7 @@ module AWS
346
346
  :signer,
347
347
  :http_handler,
348
348
  :"#{ruby_name}_endpoint",
349
+ :"#{ruby_name}_port",
349
350
  :max_retries,
350
351
  :stub_requests?,
351
352
  :proxy_uri,
@@ -359,6 +360,29 @@ module AWS
359
360
 
360
361
  add_option :"#{ruby_name}_endpoint", default_endpoint
361
362
 
363
+ add_option(:"#{ruby_name}_port") do |config,value|
364
+ value || (config.use_ssl? ? 443 : 80)
365
+ end
366
+
367
+ # users only need to specify service regions when they use
368
+ # a test endpoint with a sigv4 service
369
+ add_option(:"#{ruby_name}_region") do |config,value|
370
+ value || begin
371
+ endpoint = config.send("#{ruby_name}_endpoint")
372
+ if endpoint =~ /us-gov/
373
+ if matches = enpoint.match(/(us-gov-west-\d+)/)
374
+ matches[1]
375
+ else
376
+ 'us-gov-west-1' # e.g. iam.us-gov.amazonaws.com
377
+ end
378
+ elsif matches = endpoint.match(/^.+\.(.+)\.amazonaws.com$/)
379
+ matches[1]
380
+ else
381
+ 'us-east-1'
382
+ end
383
+ end
384
+ end
385
+
362
386
  add_option_with_needs :"#{ruby_name}_client", needs, &create_block
363
387
 
364
388
  end
@@ -376,7 +400,7 @@ module AWS
376
400
 
377
401
  add_option :max_retries, 3
378
402
 
379
- add_option :proxy_uri do |uri| uri ? URI.parse(uri.to_s) : nil end
403
+ add_option :proxy_uri do |config,uri| uri ? URI.parse(uri.to_s) : nil end
380
404
 
381
405
  add_option :secret_access_key,
382
406
  ENV['AWS_SECRET_ACCESS_KEY'] || ENV['AMAZON_SECRET_ACCESS_KEY']
@@ -86,11 +86,10 @@ module AWS
86
86
 
87
87
  private
88
88
  def make_easy_handle request, response, thread = nil
89
-
90
- url = request.use_ssl? ?
91
- "https://#{request.host}:443#{request.uri}" :
92
- "http://#{request.host}#{request.uri}"
93
-
89
+
90
+ protocol = request.use_ssl? ? 'https' : 'http'
91
+ url = "#{protocol}://#{request.host}:#{request.port}#{request.uri}"
92
+
94
93
  curl = Curl::Easy.new(url)
95
94
  # curl.verbose = true
96
95
  request.headers.each {|k, v| curl.headers[k] = v}
@@ -68,12 +68,13 @@ module AWS
68
68
  end
69
69
 
70
70
  if request.use_ssl?
71
- url = "https://#{request.host}:443#{request.uri}"
72
- opts[:ssl_ca_file] = request.ssl_ca_file if
73
- request.ssl_verify_peer?
71
+ protocol = 'https'
72
+ opts[:ssl_ca_file] = request.ssl_ca_file if request.ssl_verify_peer?
74
73
  else
75
- url = "http://#{request.host}#{request.uri}"
74
+ protocol = 'http'
76
75
  end
76
+
77
+ url = "#{protocol}://#{request.host}:#{request.port}#{request.uri}"
77
78
 
78
79
  # get, post, put, delete, head
79
80
  method = request.http_method.downcase
@@ -26,6 +26,7 @@ module AWS
26
26
  @headers = CaseInsensitiveHash.new
27
27
  @params = []
28
28
  @use_ssl = true
29
+ @port = nil
29
30
  @read_timeout = 60
30
31
  end
31
32
 
@@ -33,6 +34,10 @@ module AWS
33
34
  # before a timeout error is raised on the request. Defaults to
34
35
  # 60 seconds.
35
36
  attr_accessor :read_timeout
37
+
38
+ # @return [String] The snake-cased ruby name for the service
39
+ # (e.g. 's3', 'iam', 'dynamo_db', etc).
40
+ attr_accessor :service_ruby_name
36
41
 
37
42
  # @return [String] hostname of the request
38
43
  attr_accessor :host
@@ -57,6 +62,10 @@ module AWS
57
62
  # @return [nil, URI] The URI to the proxy server requests are
58
63
  # sent through if configured. Returns nil if there is no proxy.
59
64
  attr_accessor :proxy_uri
65
+
66
+ # @return [String] The region name this request is for. Only needs
67
+ # to be populated for requests against signature v4 endpoints.
68
+ attr_accessor :region
60
69
 
61
70
  # @param [Boolean] ssl If the request should be sent over ssl or not.
62
71
  def use_ssl= use_ssl
@@ -67,6 +76,19 @@ module AWS
67
76
  def use_ssl?
68
77
  @use_ssl
69
78
  end
79
+
80
+ # Override the default port (443 or 80). If you pass nil then
81
+ # the default port will take precedence.
82
+ # @param [Integer,nil] port_number
83
+ def port= port_number
84
+ @port = port_number
85
+ end
86
+
87
+ # @return [Integer] Returns the port the request will be made over.
88
+ # Defaults to 443 for SSL requests and 80 for non-SSL requests.
89
+ def port
90
+ @port || (use_ssl? ? 443 : 80)
91
+ end
70
92
 
71
93
  # @param [Boolean] verify_peer If the client should verify the
72
94
  # peer certificate or not.
@@ -16,7 +16,13 @@ module AWS
16
16
 
17
17
  # @private
18
18
  class Request < Core::Http::Request
19
- include Core::AuthorizeV2
19
+
20
+ include Core::AuthorizeV4
21
+
22
+ def service
23
+ 'iam'
24
+ end
25
+
20
26
  end
21
27
 
22
28
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws-sdk
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 11
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 3
9
- - 7
10
- version: 1.3.7
9
+ - 8
10
+ version: 1.3.8
11
11
  platform: ruby
12
12
  authors:
13
13
  - Amazon Web Services
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-03-09 00:00:00 Z
18
+ date: 2012-03-16 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  prerelease: false
@@ -93,6 +93,7 @@ files:
93
93
  - lib/aws/core/async_handle.rb
94
94
  - lib/aws/core/authorize_v2.rb
95
95
  - lib/aws/core/authorize_v3.rb
96
+ - lib/aws/core/authorize_v4.rb
96
97
  - lib/aws/core/authorize_with_session_token.rb
97
98
  - lib/aws/core/autoloader.rb
98
99
  - lib/aws/core/cacheable.rb