aws-sdk-core 2.0.0.rc8 → 2.0.0.rc9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/apis/DynamoDB-2012-08-10.json +1 -0
  3. data/apis/EC2-2014-05-01.json +15232 -0
  4. data/apis/ElasticBeanstalk-2010-12-01.json +17 -0
  5. data/apis/OpsWorks-2013-02-18.json +215 -0
  6. data/apis/Redshift-2012-12-01.json +72 -0
  7. data/apis/S3-2006-03-01.json +271 -15
  8. data/apis/SNS-2010-03-31.json +27 -0
  9. data/apis/source/ec2-2014-05-01.json +20662 -0
  10. data/apis/source/ec2-2014-05-01.paginators.json +112 -0
  11. data/apis/source/ec2-2014-05-01.waiters.json +146 -0
  12. data/apis/source/elasticbeanstalk-2010-12-01.json +26 -0
  13. data/apis/source/opsworks-2013-02-18.json +383 -8
  14. data/apis/source/redshift-2012-12-01.json +187 -60
  15. data/apis/source/s3-2006-03-01.json +372 -21
  16. data/apis/source/sns-2010-03-31.json +83 -36
  17. data/features/s3/objects.feature +10 -0
  18. data/features/s3/step_definitions.rb +33 -1
  19. data/features/step_definitions.rb +2 -1
  20. data/lib/aws.rb +3 -0
  21. data/lib/aws/api/service_translators/ec2.rb +11 -0
  22. data/lib/aws/api/service_translators/s3.rb +1 -0
  23. data/lib/aws/credential_provider_chain.rb +1 -2
  24. data/lib/aws/error_handler.rb +2 -1
  25. data/lib/aws/plugins/ec2_copy_encrypted_snapshot.rb +86 -0
  26. data/lib/aws/plugins/s3_md5s.rb +11 -8
  27. data/lib/aws/plugins/s3_sse_cpk.rb +42 -0
  28. data/lib/aws/query/builder.rb +4 -0
  29. data/lib/aws/query/param.rb +1 -1
  30. data/lib/aws/signers/base.rb +2 -0
  31. data/lib/aws/signers/s3.rb +0 -1
  32. data/lib/aws/signers/v4.rb +73 -22
  33. data/lib/aws/version.rb +1 -1
  34. data/spec/aws/operations_spec.rb +19 -15
  35. data/spec/aws/plugins/s3_md5s_spec.rb +84 -0
  36. data/spec/aws/query/builder_spec.rb +40 -0
  37. data/spec/aws/query/param_spec.rb +5 -0
  38. data/spec/aws/s3_spec.rb +27 -0
  39. data/spec/aws/signers/v4_spec.rb +1 -1
  40. data/spec/fixtures/operations/s3/412_response_head.yml +10 -0
  41. data/spec/spec_helper.rb +7 -0
  42. data/vendor/seahorse/lib/seahorse/client/handler_list.rb +3 -2
  43. data/vendor/seahorse/lib/seahorse/client/http/headers.rb +4 -0
  44. data/vendor/seahorse/lib/seahorse/client/net_http/handler.rb +1 -0
  45. data/vendor/seahorse/lib/seahorse/client/plugins/operation_methods.rb +4 -2
  46. data/vendor/seahorse/spec/seahorse/client/handler_list_spec.rb +2 -13
  47. metadata +15 -2
@@ -0,0 +1,11 @@
1
+ module Aws::Api::ServiceTranslators::EC2
2
+ class << self
3
+
4
+ def translate(api)
5
+ if api.version >= '2014-05-01'
6
+ api.plugins << "Aws::Plugins::EC2CopyEncryptedSnapshot"
7
+ end
8
+ end
9
+
10
+ end
11
+ end
@@ -7,6 +7,7 @@ module Aws::Api::ServiceTranslators::S3
7
7
  api.plugins << "Aws::Plugins::S3Md5s"
8
8
  api.plugins << "Aws::Plugins::S3Redirects"
9
9
  api.plugins << "Aws::Plugins::S3LocationConstraint"
10
+ api.plugins << "Aws::Plugins::S3SseCpk"
10
11
  end
11
12
  end
12
13
  end
