escher 0.2.1 → 0.3.1

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.
data/spec/escher_spec.rb DELETED
@@ -1,358 +0,0 @@
1
- require 'spec_helper'
2
-
3
- test_suites = {
4
- # 'get-header-key-duplicate',
5
- # 'get-header-value-order',
6
- aws4: %w(
7
- get-header-value-trim
8
- get-relative
9
- get-relative-relative
10
- get-slash
11
- get-slash-dot-slash
12
- get-slash-pointless-dot
13
- get-slashes
14
- get-space
15
- get-unreserved
16
- get-utf8
17
- get-vanilla
18
- get-vanilla-empty-query-key
19
- get-vanilla-query
20
- get-vanilla-query-order-key
21
- get-vanilla-query-order-key-case
22
- get-vanilla-query-order-value
23
- get-vanilla-query-unreserved
24
- get-vanilla-ut8-query
25
- post-header-key-case
26
- post-header-key-sort
27
- post-header-value-case
28
- post-vanilla
29
- post-vanilla-empty-query-value
30
- post-vanilla-query
31
- post-vanilla-query-nonunreserved
32
- post-vanilla-query-space
33
- post-x-www-form-urlencoded
34
- post-x-www-form-urlencoded-parameters
35
- ),
36
- emarsys: %w(
37
- get-header-key-duplicate
38
- post-header-key-order
39
- post-header-value-spaces
40
- post-header-value-spaces-within-quotes
41
- )
42
- }
43
-
44
- ESCHER_AWS4_OPTIONS = {
45
- algo_prefix: 'AWS4', vendor_key: 'AWS4', hash_algo: 'SHA256', auth_header_name: 'Authorization', date_header_name: 'Date'
46
- }
47
-
48
- ESCHER_MIXED_OPTIONS = {
49
- algo_prefix: 'EMS', vendor_key: 'EMS', hash_algo: 'SHA256', auth_header_name: 'Authorization', date_header_name: 'Date', clock_skew: 10
50
- }
51
-
52
- ESCHER_EMARSYS_OPTIONS = ESCHER_MIXED_OPTIONS.merge(auth_header_name: 'X-Ems-Auth', date_header_name: 'X-Ems-Date')
53
-
54
- GOOD_AUTH_HEADER = 'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470'
55
-
56
- # noinspection RubyStringKeysInHashInspection
57
- def key_db
58
- {
59
- 'AKIDEXAMPLE' => 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY',
60
- 'th3K3y' => 'very_secure',
61
- }
62
- end
63
-
64
- def credential_scope
65
- %w(us-east-1 host aws4_request)
66
- end
67
-
68
- def client
69
- {:api_key_id => 'AKIDEXAMPLE', :api_secret => 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'}
70
- end
71
-
72
- describe 'Escher' do
73
- test_suites.each do |suite, tests|
74
- tests.each do |test|
75
- it "should calculate canonicalized request for #{test} in #{suite}" do
76
- escher = Escher.new('us-east-1/host/aws4_request', ESCHER_AWS4_OPTIONS)
77
- method, request_uri, body, headers = read_request(suite, test)
78
- headers_to_sign = headers.map {|k| k[0].downcase }
79
- path, query_parts = escher.parse_uri(request_uri)
80
- canonicalized_request = escher.canonicalize(method, path, query_parts, body, headers, headers_to_sign)
81
- check_canonicalized_request(canonicalized_request, suite, test)
82
- end
83
- end
84
- end
85
-
86
- test_suites.each do |suite, tests|
87
- tests.each do |test|
88
- it "should calculate string to sign for #{test} in #{suite}" do
89
- method, request_uri, body, headers, date = read_request(suite, test)
90
- escher = Escher.new('us-east-1/host/aws4_request', ESCHER_AWS4_OPTIONS.merge(current_time: Time.parse(date)))
91
- headers_to_sign = headers.map {|k| k[0].downcase }
92
- path, query_parts = escher.parse_uri(request_uri)
93
- canonicalized_request = escher.canonicalize(method, path, query_parts, body, headers, headers_to_sign)
94
- string_to_sign = escher.get_string_to_sign(canonicalized_request)
95
- expect(string_to_sign).to eq(fixture(suite, test, 'sts'))
96
- end
97
- end
98
- end
99
-
100
- test_suites.each do |suite, tests|
101
- tests.each do |test|
102
- it "should calculate auth header for #{test} in #{suite}" do
103
- method, request_uri, body, headers, date = read_request(suite, test)
104
- escher = Escher.new('us-east-1/host/aws4_request', ESCHER_AWS4_OPTIONS.merge(current_time: Time.parse(date)))
105
- headers_to_sign = headers.map {|k| k[0].downcase }
106
- auth_header = escher.generate_auth_header(client, method, request_uri, body, headers, headers_to_sign)
107
- expect(auth_header).to eq(fixture(suite, test, 'authz'))
108
- end
109
- end
110
- end
111
-
112
- it 'should sign request' do
113
- escher = Escher.new('us-east-1/iam/aws4_request', ESCHER_EMARSYS_OPTIONS.merge(current_time: Time.parse('20110909T233600Z')))
114
- client = { :api_key_id => 'AKIDEXAMPLE', :api_secret => 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY' }
115
-
116
- input_headers = {
117
- 'content-type' => 'application/x-www-form-urlencoded; charset=utf-8'
118
- }
119
-
120
- expected_headers = {
121
- 'content-type' => 'application/x-www-form-urlencoded; charset=utf-8',
122
- 'host' => 'iam.amazonaws.com',
123
- 'x-ems-date' => '20110909T233600Z',
124
- 'x-ems-auth' => 'EMS-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/iam/aws4_request, SignedHeaders=content-type;host;x-ems-date, Signature=f36c21c6e16a71a6e8dc56673ad6354aeef49c577a22fd58a190b5fcf8891dbd',
125
- }
126
- headers_to_sign = %w(content-type)
127
-
128
- request = {
129
- method: 'POST',
130
- uri: 'http://iam.amazonaws.com/',
131
- body: 'Action=ListUsers&Version=2010-05-08',
132
- headers: input_headers,
133
- }
134
-
135
- downcase = escher.sign!(request, client, headers_to_sign)[:headers].map { |k, v| { k.downcase => v } }.reduce({}, &:merge)
136
- expect(downcase).to eq expected_headers
137
- end
138
-
139
- it 'should generate presigned url' do
140
- escher = Escher.new('us-east-1/host/aws4_request', ESCHER_MIXED_OPTIONS.merge(current_time: Time.parse('2011/05/11 12:00:00 UTC')))
141
- expected_url =
142
- 'http://example.com/something?foo=bar&' + 'baz=barbaz&' +
143
- 'X-EMS-Algorithm=EMS-HMAC-SHA256&' +
144
- 'X-EMS-Credentials=th3K3y%2F20110511%2Fus-east-1%2Fhost%2Faws4_request&' +
145
- 'X-EMS-Date=20110511T120000Z&' +
146
- 'X-EMS-Expires=123456&' +
147
- 'X-EMS-SignedHeaders=host&' +
148
- 'X-EMS-Signature=fbc9dbb91670e84d04ad2ae7505f4f52ab3ff9e192b8233feeae57e9022c2b67'
149
-
150
- client = {:api_key_id => 'th3K3y', :api_secret => 'very_secure'}
151
- expect(escher.generate_signed_url('http://example.com/something?foo=bar&baz=barbaz', client, 123456)).to eq expected_url
152
- end
153
-
154
- it 'should validate presigned url' do
155
- escher = Escher.new('us-east-1/host/aws4_request', ESCHER_MIXED_OPTIONS.merge(current_time: Time.parse('2011/05/12 21:59:00 UTC')))
156
- presigned_uri =
157
- '/something?foo=bar&' + 'baz=barbaz&' +
158
- 'X-EMS-Algorithm=EMS-HMAC-SHA256&' +
159
- 'X-EMS-Credentials=th3K3y%2F20110511%2Fus-east-1%2Fhost%2Faws4_request&' +
160
- 'X-EMS-Date=20110511T120000Z&' +
161
- 'X-EMS-Expires=123456&' +
162
- 'X-EMS-SignedHeaders=host&' +
163
- 'X-EMS-Signature=fbc9dbb91670e84d04ad2ae7505f4f52ab3ff9e192b8233feeae57e9022c2b67'
164
-
165
- client = {:api_key_id => 'th3K3y', :api_secret => 'very_secure'}
166
- expect { escher.authenticate({
167
- :method => 'GET',
168
- :headers => [%w(host example.com)],
169
- :uri => presigned_uri,
170
- :body => 'IRRELEVANT'
171
- }, key_db) }.not_to raise_error
172
- end
173
-
174
- it 'should validate expiration' do
175
- escher = Escher.new('us-east-1/host/aws4_request', ESCHER_MIXED_OPTIONS.merge(current_time: Time.parse('2011/05/12 22:20:00 UTC')))
176
- presigned_uri =
177
- '/something?foo=bar&' + 'baz=barbaz&' +
178
- 'X-EMS-Algorithm=EMS-HMAC-SHA256&' +
179
- 'X-EMS-Credentials=th3K3y%2F20110511%2Fus-east-1%2Fhost%2Faws4_request&' +
180
- 'X-EMS-Date=20110511T120000Z&' +
181
- 'X-EMS-Expires=123456&' +
182
- 'X-EMS-SignedHeaders=host&' +
183
- 'X-EMS-Signature=fbc9dbb91670e84d04ad2ae7505f4f52ab3ff9e192b8233feeae57e9022c2b67'
184
-
185
- client = {:api_key_id => 'th3K3y', :api_secret => 'very_secure'}
186
- expect { escher.authenticate({
187
- :method => 'GET',
188
- :headers => [%w(host example.com)],
189
- :uri => presigned_uri,
190
- :body => 'IRRELEVANT'
191
- }, key_db) }.to raise_error(EscherError, 'The request date is not within the accepted time range')
192
- end
193
-
194
- it 'should validate request' do
195
- headers = [
196
- %w(Host host.foo.com),
197
- ['Date', 'Mon, 09 Sep 2011 23:36:00 GMT'],
198
- ['Authorization', GOOD_AUTH_HEADER],
199
- ]
200
- expect { call_validate(headers) }.not_to raise_error
201
- end
202
-
203
- it 'should authenticate' do
204
- headers = [
205
- %w(Host host.foo.com),
206
- ['Date', 'Mon, 09 Sep 2011 23:36:00 GMT'],
207
- ['Authorization', GOOD_AUTH_HEADER],
208
- ]
209
- expect(call_validate(headers)).to eq 'AKIDEXAMPLE'
210
- end
211
-
212
- it 'should detect if signatures do not match' do
213
- headers = [
214
- %w(Host host.foo.com),
215
- ['Date', 'Mon, 09 Sep 2011 23:36:00 GMT'],
216
- ['Authorization', 'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'],
217
- ]
218
- expect { call_validate(headers) }.to raise_error(EscherError, 'The signatures do not match')
219
- end
220
-
221
- it 'should detect if dates are not on the same day' do
222
- yesterday = '08'
223
- headers = [
224
- %w(Host host.foo.com),
225
- ['Date', "Mon, #{yesterday} Sep 2011 23:36:00 GMT"],
226
- ['Authorization', GOOD_AUTH_HEADER],
227
- ]
228
- expect { call_validate(headers) }.to raise_error(EscherError, 'Invalid request date')
229
- end
230
-
231
- it 'should detect if date is not within the 15 minutes range' do
232
- long_ago = '00'
233
- headers = [
234
- %w(Host host.foo.com),
235
- ['Date', "Mon, 09 Sep 2011 23:#{long_ago}:00 GMT"],
236
- ['Authorization', GOOD_AUTH_HEADER],
237
- ]
238
- expect { call_validate(headers) }.to raise_error(EscherError, 'The request date is not within the accepted time range')
239
- end
240
-
241
- it 'should detect missing host header' do
242
- headers = [
243
- ['Date', 'Mon, 09 Sep 2011 23:36:00 GMT'],
244
- ['Authorization', GOOD_AUTH_HEADER],
245
- ]
246
- expect { call_validate(headers) }.to raise_error(EscherError, 'Missing header: Host')
247
- end
248
-
249
- it 'should detect missing date header' do
250
- headers = [
251
- %w(Host host.foo.com),
252
- ['Authorization', GOOD_AUTH_HEADER],
253
- ]
254
- expect { call_validate(headers) }.to raise_error(EscherError, 'Missing header: Date')
255
- end
256
-
257
- it 'should detect missing auth header' do
258
- headers = [
259
- %w(Host host.foo.com),
260
- ['Date', 'Mon, 09 Sep 2011 23:36:00 GMT'],
261
- ]
262
- expect { call_validate(headers) }.to raise_error(EscherError, 'Missing header: Authorization')
263
- end
264
-
265
- it 'should detect malformed auth header' do
266
- headers = [
267
- %w(Host host.foo.com),
268
- ['Date', 'Mon, 09 Sep 2011 23:36:00 GMT'],
269
- ['Authorization', 'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=UNPARSABLE'],
270
- ]
271
- expect { call_validate(headers) }.to raise_error(EscherError, 'Malformed authorization header')
272
- end
273
-
274
- it 'should detect malformed credential scope' do
275
- headers = [
276
- %w(Host host.foo.com),
277
- ['Date', 'Mon, 09 Sep 2011 23:36:00 GMT'],
278
- ['Authorization', 'AWS4-HMAC-SHA256 Credential=BAD-CREDENTIAL-SCOPE, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470'],
279
- ]
280
- expect { call_validate(headers) }.to raise_error(EscherError, 'Malformed authorization header')
281
- end
282
-
283
- it 'should check mandatory signed headers: host' do
284
- headers = [
285
- %w(Host host.foo.com),
286
- ['Date', 'Mon, 09 Sep 2011 23:36:00 GMT'],
287
- ['Authorization', 'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470'],
288
- ]
289
- expect { call_validate(headers) }.to raise_error(EscherError, 'Host header is not signed')
290
- end
291
-
292
- it 'should check mandatory signed headers: date' do
293
- headers = [
294
- %w(Host host.foo.com),
295
- ['Date', 'Mon, 09 Sep 2011 23:36:00 GMT'],
296
- ['Authorization', 'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470'],
297
- ]
298
- expect { call_validate(headers) }.to raise_error(EscherError, 'Date header is not signed')
299
- end
300
-
301
- it 'should check algorithm' do
302
- headers = [
303
- %w(Host host.foo.com),
304
- ['Date', 'Mon, 09 Sep 2011 23:36:00 GMT'],
305
- ['Authorization', 'AWS4-HMAC-INVALID Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470'],
306
- ]
307
- expect { call_validate(headers) }.to raise_error(EscherError, 'Only SHA256 and SHA512 hash algorithms are allowed')
308
- end
309
-
310
- it 'should check credential scope' do
311
- headers = [
312
- %w(Host host.foo.com),
313
- ['Date', 'Mon, 09 Sep 2011 23:36:00 GMT'],
314
- ['Authorization', 'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/INVALID/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470'],
315
- ]
316
- expect { call_validate(headers) }.to raise_error(EscherError, 'Invalid credentials')
317
- end
318
-
319
- it 'should convert dates' do
320
- date_str = 'Fri, 09 Sep 2011 23:36:00 GMT'
321
- expect(Escher.new('irrelevant', date_header_name: 'date', current_time: Time.parse(date_str)).format_date_for_header).to eq date_str
322
- end
323
-
324
- def call_validate(headers)
325
- escher = Escher.new('us-east-1/host/aws4_request', ESCHER_AWS4_OPTIONS.merge(current_time: Time.parse('Mon, 09 Sep 2011 23:40:00 GMT')))
326
- escher.authenticate({
327
- :method => 'GET',
328
- :headers => headers,
329
- :uri => '/',
330
- :body => '',
331
- }, key_db)
332
- end
333
-
334
- end
335
-
336
- def fixture(suite, test, extension)
337
- open("spec/#{suite}_testsuite/#{test}.#{extension}").read
338
- end
339
-
340
- def get_host(headers)
341
- headers.detect {|header| header[0].downcase == 'host'}[1]
342
- end
343
-
344
- def get_date(headers)
345
- headers.detect {|header| header[0].downcase == 'date'}[1]
346
- end
347
-
348
- def read_request(suite, test, extension = 'req')
349
- lines = (fixture(suite, test, extension) + "\n").lines.map(&:chomp)
350
- method, request_uri = lines[0].split ' '
351
- headers = lines[1..-3].map { |header| k, v = header.split(':', 2); [k, v] }
352
- request_body = lines[-1]
353
- [method, request_uri, request_body, headers, get_date(headers), get_host(headers)]
354
- end
355
-
356
- def check_canonicalized_request(creq, suite, test)
357
- expect(creq).to eq(fixture(suite, test, 'creq'))
358
- end