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

Sign up to get free protection for your applications and to get access to all the features.
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