@@ -1,9 +1,8 @@
1
1
  module Aws
2
2
  class CredentialProviderChain
3
3
 
4
- def initialize(config, foo=nil)
4
+ def initialize(config)
5
5
  @config = config
6
- @foo = foo
7
6
  end
8
7
 
9
8
  def resolve
@@ -41,7 +41,8 @@ module Aws
41
41
  400 => 'BadRequest',
42
42
  403 => 'Forbidden',
43
43
  404 => 'NotFound',
44
- }[status_code] || "#{status_code}Error"
44
+ 412 => 'PreconditionFailed',
45
+ }[status_code] || "Http#{status_code}Error"
45
46
  end
46
47
 
47
48
  end
@@ -0,0 +1,86 @@
1
+ module Aws
2
+ module Plugins
3
+
4
+ # This plugin auto populates the following request params for the
5
+ # CopySnapshot API:
6
+ #
7
+ # * `:destination_region`
8
+ # * `:presigned_url`
9
+ #
10
+ # These params are required by EC2 when copying an encrypted snapshot.
11
+ class EC2CopyEncryptedSnapshot < Seahorse::Client::Plugin
12
+
13
+ # @api private
14
+ class Handler < Seahorse::Client::Handler
15
+
16
+ def call(context)
17
+ params = context.params
18
+ unless params.key?(:destination_region)
19
+ params[:destination_region] = context.config.region
20
+ params[:presigned_url] = presigned_url(context.client, params)
21
+ end
22
+ @handler.call(context)
23
+ end
24
+
25
+ private
26
+
27
+ def presigned_url(client, params)
28
+ client = source_region_client(client, params)
29
+ client.handle(PresignHandler, step: :build, priority: 0)
30
+ client.copy_snapshot(params).data # presigned url
31
+ end
32
+
33
+ def source_region_client(client, params)
34
+ config = client.config.to_h
35
+ config.delete(:endpoint)
36
+ config[:region] = params[:source_region]
37
+ client.class.new(config)
38
+ end
39
+
40
+ end
41
+
42
+ # This handler intentionally does NOT call the next handler in
43
+ # the stack. It generates a presigned url from the request
44
+ # and returns it as the response data.
45
+ #
46
+ # Before signing:
47
+ #
48
+ # * The HTTP method is changed from POST to GET
49
+ # * The url-encoded body is moved to the querystring
50
+ #
51
+ # @api private
52
+ class PresignHandler < Seahorse::Client::Handler
53
+
54
+ def call(context)
55
+ convert_post_2_get(context)
56
+ Seahorse::Client::Response.new(
57
+ context: context,
58
+ data: presigned_url(context.http_request, context.config))
59
+ end
60
+
61
+ private
62
+
63
+ def convert_post_2_get(context)
64
+ context.http_request.http_method = 'GET'
65
+ context.http_request.endpoint = new_endpoint(context)
66
+ context.http_request.body = ''
67
+ end
68
+
69
+ def new_endpoint(context)
70
+ body = context.http_request.body_contents
71
+ endpoint = context.http_request.endpoint.to_s + '?' + body
72
+ Seahorse::Client::Http::Endpoint.new(endpoint)
73
+ end
74
+
75
+ def presigned_url(http_request, config)
76
+ signer = Signers::V4.new('ec2', config.credentials, config.region)
77
+ signer.presigned_url(http_request, expires_in: 3600)
78
+ end
79
+
80
+ end
81
+
82
+ handler(Handler, step: :initialize, operations: [:copy_snapshot])
83
+
84
+ end
85
+ end
86
+ end
@@ -1,13 +1,14 @@
1
- require 'digest/md5'
1
+ require 'openssl'
2
2
  require 'base64'
3
3
 
4
4
  module Aws
5
5
  module Plugins
6
6
 
7
7
  # @seahorse.client.option [Boolean] :compute_checksums (true)
8
- # When `true` a MD5 checksum will be computed for all requests that
9
- # accept the optional `Content-MD5` header. Checksum errors returned
10
- # by Amazon S3 are automatically retried up to `:retry_limit` times.
8
+ # When `true` a MD5 checksum will be computed for every request that
9
+ # sends a body. When `false`, MD5 checksums will only be computed
10
+ # for operations that require them. Checksum errors returned by Amazon
11
+ # S3 are automatically retried up to `:retry_limit` times.
11
12
  class S3Md5s < Seahorse::Client::Plugin
