right_aws_api 0.2.0 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/HISTORY +9 -0
- data/lib/cloud/aws/base/helpers/utils.rb +216 -13
- data/lib/cloud/aws/base/manager.rb +1 -1
- data/lib/cloud/aws/base/routines/request_signer.rb +13 -29
- data/lib/cloud/aws/s3/link/routines/request_signer.rb +12 -44
- data/lib/cloud/aws/s3/manager.rb +13 -0
- data/lib/cloud/aws/s3/routines/request_signer.rb +38 -107
- data/lib/cloud/aws/s3/wrappers/default.rb +37 -35
- data/lib/cloud/aws/sqs/manager.rb +2 -2
- data/lib/cloud/aws/sqs/routines/request_signer.rb +62 -0
- data/lib/right_aws_api_version.rb +1 -1
- data/right_aws_api.gemspec +2 -1
- data/spec/cloud/aws/base/helpers/utils_spec.rb +298 -0
- data/spec/cloud/aws/s3/routines/request_signer_spec.rb +3 -119
- metadata +23 -8
- data/spec/cloud/aws/s3/link/routines/request_signer_spec.rb +0 -53
@@ -61,11 +61,11 @@ module RightScale
|
|
61
61
|
|
62
62
|
|
63
63
|
base.query_api_pattern 'DeleteBucketPolicy', :delete, '{:Bucket}',
|
64
|
-
:params => { 'policy' =>
|
64
|
+
:params => { 'policy' => '' }
|
65
65
|
|
66
66
|
|
67
67
|
base.query_api_pattern 'DeleteBucketWebsite', :delete, '{:Bucket}',
|
68
|
-
:params => { 'website' =>
|
68
|
+
:params => { 'website' => '' }
|
69
69
|
|
70
70
|
|
71
71
|
base.query_api_pattern 'ListObjects', :get, '{:Bucket}'
|
@@ -73,130 +73,131 @@ module RightScale
|
|
73
73
|
|
74
74
|
|
75
75
|
base.query_api_pattern 'GetBucketAcl', :get, '{:Bucket}',
|
76
|
-
:params => { 'acl'=>
|
76
|
+
:params => { 'acl'=> '' }
|
77
77
|
|
78
78
|
|
79
79
|
base.query_api_pattern 'GetBucketPolicy', :get, '{:Bucket}',
|
80
|
-
:params => { 'policy'=>
|
80
|
+
:params => { 'policy'=> '' }
|
81
81
|
|
82
82
|
|
83
83
|
base.query_api_pattern 'GetBucketLocation', :get, '{:Bucket}',
|
84
|
-
:params => { 'location'=>
|
84
|
+
:params => { 'location'=> '' }
|
85
85
|
|
86
86
|
|
87
87
|
base.query_api_pattern 'GetBucketLogging', :get, '{:Bucket}',
|
88
|
-
:params => { 'logging'=>
|
88
|
+
:params => { 'logging'=> '' }
|
89
89
|
|
90
90
|
|
91
91
|
base.query_api_pattern 'GetBucketNotification', :get, '{:Bucket}',
|
92
|
-
:params => { 'notification'=>
|
92
|
+
:params => { 'notification'=> '' }
|
93
93
|
|
94
94
|
|
95
95
|
base.query_api_pattern 'GetBucketVersions', :get, '{:Bucket}',
|
96
|
-
:params => { 'versions'=>
|
96
|
+
:params => { 'versions'=> '' }
|
97
97
|
|
98
98
|
|
99
99
|
base.query_api_pattern 'GetBucketRequestPayment', :get, '{:Bucket}',
|
100
|
-
:params => { 'requestPayment'=>
|
100
|
+
:params => { 'requestPayment'=> '' }
|
101
101
|
|
102
102
|
|
103
103
|
base.query_api_pattern 'GetBucketVersioning', :get, '{:Bucket}',
|
104
|
-
:params => { 'versioning'=>
|
104
|
+
:params => { 'versioning'=> '' }
|
105
105
|
|
106
106
|
|
107
107
|
base.query_api_pattern 'GetBucketWebsite', :get, '{:Bucket}',
|
108
|
-
:params => { 'website'=>
|
108
|
+
:params => { 'website'=> '' }
|
109
109
|
|
110
110
|
|
111
111
|
base.query_api_pattern 'ListMultipartUploads', :get, '{:Bucket}',
|
112
|
-
:params => { 'uploads'=>
|
112
|
+
:params => { 'uploads'=> '' }
|
113
113
|
|
114
114
|
|
115
|
-
base.query_api_pattern 'PutBucket', :put, '{:Bucket}'
|
115
|
+
base.query_api_pattern 'PutBucket', :put, '{:Bucket}',
|
116
|
+
:headers => { 'content-type' => 'application/xml' }
|
116
117
|
|
117
118
|
|
118
119
|
base.query_api_pattern 'PutBucketAcl', :put, '{:Bucket}',
|
119
|
-
:params => { 'acl' =>
|
120
|
+
:params => { 'acl' => '' },
|
120
121
|
:body => { 'AccessControlPolicy' => :AccessControlPolicy },
|
121
122
|
:headers => { 'content-type' => 'application/xml'}
|
122
123
|
|
123
124
|
|
124
125
|
base.query_api_pattern 'PutBucketPolicy', :put, '{:Bucket}',
|
125
|
-
:params => { 'policy' =>
|
126
|
+
:params => { 'policy' => '' },
|
126
127
|
:body => Utils::MUST_BE_SET,
|
127
128
|
:headers => { 'content-type' => 'application/json' }
|
128
129
|
|
129
130
|
|
130
131
|
base.query_api_pattern 'PutBucketLogging', :put, '{:Bucket}',
|
131
|
-
:params => { 'logging' =>
|
132
|
+
:params => { 'logging' => '' },
|
132
133
|
:body => { 'BucketLoggingStatus' => :BucketLoggingStatus },
|
133
134
|
:headers => { 'content-type' => 'application/xml'}
|
134
135
|
|
135
136
|
|
136
137
|
base.query_api_pattern 'PutBucketNotification', :put, '{:Bucket}',
|
137
|
-
:params => { 'notification' =>
|
138
|
+
:params => { 'notification' => '' },
|
138
139
|
:body => { 'NotificationConfiguration' => :NotificationConfiguration },
|
139
140
|
:headers => { 'content-type' => 'application/xml'}
|
140
141
|
|
141
142
|
|
142
143
|
base.query_api_pattern 'PutBucketRequestPayment', :put, '{:Bucket}',
|
143
|
-
:params => { 'requestPayment' =>
|
144
|
+
:params => { 'requestPayment' => '' },
|
144
145
|
:body => { 'RequestPaymentConfiguration' => :RequestPaymentConfiguration },
|
145
146
|
:headers => { 'content-type' => 'application/xml'}
|
146
147
|
|
147
148
|
|
148
149
|
base.query_api_pattern 'PutBucketVersioning', :put, '{:Bucket}',
|
149
|
-
:params => { 'versioning' =>
|
150
|
+
:params => { 'versioning' => '' },
|
150
151
|
:body => { 'VersioningConfiguration' => :VersioningConfiguration },
|
151
152
|
:headers => { 'content-type' => 'application/xml'}
|
152
153
|
|
153
154
|
|
154
155
|
base.query_api_pattern 'PutBucketWebsite', :put, '{:Bucket}',
|
155
|
-
:params => { 'website' =>
|
156
|
+
:params => { 'website' => '' },
|
156
157
|
:body => { 'WebsiteConfiguration' => :WebsiteConfiguration },
|
157
158
|
:headers => { 'content-type' => 'application/xml'}
|
158
159
|
|
159
160
|
|
160
161
|
base.query_api_pattern 'GetBucketCors', :get, '{:Bucket}',
|
161
|
-
:params => { 'cors'=>
|
162
|
+
:params => { 'cors'=> '' }
|
162
163
|
|
163
164
|
|
164
165
|
base.query_api_pattern 'DeleteBucketCors', :delete, '{:Bucket}',
|
165
|
-
:params => { 'cors'=>
|
166
|
+
:params => { 'cors'=> '' }
|
166
167
|
|
167
168
|
|
168
169
|
base.query_api_pattern 'PutBucketCors', :put, '{:Bucket}',
|
169
|
-
:params => { 'cors'=>
|
170
|
+
:params => { 'cors'=> '' },
|
170
171
|
:body => { 'CORSConfiguration' => { 'CORSRule' => :CORSRule } },
|
171
172
|
:headers => { 'content-type' => 'application/xml'}
|
172
173
|
|
173
174
|
|
174
175
|
base.query_api_pattern 'GetBucketTagging', :get, '{:Bucket}',
|
175
|
-
:params => { 'tagging' =>
|
176
|
+
:params => { 'tagging' => '' }
|
176
177
|
|
177
178
|
|
178
179
|
base.query_api_pattern 'PutBucketTagging', :put, '{:Bucket}',
|
179
|
-
:params => { 'tagging' =>
|
180
|
+
:params => { 'tagging' => '' },
|
180
181
|
:body => { 'Tagging' => { 'TagSet' => { 'Tag' => :TagSet } } },
|
181
182
|
:headers => { 'content-type' => 'application/xml'}
|
182
183
|
|
183
184
|
|
184
185
|
base.query_api_pattern 'DeleteBucketTagging', :delete, '{:Bucket}',
|
185
|
-
:params => { 'tagging' =>
|
186
|
+
:params => { 'tagging' => '' }
|
186
187
|
|
187
188
|
|
188
189
|
base.query_api_pattern 'GetBucketLifecycle', :get, '{:Bucket}',
|
189
|
-
:params => { 'lifecycle' =>
|
190
|
+
:params => { 'lifecycle' => '' }
|
190
191
|
|
191
192
|
|
192
193
|
base.query_api_pattern 'PutBucketLifecycle', :put, '{:Bucket}',
|
193
|
-
:params => { 'lifecycle' =>
|
194
|
+
:params => { 'lifecycle' => '' },
|
194
195
|
:body => { 'LifecycleConfiguration' => { 'Rule' => :Rule } },
|
195
196
|
:headers => { 'content-type' => 'application/xml'}
|
196
197
|
|
197
198
|
|
198
199
|
base.query_api_pattern 'DeleteBucketLifecycle', :delete, '{:Bucket}',
|
199
|
-
:params => { 'lifecycle' =>
|
200
|
+
:params => { 'lifecycle' => '' }
|
200
201
|
|
201
202
|
|
202
203
|
#-----------------
|
@@ -208,7 +209,8 @@ module RightScale
|
|
208
209
|
|
209
210
|
|
210
211
|
base.query_api_pattern 'DeleteMultipleObjects', :post, '{:Bucket}',
|
211
|
-
|
212
|
+
:headers => { 'content-type' => 'application/xml'},
|
213
|
+
:params => { 'delete' => '' },
|
212
214
|
:body => { 'Delete' => {
|
213
215
|
'Quiet' => :Quiet,
|
214
216
|
'Object[{:Object}]' => {
|
@@ -236,11 +238,11 @@ module RightScale
|
|
236
238
|
|
237
239
|
|
238
240
|
base.query_api_pattern 'GetObjectAcl', :get, '{:Bucket}/{:Object}',
|
239
|
-
:params => { 'acl' =>
|
241
|
+
:params => { 'acl' => '' }
|
240
242
|
|
241
243
|
|
242
244
|
base.query_api_pattern 'GetObjectTorrent', :get, '{:Bucket}/{:Object}',
|
243
|
-
:params => { 'torrent'=>
|
245
|
+
:params => { 'torrent'=> '' }
|
244
246
|
|
245
247
|
|
246
248
|
base.query_api_pattern 'HeadObject', :head, '{:Bucket}/{:Object}'
|
@@ -255,13 +257,13 @@ module RightScale
|
|
255
257
|
|
256
258
|
|
257
259
|
base.query_api_pattern 'PutObjectAcl', :put, '{:Bucket}/{:Object}',
|
258
|
-
:params => { 'acl'=>
|
260
|
+
:params => { 'acl'=> '' },
|
259
261
|
:body => { 'AccessControlPolicy' => :AccessControlPolicy },
|
260
262
|
:headers => { 'content-type' => 'application/xml'}
|
261
263
|
|
262
264
|
|
263
265
|
base.query_api_pattern 'PutObjectCannedAcl', :put, '{:Bucket}/{:Object}',
|
264
|
-
:params => { 'acl' =>
|
266
|
+
:params => { 'acl' => '' },
|
265
267
|
:headers => { 'x-amz-acl' => :Acl, 'content-type' => 'application/xml'}
|
266
268
|
|
267
269
|
|
@@ -278,7 +280,7 @@ module RightScale
|
|
278
280
|
|
279
281
|
|
280
282
|
base.query_api_pattern 'InitiateMultipartUpload', :post, '{:Bucket}/{:Object}',
|
281
|
-
:params => { 'uploads' =>
|
283
|
+
:params => { 'uploads' => '' }
|
282
284
|
|
283
285
|
|
284
286
|
base.query_api_pattern 'UploadPart', :post, '{:Bucket}/{:Object}',
|
@@ -22,7 +22,7 @@
|
|
22
22
|
#++
|
23
23
|
|
24
24
|
require "cloud/aws/base/helpers/utils"
|
25
|
-
require "cloud/aws/
|
25
|
+
require "cloud/aws/sqs/routines/request_signer"
|
26
26
|
require "cloud/aws/base/parsers/response_error"
|
27
27
|
|
28
28
|
module RightScale
|
@@ -206,7 +206,7 @@ module RightScale
|
|
206
206
|
|
207
207
|
set_routine CloudApi::RetryManager
|
208
208
|
set_routine CloudApi::RequestInitializer
|
209
|
-
set_routine AWS::RequestSigner
|
209
|
+
set_routine AWS::SQS::RequestSigner
|
210
210
|
set_routine CloudApi::RequestGenerator
|
211
211
|
set_routine CloudApi::RequestAnalyzer
|
212
212
|
set_routine CloudApi::ConnectionProxy
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2013 RightScale, Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# 'Software'), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
18
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
19
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
20
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
21
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
module RightScale
|
25
|
+
module CloudApi
|
26
|
+
module AWS
|
27
|
+
module SQS
|
28
|
+
# SQS Request signer
|
29
|
+
class RequestSigner < CloudApi::Routine
|
30
|
+
|
31
|
+
# RequestSigner error
|
32
|
+
class Error < CloudApi::Error
|
33
|
+
end
|
34
|
+
|
35
|
+
# Authenticates an SQS request
|
36
|
+
#
|
37
|
+
# @return [void]
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# no example
|
41
|
+
#
|
42
|
+
def process
|
43
|
+
# Compile a final request path
|
44
|
+
@data[:request][:path] = Utils::join_urn(@data[:connection][:uri].path, @data[:request][:relative_path])
|
45
|
+
|
46
|
+
# Swap query params and body
|
47
|
+
@data[:request][:params]['Version'] ||= @data[:options][:api_version]
|
48
|
+
#@data[:request][:body] = Utils::params_to_urn(@data[:request][:params]){ |value| Utils::AWS::amz_escape(value) }
|
49
|
+
#@data[:request][:params] = {}
|
50
|
+
|
51
|
+
Utils::AWS::sign_v4_signature(
|
52
|
+
@data[:credentials][:aws_access_key_id],
|
53
|
+
@data[:credentials][:aws_secret_access_key],
|
54
|
+
@data[:connection][:uri].host,
|
55
|
+
@data[:request]
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/right_aws_api.gemspec
CHANGED
@@ -35,10 +35,11 @@ Gem::Specification.new do |spec|
|
|
35
35
|
spec.require_path = 'lib'
|
36
36
|
spec.required_ruby_version = '>= 1.8.7'
|
37
37
|
|
38
|
-
spec.add_dependency 'right_cloud_api_base', '>= 0.1
|
38
|
+
spec.add_dependency 'right_cloud_api_base', '>= 0.2.1'
|
39
39
|
|
40
40
|
spec.add_development_dependency 'rake'
|
41
41
|
spec.add_development_dependency 'rspec', '>= 3.0.0'
|
42
|
+
spec.add_development_dependency 'timecop'
|
42
43
|
|
43
44
|
spec.description = <<-EOF
|
44
45
|
== DESCRIPTION:
|
@@ -0,0 +1,298 @@
|
|
1
|
+
require_relative '../../../../../lib/right_aws_api'
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'timecop'
|
5
|
+
|
6
|
+
describe RightScale::CloudApi::Utils::AWS do
|
7
|
+
subject do
|
8
|
+
RightScale::CloudApi::Utils::AWS
|
9
|
+
end
|
10
|
+
|
11
|
+
context "signature v4" do
|
12
|
+
|
13
|
+
context "sign_v4_get_service_and_region" do
|
14
|
+
context "weird unknown host" do
|
15
|
+
it "fails" do
|
16
|
+
expect{
|
17
|
+
subject.sign_v4_get_service_and_region('foo.bar.com')
|
18
|
+
}.to raise_error(ArgumentError)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "host with no service" do
|
23
|
+
it "fails" do
|
24
|
+
expect{
|
25
|
+
subject.sign_v4_get_service_and_region('foo.amazonaws.com')
|
26
|
+
}.to raise_error(ArgumentError,/service/)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "S3" do
|
31
|
+
it "works" do
|
32
|
+
expect(
|
33
|
+
subject.sign_v4_get_service_and_region('s3.amazonaws.com')
|
34
|
+
).to eq ['s3', 'us-east-1']
|
35
|
+
|
36
|
+
expect(
|
37
|
+
subject.sign_v4_get_service_and_region('xxx.s3.amazonaws.com')
|
38
|
+
).to eq ['s3', 'us-east-1']
|
39
|
+
|
40
|
+
expect(
|
41
|
+
subject.sign_v4_get_service_and_region('s3-external-1.amazonaws.com')
|
42
|
+
).to eq ['s3', 'us-east-1']
|
43
|
+
|
44
|
+
expect(
|
45
|
+
subject.sign_v4_get_service_and_region('s3-region-1.amazonaws.com')
|
46
|
+
).to eq ['s3', 'region-1']
|
47
|
+
|
48
|
+
expect(
|
49
|
+
subject.sign_v4_get_service_and_region('xxx.s3-region-1.amazonaws.com')
|
50
|
+
).to eq ['s3', 'region-1']
|
51
|
+
|
52
|
+
expect(
|
53
|
+
subject.sign_v4_get_service_and_region('s3.region-1.amazonaws.com')
|
54
|
+
).to eq ['s3', 'region-1']
|
55
|
+
|
56
|
+
expect(
|
57
|
+
subject.sign_v4_get_service_and_region('xxx.s3.region-1.amazonaws.com')
|
58
|
+
).to eq ['s3', 'region-1']
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
context "any other service with standard endpoint" do
|
64
|
+
it "works" do
|
65
|
+
expect(
|
66
|
+
subject.sign_v4_get_service_and_region('ec2.region-1.amazonaws.com')
|
67
|
+
).to eq ['ec2', 'region-1']
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
context "sign_v4_get_canonical_verb" do
|
74
|
+
it "uppercases the given verb" do
|
75
|
+
expect( subject.sign_v4_get_canonical_verb(:delete) ).to eq 'DELETE'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
context "sign_v4_get_canonical_path" do
|
81
|
+
it "does nothing to the given path" do
|
82
|
+
expect( subject.sign_v4_get_canonical_path('foo/bar') ).to eq 'foo/bar'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
context "sign_v4_get_canonical_headers" do
|
88
|
+
it "works" do
|
89
|
+
headers = {
|
90
|
+
'foo' => 'x',
|
91
|
+
'bar' => 'y,z'
|
92
|
+
}
|
93
|
+
expect(
|
94
|
+
subject.sign_v4_get_canonical_headers(headers)
|
95
|
+
).to eq "bar:y,z\nfoo:x"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
context "sign_v4_get_signed_headers" do
|
101
|
+
it "works" do
|
102
|
+
headers = {
|
103
|
+
'foo' => 'x',
|
104
|
+
'bar' => 'y,z'
|
105
|
+
}
|
106
|
+
expect(
|
107
|
+
subject.sign_v4_get_signed_headers(headers)
|
108
|
+
).to eq "bar;foo"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
context "sign_v4_headers" do
|
114
|
+
it "builds credential params" do
|
115
|
+
request = {
|
116
|
+
:headers => RightScale::CloudApi::HTTPHeaders.new,
|
117
|
+
:params => { 'A' => 'B' },
|
118
|
+
:body => "banana"
|
119
|
+
}
|
120
|
+
expect(
|
121
|
+
subject.sign_v4_headers(
|
122
|
+
request,
|
123
|
+
"ec2.region-1.amazonaws.com",
|
124
|
+
"20141103T150750Z"
|
125
|
+
)
|
126
|
+
).to eq "b493d48364afe44d11c0165cf470a4164d1e2609911ef998be868d46ade3de4e"
|
127
|
+
|
128
|
+
expect(request).to eq(
|
129
|
+
{
|
130
|
+
:body => "banana",
|
131
|
+
:params => { 'A' => 'B' },
|
132
|
+
:headers => {
|
133
|
+
"content-length" => [6],
|
134
|
+
"content-type" => ["application/x-www-form-urlencoded; charset=utf-8"],
|
135
|
+
"content-md5" => ["crMCvyl6Iop1cwEj7+98QQ=="],
|
136
|
+
"x-amz-content-sha256" => ["b493d48364afe44d11c0165cf470a4164d1e2609911ef998be868d46ade3de4e"],
|
137
|
+
"x-amz-date" => ["20141103T150750Z"],
|
138
|
+
"x-amz-expires" => [3600]
|
139
|
+
}
|
140
|
+
}
|
141
|
+
)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
context "sign_v4_query_params" do
|
147
|
+
it "builds credential params" do
|
148
|
+
request = { :params => {} }
|
149
|
+
expect(
|
150
|
+
subject.sign_v4_query_params(
|
151
|
+
request,
|
152
|
+
"AWS4-HMAC-SHA256",
|
153
|
+
"20141103T150750Z",
|
154
|
+
"host;content-type",
|
155
|
+
"aws_access_key",
|
156
|
+
"20141103/region-1/ec2/aws4_request"
|
157
|
+
)
|
158
|
+
).to eq "UNSIGNED-PAYLOAD"
|
159
|
+
|
160
|
+
expect(request).to eq(
|
161
|
+
{
|
162
|
+
:params => {
|
163
|
+
"X-Amz-Date" => "20141103T150750Z",
|
164
|
+
"X-Amz-Expires" => 3600,
|
165
|
+
"X-Amz-Algorithm" => "AWS4-HMAC-SHA256",
|
166
|
+
"X-Amz-SignedHeaders" => "host;content-type",
|
167
|
+
"X-Amz-Credential" => "aws_access_key/20141103/region-1/ec2/aws4_request"
|
168
|
+
}
|
169
|
+
}
|
170
|
+
)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
context "sign_v4_get_canonical_string" do
|
176
|
+
it "joins things properly" do
|
177
|
+
expect(
|
178
|
+
subject.sign_v4_get_canonical_string(
|
179
|
+
'verb',
|
180
|
+
'path',
|
181
|
+
'query_string',
|
182
|
+
'headers',
|
183
|
+
'signed_headers',
|
184
|
+
'payload'
|
185
|
+
)
|
186
|
+
).to eq "verb\npath\nquery_string\nheaders\n\nsigned_headers\npayload"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
|
191
|
+
context "sign_v4_get_string_to_sign" do
|
192
|
+
it "joins things properly" do
|
193
|
+
expect(
|
194
|
+
subject.sign_v4_get_string_to_sign('algorithm', 'current_time', 'creds_scope', 'canonical_string')
|
195
|
+
).to eq "algorithm\ncurrent_time\ncreds_scope\ne32453154e605024921ee9c5eb4ee7eba458cbd2b387c9ac74e729f9fd7cb81b"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
context "sign_v4_get_signature_key" do
|
201
|
+
it "works" do
|
202
|
+
expect(
|
203
|
+
subject.sign_v4_get_signature_key('key', 'string_to_sign', 'date', 'region', 'service')
|
204
|
+
).to eq "0eb6bf1678c804d70bdda801326b3b70080ff19f2cb381e28231b2dcdd445b7c"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
context "hex_encode" do
|
210
|
+
it "works" do
|
211
|
+
expect(
|
212
|
+
subject.hex_encode('HelloWorld!')
|
213
|
+
).to eq "48656c6c6f576f726c6421"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
context "sign_v4_signature" do
|
219
|
+
context "signature in headers" do
|
220
|
+
it 'works' do
|
221
|
+
request = {
|
222
|
+
:verb => :post,
|
223
|
+
:path => '/foo/bar',
|
224
|
+
:headers => RightScale::CloudApi::HTTPHeaders.new,
|
225
|
+
:params => { 'A' => 'B' },
|
226
|
+
:body => "banana"
|
227
|
+
}
|
228
|
+
Timecop.freeze(Time.local(2014)) do
|
229
|
+
subject.sign_v4_signature(
|
230
|
+
'aws_access_key',
|
231
|
+
'aws_secret_access_key',
|
232
|
+
'ec2.us-region-1.amazonaws.com',
|
233
|
+
request,
|
234
|
+
)
|
235
|
+
end
|
236
|
+
expect(request).to eq(
|
237
|
+
{
|
238
|
+
:body => "banana",
|
239
|
+
:headers => {
|
240
|
+
"content-length" => [6],
|
241
|
+
"content-type" => ["application/x-www-form-urlencoded; charset=utf-8"],
|
242
|
+
"content-md5" => ["crMCvyl6Iop1cwEj7+98QQ=="],
|
243
|
+
"x-amz-content-sha256" => ["b493d48364afe44d11c0165cf470a4164d1e2609911ef998be868d46ade3de4e"],
|
244
|
+
"x-amz-date" => ["20140101T080000Z"],
|
245
|
+
"x-amz-expires" => [3600],
|
246
|
+
"host" => ["ec2.us-region-1.amazonaws.com"],
|
247
|
+
"authorization" => ["AWS4-HMAC-SHA256 Credential=aws_access_key/20140101/us-region-1/ec2/aws4_request, SignedHeaders=content-length;content-md5;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-expires, Signature=c7814d3865b530322742b73670475ac5adb0c1bd4ab39ecf9b80d672283fc177"]
|
248
|
+
},
|
249
|
+
:params => {"A" => "B"},
|
250
|
+
:path => "/foo/bar?A=B",
|
251
|
+
:verb => :post
|
252
|
+
}
|
253
|
+
)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
context "signature in query params" do
|
259
|
+
it 'works' do
|
260
|
+
request = {
|
261
|
+
:verb => :post,
|
262
|
+
:path => '/foo/bar',
|
263
|
+
:headers => RightScale::CloudApi::HTTPHeaders.new,
|
264
|
+
:params => { 'A' => 'B' },
|
265
|
+
:body => "banana"
|
266
|
+
}
|
267
|
+
Timecop.freeze(Time.local(2014)) do
|
268
|
+
subject.sign_v4_signature(
|
269
|
+
'aws_access_key',
|
270
|
+
'aws_secret_access_key',
|
271
|
+
'ec2.us-region-1.amazonaws.com',
|
272
|
+
request,
|
273
|
+
:query_params
|
274
|
+
)
|
275
|
+
end
|
276
|
+
expect(request).to eq(
|
277
|
+
{
|
278
|
+
:body => "banana",
|
279
|
+
:headers => {"host"=>["ec2.us-region-1.amazonaws.com"]},
|
280
|
+
:params => {
|
281
|
+
"A" => "B",
|
282
|
+
"X-Amz-Date" => "20140101T080000Z",
|
283
|
+
"X-Amz-Expires" => 3600,
|
284
|
+
"X-Amz-Algorithm" => "AWS4-HMAC-SHA256",
|
285
|
+
"X-Amz-SignedHeaders" => "host",
|
286
|
+
"X-Amz-Credential" => "aws_access_key/20140101/us-region-1/ec2/aws4_request"
|
287
|
+
},
|
288
|
+
:path => "/foo/bar?A=B&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=aws_access_key%2F20140101%2Fus-region-1%2Fec2%2Faws4_request&X-Amz-Date=20140101T080000Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=0fcac655717af1f947fc0942cc2a2e98fddfe71cf669b0a344235bb83684c9cf",
|
289
|
+
:verb => :post
|
290
|
+
}
|
291
|
+
)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
298
|
+
end
|