12
13
 
13
14
  # Amazon S3 requires these operations to have an MD5 checksum
@@ -25,13 +26,15 @@ module Aws
25
26
  OneMB = 1024 * 1024
26
27
 
27
28
  def call(context)
28
- context.http_request.headers['Content-Md5'] = md5(context)
29
+ body = context.http_request.body
30
+ if body.size > 0
31
+ context.http_request.headers['Content-Md5'] ||= md5(body)
32
+ end
29
33
  @handler.call(context)
30
34
  end
31
35
 
32
- def md5(context)
33
- md5 = Digest::MD5.new
34
- body = context.http_request.body
36
+ def md5(body)
37
+ md5 = OpenSSL::Digest::MD5.new
35
38
  while chunk = body.read(OneMB)
36
39
  md5.update(chunk)
37
40
  end
@@ -0,0 +1,42 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Aws
5
+ module Plugins
6
+ class S3SseCpk < Seahorse::Client::Plugin
7
+
8
+ class Handler < Seahorse::Client::Handler
9
+
10
+ def call(context)
11
+ compute_key_md5(context.params)
12
+ @handler.call(context)
13
+ end
14
+
15
+ private
16
+
17
+ def compute_key_md5(params)
18
+ if key = params[:sse_customer_key]
19
+ params[:sse_customer_key] = base64(key)
20
+ params[:sse_customer_key_md5] = base64(md5(key))
21
+ end
22
+ if key = params[:copy_source_sse_customer_key]
23
+ params[:copy_source_sse_customer_key] = base64(key)
24
+ params[:copy_source_sse_customer_key_md5] = base64(md5(key))
25
+ end
26
+ end
27
+
28
+ def md5(str)
29
+ OpenSSL::Digest::MD5.digest(str)
30
+ end
31
+
32
+ def base64(str)
33
+ Base64.encode64(str).strip
34
+ end
35
+
36
+ end
37
+
38
+ handler(Handler, step: :initialize)
39
+
40
+ end
41
+ end
42
+ end
@@ -38,6 +38,10 @@ module Aws
38
38
  end
39
39
 
40
40
  def list(param_list, shape, prefix, values)
41
+ if values.empty?
42
+ param_list.set(prefix, '')
43
+ return
44
+ end
41
45
  member_shape = shape.members
42
46
  if flat?(shape)
43
47
  if member_shape.serialized_name
@@ -17,7 +17,7 @@ module Aws
17
17
 
18
18
  # @return [String]
19
19
  def to_s
20
- value ? "#{escape(name)}=#{escape(value)}" : escape(name)
20
+ value ? "#{escape(name)}=#{escape(value)}" : "#{escape(name)}="
21
21
  end
22
22
 
23
23
  # @api private
@@ -1,3 +1,5 @@
1
+ require 'openssl'
2
+
1
3
  module Aws
2
4
  module Signers
3
5
  class Base
@@ -1,6 +1,5 @@
1
1
  require 'set'
2
2
  require 'time'
3
- require 'digest/sha1'
4
3
  require 'openssl'
5
4
  require 'uri'
6
5
 
@@ -1,5 +1,4 @@
1
1
  require 'time'
2
- require 'digest/sha1'
3
2
  require 'openssl'
4
3
 
5
4
  module Aws
@@ -8,8 +7,8 @@ module Aws
8
7
 
9
8
  def self.sign(context)
10
9
  new(
11
- context.config.credentials,
12
10
  context.config.sigv4_name,
11
+ context.config.credentials,
13
12
  context.config.sigv4_region
14
13
  ).sign(context.http_request)
15
14
  end
@@ -20,48 +19,94 @@ module Aws
20
19
  # the endpoint prefix.
21
20
  # @param [String] region The region (e.g. 'us-west-1') the request
22
21
  # will be made to.
23
- def initialize(credentials, service_name, region)
24
- @credentials = credentials
22
+ def initialize(service_name, credentials, region)
25
23
  @service_name = service_name
24
+ @credentials = credentials
26
25
  @region = region
27
26
  end
28
27
 
29
28
  # @param [Seahorse::Client::Http::Request] request
30
29
  # @return [Seahorse::Client::Http::Request] the signed request.
31
- def sign(request)
30
+ def sign(req)
32
31
  datetime = Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
33
- request.headers['X-Amz-Date'] = datetime
34
- request.headers['Host'] = request.endpoint.host
35
- request.headers['X-Amz-Security-Token'] = credentials.session_token if
32
+ body_digest = req.headers['X-Amz-Content-Sha256'] || hexdigest(req.body)
33
+ req.headers['X-Amz-Date'] = datetime
34
+ req.headers['Host'] = req.endpoint.host
35
+ req.headers['X-Amz-Security-Token'] = credentials.session_token if
36
36
  credentials.session_token
37
- request.headers['X-Amz-Content-Sha256'] ||= hexdigest(request.body)
38
- request.headers['Authorization'] = authorization(request, datetime)
39
- request
37
+ req.headers['X-Amz-Content-Sha256'] ||= body_digest
38
+ req.headers['Authorization'] = authorization(req, datetime, body_digest)
39
+ req
40
40
  end
41
41
 
42
- def authorization(request, datetime)
42
+ # Generates an returns a presigned URL.
43
+ # @param [Seahorse::Client::Http::Request] request
44
+ # @option options [required, Integer<Seconds>] :expires_in
45
+ # @option options [optional, String] :body_digest The SHA256 hexdigest of
46
+ # the payload to sign. For S3, this should be the string literal
47
+ # `UNSIGNED-PALOAD`.
48
+ # @return [Seahorse::Client::Http::Request] the signed request.
49
+ # @api private
50
+ def presigned_url(request, options = {})
51
+ now = Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
52
+ body_digest = options[:body_digest] || hexdigest(request.body)
53
+
54
+ params = Query::ParamList.new
55
+
56
+ request.headers['Host'] ||= request.endpoint.host
57
+ request.headers.each do |header_name, header_value|
58
+ if header_name.match(/^x-amz/)
59
+ params.set(header_name, header_value)
60
+ end
61
+ unless %w(host content-md5).include?(header_name)
62
+ request.headers.delete(header_name)
63
+ end
64
+ end
65
+
66
+ params.set("X-Amz-Algorithm", "AWS4-HMAC-SHA256")
67
+ params.set("X-Amz-Date", now)
68
+ params.set("X-Amz-SignedHeaders", signed_headers(request))
69
+ params.set("X-Amz-Expires", options[:expires_in].to_s)
70
+ params.set('X-Amz-Security-Token', credentials.session_token) if
71
+ credentials.session_token
72
+ params.set("X-Amz-Credential", credential(now))
73
+
74
+ endpoint = request.endpoint
75
+ if endpoint.querystring
76
+ endpoint.request_uri += '&' + params.to_s
77
+ else
78
+ endpoint.request_uri += '?' + params.to_s
79
+ end
80
+ endpoint.to_s + '&X-Amz-Signature=' + signature(request, now, body_digest)
81
+ end
82
+
83
+ def authorization(request, datetime, body_digest)
43
84
  parts = []
44
- parts << "AWS4-HMAC-SHA256 Credential=#{credentials.access_key_id}/#{credential_scope(datetime)}"
85
+ parts << "AWS4-HMAC-SHA256 Credential=#{credential(datetime)}"
45
86
  parts << "SignedHeaders=#{signed_headers(request)}"
46
- parts << "Signature=#{signature(request, datetime)}"
87
+ parts << "Signature=#{signature(request, datetime, body_digest)}"
47
88
  parts.join(', ')
48
89
  end
49
90
 
50
- def signature(request, datetime)
91
+ def credential(datetime)
92
+ "#{credentials.access_key_id}/#{credential_scope(datetime)}"
93
+ end
94
+
95
+ def signature(request, datetime, body_digest)
51
96
  k_secret = credentials.secret_access_key
52
97
  k_date = hmac("AWS4" + k_secret, datetime[0,8])
53
98
  k_region = hmac(k_date, region)
54
99
  k_service = hmac(k_region, service_name)
55
100
  k_credentials = hmac(k_service, 'aws4_request')
56
- hexhmac(k_credentials, string_to_sign(request, datetime))
101
+ hexhmac(k_credentials, string_to_sign(request, datetime, body_digest))
57
102
  end
58
103
 
59
- def string_to_sign(request, datetime)
104
+ def string_to_sign(request, datetime, body_digest)
60
105
  parts = []
61
106
  parts << 'AWS4-HMAC-SHA256'
62
107
  parts << datetime
63
108
  parts << credential_scope(datetime)
64
- parts << hexdigest(canonical_request(request))
109
+ parts << hexdigest(canonical_request(request, body_digest))
65
110
  parts.join("\n")
66
111
  end
67
112
 
@@ -74,17 +119,23 @@ module Aws
74
119
  parts.join("/")
75
120
  end
76
121
 
77
- def canonical_request(request)
122
+ def canonical_request(request, body_digest)
78
123
  [
79
124
  request.http_method,
80
125
  request.endpoint.path,
81
- request.endpoint.querystring,
126
+ normalized_querystring(request.endpoint.querystring),
82
127
  canonical_headers(request) + "\n",
83
128
  signed_headers(request),
84
- request.headers['X-Amz-Content-Sha256']
129
+ body_digest
85
130
  ].join("\n")
86
131
  end
87
132
 
133
+ def normalized_querystring(querystring)
134
+ if querystring
135
+ querystring.split('&').sort.join('&')
136
+ end
137
+ end
138
+
88
139
  def signed_headers(request)
89
140
  headers = request.headers.keys
90
141
  headers.delete('authorization')
@@ -106,7 +157,7 @@ module Aws
106
157
  end
107
158
 
108
159
  def hexdigest(value)
109
- digest = Digest::SHA256.new
160
+ digest = OpenSSL::Digest::SHA256.new
110
161
  if value.respond_to?(:read)
111
162
  chunk = nil
112
163
  chunk_size = 1024 * 1024 # 1 megabyte
@@ -1,3 +1,3 @@
1
1
  module Aws
2
- VERSION = '2.0.0.rc8'
2
+ VERSION = '2.0.0.rc9'
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'stringio'
2
3
 
3
4
  module Aws
4
5
  describe 'Aws' do
@@ -116,27 +117,30 @@ module Aws
116
117
  fixture_name = path.split('/')[-1][0..-5]
117
118
 
118
119
  it(fixture_name) do
119
- # load the fixture from disk
120
- f = OperationFixture.load(svc_name, fixture_name)
120
+ begin
121
+ # load the fixture from disk
122
+ f = OperationFixture.load(svc_name, fixture_name)
121
123
 
122
- # remove the plugin that raises errors
123
- Aws.service_classes[svc_name.to_sym].remove_plugin(
124
- Seahorse::Client::Plugins::RaiseResponseErrors)
124
+ # remove the plugin that raises errors
125
+ Aws.service_classes[svc_name.to_sym].remove_plugin(
126
+ Seahorse::Client::Plugins::RaiseResponseErrors)
125
127
 
126
- # build the service interface
127
- svc = Aws.send(svc_name, f.config)
128
+ # build the service interface
129
+ svc = Aws.send(svc_name, f.config)
128
130
 
129
- # build the request
130
- req = svc.build_request(f.operation, f.params)
131
- req.handler(f.handler, step: :send)
131
+ # build the request
132
+ req = svc.build_request(f.operation, f.params)
133
+ req.handler(f.handler, step: :send)
132
134
 
133
135
 
134
- # send the request
135
- resp = req.send_request
136
-
137
- request_assertions(f, resp.context.http_request)
138
- response_assertions(f, resp)
136
+ # send the request
137
+ resp = req.send_request
139
138
 
139
+ request_assertions(f, resp.context.http_request)
140
+ response_assertions(f, resp)
141
+ ensure
142
+ Aws.service_classes[svc_name.to_sym].add_plugin(Seahorse::Client::Plugins::RaiseResponseErrors)
143
+ end
140
144
  end
141
145
  end
142
146
  end