jmoses_api-auth 1.0.4
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/.document +5 -0
- data/.gitignore +44 -0
- data/.rspec +3 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +46 -0
- data/LICENSE.txt +20 -0
- data/README.md +185 -0
- data/Rakefile +22 -0
- data/VERSION +1 -0
- data/api_auth.gemspec +26 -0
- data/lib/api-auth.rb +2 -0
- data/lib/api_auth/base.rb +97 -0
- data/lib/api_auth/errors.rb +9 -0
- data/lib/api_auth/headers.rb +82 -0
- data/lib/api_auth/helpers.rb +18 -0
- data/lib/api_auth/railtie.rb +129 -0
- data/lib/api_auth/request_drivers/action_controller.rb +85 -0
- data/lib/api_auth/request_drivers/action_dispatch.rb +15 -0
- data/lib/api_auth/request_drivers/curb.rb +70 -0
- data/lib/api_auth/request_drivers/net_http.rb +78 -0
- data/lib/api_auth/request_drivers/rack.rb +85 -0
- data/lib/api_auth/request_drivers/rest_client.rb +93 -0
- data/lib/api_auth.rb +16 -0
- data/spec/api_auth_spec.rb +407 -0
- data/spec/application_helper.rb +2 -0
- data/spec/headers_spec.rb +223 -0
- data/spec/helpers_spec.rb +14 -0
- data/spec/railtie_spec.rb +114 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/test_helper.rb +2 -0
- metadata +212 -0
@@ -0,0 +1,407 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "ApiAuth" do
|
4
|
+
|
5
|
+
describe "generating secret keys" do
|
6
|
+
|
7
|
+
it "should generate secret keys" do
|
8
|
+
ApiAuth.generate_secret_key
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should generate secret keys that are 88 characters" do
|
12
|
+
ApiAuth.generate_secret_key.size.should be(88)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should generate keys that have a Hamming Distance of at least 65" do
|
16
|
+
key1 = ApiAuth.generate_secret_key
|
17
|
+
key2 = ApiAuth.generate_secret_key
|
18
|
+
Amatch::Hamming.new(key1).match(key2).should be > 65
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "signing requests" do
|
24
|
+
|
25
|
+
def hmac(secret_key, request)
|
26
|
+
canonical_string = ApiAuth::Headers.new(request).canonical_string
|
27
|
+
digest = OpenSSL::Digest::Digest.new('sha1')
|
28
|
+
ApiAuth.b64_encode(OpenSSL::HMAC.digest(digest, secret_key, canonical_string))
|
29
|
+
end
|
30
|
+
|
31
|
+
before(:all) do
|
32
|
+
@access_id = "1044"
|
33
|
+
@secret_key = ApiAuth.generate_secret_key
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "with Net::HTTP" do
|
37
|
+
|
38
|
+
before(:each) do
|
39
|
+
@request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
40
|
+
'content-type' => 'text/plain',
|
41
|
+
'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
|
42
|
+
'date' => Time.now.utc.httpdate)
|
43
|
+
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should return a Net::HTTP object after signing it" do
|
47
|
+
ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match("Net::HTTP")
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "md5 header" do
|
51
|
+
context "not already provided" do
|
52
|
+
it "should calculate for empty string" do
|
53
|
+
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
54
|
+
'content-type' => 'text/plain',
|
55
|
+
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
56
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
57
|
+
signed_request['Content-MD5'].should == Digest::MD5.base64digest('')
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should calculate for real content" do
|
61
|
+
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
62
|
+
'content-type' => 'text/plain',
|
63
|
+
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
64
|
+
request.body = "hello\nworld"
|
65
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
66
|
+
signed_request['Content-MD5'].should == Digest::MD5.base64digest("hello\nworld")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should leave the content-md5 alone if provided" do
|
71
|
+
@signed_request['Content-MD5'].should == '1B2M2Y8AsgTpgAmY7PhCfg=='
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should sign the request" do
|
76
|
+
@signed_request['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should authenticate a valid request" do
|
80
|
+
ApiAuth.authentic?(@signed_request, @secret_key).should be_true
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should NOT authenticate a non-valid request" do
|
84
|
+
ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should NOT authenticate a mismatched content-md5 when body has changed" do
|
88
|
+
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
89
|
+
'content-type' => 'text/plain',
|
90
|
+
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
91
|
+
request.body = "hello\nworld"
|
92
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
93
|
+
signed_request.body = "goodbye"
|
94
|
+
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should NOT authenticate an expired request" do
|
98
|
+
@request['Date'] = 16.minutes.ago.utc.httpdate
|
99
|
+
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
100
|
+
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should retrieve the access_id" do
|
104
|
+
ApiAuth.access_id(@signed_request).should == "1044"
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "with RestClient" do
|
110
|
+
|
111
|
+
before(:each) do
|
112
|
+
headers = { 'Content-MD5' => "1B2M2Y8AsgTpgAmY7PhCfg==",
|
113
|
+
'Content-Type' => "text/plain",
|
114
|
+
'Date' => Time.now.utc.httpdate }
|
115
|
+
@request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
116
|
+
:headers => headers,
|
117
|
+
:method => :put)
|
118
|
+
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should return a RestClient object after signing it" do
|
122
|
+
ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match("RestClient")
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "md5 header" do
|
126
|
+
context "not already provided" do
|
127
|
+
it "should calculate for empty string" do
|
128
|
+
headers = { 'Content-Type' => "text/plain",
|
129
|
+
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
130
|
+
request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
131
|
+
:headers => headers,
|
132
|
+
:method => :put)
|
133
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
134
|
+
signed_request.headers['Content-MD5'].should == Digest::MD5.base64digest('')
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should calculate for real content" do
|
138
|
+
headers = { 'Content-Type' => "text/plain",
|
139
|
+
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
140
|
+
request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
141
|
+
:headers => headers,
|
142
|
+
:method => :put,
|
143
|
+
:payload => "hellow\nworld")
|
144
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
145
|
+
signed_request.headers['Content-MD5'].should == Digest::MD5.base64digest("hellow\nworld")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should leave the content-md5 alone if provided" do
|
150
|
+
@signed_request.headers['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should sign the request" do
|
155
|
+
@signed_request.headers['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should authenticate a valid request" do
|
159
|
+
ApiAuth.authentic?(@signed_request, @secret_key).should be_true
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should NOT authenticate a non-valid request" do
|
163
|
+
ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should NOT authenticate a mismatched content-md5 when body has changed" do
|
167
|
+
headers = { 'Content-Type' => "text/plain",
|
168
|
+
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
169
|
+
request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
170
|
+
:headers => headers,
|
171
|
+
:method => :put,
|
172
|
+
:payload => "hello\nworld")
|
173
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
174
|
+
signed_request.instance_variable_set("@payload", RestClient::Payload.generate('goodbye'))
|
175
|
+
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should NOT authenticate an expired request" do
|
179
|
+
@request.headers['Date'] = 16.minutes.ago.utc.httpdate
|
180
|
+
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
181
|
+
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should retrieve the access_id" do
|
185
|
+
ApiAuth.access_id(@signed_request).should == "1044"
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "with Curb" do
|
191
|
+
|
192
|
+
before(:each) do
|
193
|
+
headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
|
194
|
+
'Content-Type' => "text/plain",
|
195
|
+
'Date' => Time.now.utc.httpdate }
|
196
|
+
@request = Curl::Easy.new("/resource.xml?foo=bar&bar=foo") do |curl|
|
197
|
+
curl.headers = headers
|
198
|
+
end
|
199
|
+
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should return a Curl::Easy object after signing it" do
|
203
|
+
ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match("Curl::Easy")
|
204
|
+
end
|
205
|
+
|
206
|
+
describe "md5 header" do
|
207
|
+
it "should not calculate and add the content-md5 header if not provided" do
|
208
|
+
headers = { 'Content-Type' => "text/plain",
|
209
|
+
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
210
|
+
request = Curl::Easy.new("/resource.xml?foo=bar&bar=foo") do |curl|
|
211
|
+
curl.headers = headers
|
212
|
+
end
|
213
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
214
|
+
signed_request.headers['Content-MD5'].should == nil
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should leave the content-md5 alone if provided" do
|
218
|
+
@signed_request.headers['Content-MD5'].should == "e59ff97941044f85df5297e1c302d260"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should sign the request" do
|
223
|
+
@signed_request.headers['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should authenticate a valid request" do
|
227
|
+
ApiAuth.authentic?(@signed_request, @secret_key).should be_true
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should NOT authenticate a non-valid request" do
|
231
|
+
ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should NOT authenticate an expired request" do
|
235
|
+
@request.headers['Date'] = 16.minutes.ago.utc.httpdate
|
236
|
+
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
237
|
+
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should retrieve the access_id" do
|
241
|
+
ApiAuth.access_id(@signed_request).should == "1044"
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
245
|
+
|
246
|
+
describe "with ActionController" do
|
247
|
+
|
248
|
+
before(:each) do
|
249
|
+
@request = ActionController::Request.new(
|
250
|
+
'PATH_INFO' => '/resource.xml',
|
251
|
+
'QUERY_STRING' => 'foo=bar&bar=foo',
|
252
|
+
'REQUEST_METHOD' => 'PUT',
|
253
|
+
'CONTENT_MD5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
|
254
|
+
'CONTENT_TYPE' => 'text/plain',
|
255
|
+
'HTTP_DATE' => Time.now.utc.httpdate)
|
256
|
+
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should return a ActionController::Request object after signing it" do
|
260
|
+
ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match("ActionController::Request")
|
261
|
+
end
|
262
|
+
|
263
|
+
describe "md5 header" do
|
264
|
+
context "not already provided" do
|
265
|
+
it "should calculate for empty string" do
|
266
|
+
request = ActionController::Request.new(
|
267
|
+
'PATH_INFO' => '/resource.xml',
|
268
|
+
'QUERY_STRING' => 'foo=bar&bar=foo',
|
269
|
+
'REQUEST_METHOD' => 'PUT',
|
270
|
+
'CONTENT_TYPE' => 'text/plain',
|
271
|
+
'HTTP_DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT')
|
272
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
273
|
+
signed_request.env['Content-MD5'].should == Digest::MD5.base64digest('')
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should calculate for real content" do
|
277
|
+
request = ActionController::Request.new(
|
278
|
+
'PATH_INFO' => '/resource.xml',
|
279
|
+
'QUERY_STRING' => 'foo=bar&bar=foo',
|
280
|
+
'REQUEST_METHOD' => 'PUT',
|
281
|
+
'CONTENT_TYPE' => 'text/plain',
|
282
|
+
'HTTP_DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT',
|
283
|
+
'RAW_POST_DATA' => "hello\nworld")
|
284
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
285
|
+
signed_request.env['Content-MD5'].should == Digest::MD5.base64digest("hello\nworld")
|
286
|
+
end
|
287
|
+
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should leave the content-md5 alone if provided" do
|
291
|
+
@signed_request.env['CONTENT_MD5'].should == '1B2M2Y8AsgTpgAmY7PhCfg=='
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
it "should sign the request" do
|
296
|
+
@signed_request.env['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
|
297
|
+
end
|
298
|
+
|
299
|
+
it "should authenticate a valid request" do
|
300
|
+
ApiAuth.authentic?(@signed_request, @secret_key).should be_true
|
301
|
+
end
|
302
|
+
|
303
|
+
it "should NOT authenticate a non-valid request" do
|
304
|
+
ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
|
305
|
+
end
|
306
|
+
|
307
|
+
it "should NOT authenticate a mismatched content-md5 when body has changed" do
|
308
|
+
request = ActionController::Request.new(
|
309
|
+
'PATH_INFO' => '/resource.xml',
|
310
|
+
'QUERY_STRING' => 'foo=bar&bar=foo',
|
311
|
+
'REQUEST_METHOD' => 'PUT',
|
312
|
+
'CONTENT_TYPE' => 'text/plain',
|
313
|
+
'HTTP_DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT',
|
314
|
+
'rack.input' => StringIO.new("hello\nworld"))
|
315
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
316
|
+
signed_request.instance_variable_get("@env")["rack.input"] = StringIO.new("goodbye")
|
317
|
+
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
318
|
+
end
|
319
|
+
|
320
|
+
it "should NOT authenticate an expired request" do
|
321
|
+
@request.env['HTTP_DATE'] = 16.minutes.ago.utc.httpdate
|
322
|
+
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
323
|
+
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
324
|
+
end
|
325
|
+
|
326
|
+
it "should retrieve the access_id" do
|
327
|
+
ApiAuth.access_id(@signed_request).should == "1044"
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
331
|
+
|
332
|
+
describe "with Rack::Request" do
|
333
|
+
|
334
|
+
before(:each) do
|
335
|
+
headers = { 'Content-MD5' => "1B2M2Y8AsgTpgAmY7PhCfg==",
|
336
|
+
'Content-Type' => "text/plain",
|
337
|
+
'Date' => Time.now.utc.httpdate }
|
338
|
+
@request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put).merge!(headers))
|
339
|
+
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
340
|
+
end
|
341
|
+
|
342
|
+
it "should return a Rack::Request object after signing it" do
|
343
|
+
ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match("Rack::Request")
|
344
|
+
end
|
345
|
+
|
346
|
+
describe "md5 header" do
|
347
|
+
context "not already provided" do
|
348
|
+
it "should calculate for empty string" do
|
349
|
+
headers = { 'Content-Type' => "text/plain",
|
350
|
+
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
351
|
+
request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put).merge!(headers))
|
352
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
353
|
+
signed_request.env['Content-MD5'].should == Digest::MD5.base64digest('')
|
354
|
+
end
|
355
|
+
|
356
|
+
it "should calculate for real content" do
|
357
|
+
headers = { 'Content-Type' => "text/plain",
|
358
|
+
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
359
|
+
request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put, :input => "hellow\nworld").merge!(headers))
|
360
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
361
|
+
signed_request.env['Content-MD5'].should == Digest::MD5.base64digest("hellow\nworld")
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
it "should leave the content-md5 alone if provided" do
|
366
|
+
@signed_request.env['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
it "should sign the request" do
|
371
|
+
@signed_request.env['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
|
372
|
+
end
|
373
|
+
|
374
|
+
it "should authenticate a valid request" do
|
375
|
+
ApiAuth.authentic?(@signed_request, @secret_key).should be_true
|
376
|
+
end
|
377
|
+
|
378
|
+
it "should NOT authenticate a non-valid request" do
|
379
|
+
ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
|
380
|
+
end
|
381
|
+
|
382
|
+
it "should NOT authenticate a mismatched content-md5 when body has changed" do
|
383
|
+
headers = { 'Content-Type' => "text/plain",
|
384
|
+
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
385
|
+
request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put, :input => "hellow\nworld").merge!(headers))
|
386
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
387
|
+
changed_request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put, :input => "goodbye").merge!(headers))
|
388
|
+
signed_request.env['rack.input'] = changed_request.env['rack.input']
|
389
|
+
signed_request.env['CONTENT_LENGTH'] = changed_request.env['CONTENT_LENGTH']
|
390
|
+
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
391
|
+
end
|
392
|
+
|
393
|
+
it "should NOT authenticate an expired request" do
|
394
|
+
@request.env['Date'] = 16.minutes.ago.utc.httpdate
|
395
|
+
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
396
|
+
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
397
|
+
end
|
398
|
+
|
399
|
+
it "should retrieve the access_id" do
|
400
|
+
ApiAuth.access_id(@signed_request).should == "1044"
|
401
|
+
end
|
402
|
+
|
403
|
+
end
|
404
|
+
|
405
|
+
end
|
406
|
+
|
407
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "ApiAuth::Headers" do
|
4
|
+
|
5
|
+
CANONICAL_STRING = "text/plain,e59ff97941044f85df5297e1c302d260,/resource.xml?foo=bar&bar=foo,Mon, 23 Jan 1984 03:29:56 GMT"
|
6
|
+
|
7
|
+
describe "with Net::HTTP" do
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
@request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
11
|
+
'content-type' => 'text/plain',
|
12
|
+
'content-md5' => 'e59ff97941044f85df5297e1c302d260',
|
13
|
+
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
14
|
+
@headers = ApiAuth::Headers.new(@request)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should generate the proper canonical string" do
|
18
|
+
@headers.canonical_string.should == CANONICAL_STRING
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should set the authorization header" do
|
22
|
+
@headers.sign_header("alpha")
|
23
|
+
@headers.authorization_header.should == "alpha"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should set the DATE header if one is not already present" do
|
27
|
+
@request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
28
|
+
'content-type' => 'text/plain',
|
29
|
+
'content-md5' => 'e59ff97941044f85df5297e1c302d260')
|
30
|
+
ApiAuth.sign!(@request, "some access id", "some secret key")
|
31
|
+
@request['DATE'].should_not be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not set the DATE header just by asking for the canonical_string" do
|
35
|
+
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
36
|
+
'content-type' => 'text/plain',
|
37
|
+
'content-md5' => 'e59ff97941044f85df5297e1c302d260')
|
38
|
+
headers = ApiAuth::Headers.new(request)
|
39
|
+
headers.canonical_string
|
40
|
+
request['DATE'].should be_nil
|
41
|
+
end
|
42
|
+
|
43
|
+
context "md5_mismatch?" do
|
44
|
+
it "is false if no md5 header is present" do
|
45
|
+
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
46
|
+
'content-type' => 'text/plain')
|
47
|
+
headers = ApiAuth::Headers.new(request)
|
48
|
+
headers.md5_mismatch?.should be_false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "with RestClient" do
|
54
|
+
|
55
|
+
before(:each) do
|
56
|
+
headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
|
57
|
+
'Content-Type' => "text/plain",
|
58
|
+
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
59
|
+
@request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
60
|
+
:headers => headers,
|
61
|
+
:method => :put)
|
62
|
+
@headers = ApiAuth::Headers.new(@request)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should generate the proper canonical string" do
|
66
|
+
@headers.canonical_string.should == CANONICAL_STRING
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should set the authorization header" do
|
70
|
+
@headers.sign_header("alpha")
|
71
|
+
@headers.authorization_header.should == "alpha"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should set the DATE header if one is not already present" do
|
75
|
+
headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
|
76
|
+
'Content-Type' => "text/plain" }
|
77
|
+
@request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
78
|
+
:headers => headers,
|
79
|
+
:method => :put)
|
80
|
+
ApiAuth.sign!(@request, "some access id", "some secret key")
|
81
|
+
@request.headers['DATE'].should_not be_nil
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should not set the DATE header just by asking for the canonical_string" do
|
85
|
+
headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
|
86
|
+
'Content-Type' => "text/plain" }
|
87
|
+
request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
88
|
+
:headers => headers,
|
89
|
+
:method => :put)
|
90
|
+
headers = ApiAuth::Headers.new(request)
|
91
|
+
headers.canonical_string
|
92
|
+
request.headers['DATE'].should be_nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "with Curb" do
|
97
|
+
|
98
|
+
before(:each) do
|
99
|
+
headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
|
100
|
+
'Content-Type' => "text/plain",
|
101
|
+
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
102
|
+
@request = Curl::Easy.new("/resource.xml?foo=bar&bar=foo") do |curl|
|
103
|
+
curl.headers = headers
|
104
|
+
end
|
105
|
+
@headers = ApiAuth::Headers.new(@request)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should generate the proper canonical string" do
|
109
|
+
@headers.canonical_string.should == CANONICAL_STRING
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should set the authorization header" do
|
113
|
+
@headers.sign_header("alpha")
|
114
|
+
@headers.authorization_header.should == "alpha"
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should set the DATE header if one is not already present" do
|
118
|
+
headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
|
119
|
+
'Content-Type' => "text/plain" }
|
120
|
+
@request = Curl::Easy.new("/resource.xml?foo=bar&bar=foo") do |curl|
|
121
|
+
curl.headers = headers
|
122
|
+
end
|
123
|
+
ApiAuth.sign!(@request, "some access id", "some secret key")
|
124
|
+
@request.headers['DATE'].should_not be_nil
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should not set the DATE header just by asking for the canonical_string" do
|
128
|
+
headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
|
129
|
+
'Content-Type' => "text/plain" }
|
130
|
+
request = Curl::Easy.new("/resource.xml?foo=bar&bar=foo") do |curl|
|
131
|
+
curl.headers = headers
|
132
|
+
end
|
133
|
+
headers = ApiAuth::Headers.new(request)
|
134
|
+
headers.canonical_string
|
135
|
+
request.headers['DATE'].should be_nil
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "with ActionController" do
|
140
|
+
|
141
|
+
before(:each) do
|
142
|
+
@request = ActionController::Request.new(
|
143
|
+
'PATH_INFO' => '/resource.xml',
|
144
|
+
'QUERY_STRING' => 'foo=bar&bar=foo',
|
145
|
+
'REQUEST_METHOD' => 'PUT',
|
146
|
+
'CONTENT_MD5' => 'e59ff97941044f85df5297e1c302d260',
|
147
|
+
'CONTENT_TYPE' => 'text/plain',
|
148
|
+
'HTTP_DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT')
|
149
|
+
@headers = ApiAuth::Headers.new(@request)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should generate the proper canonical string" do
|
153
|
+
@headers.canonical_string.should == CANONICAL_STRING
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should set the authorization header" do
|
157
|
+
@headers.sign_header("alpha")
|
158
|
+
@headers.authorization_header.should == "alpha"
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should set the DATE header if one is not already present" do
|
162
|
+
@request = ActionController::Request.new(
|
163
|
+
'PATH_INFO' => '/resource.xml',
|
164
|
+
'QUERY_STRING' => 'foo=bar&bar=foo',
|
165
|
+
'REQUEST_METHOD' => 'PUT',
|
166
|
+
'CONTENT_MD5' => 'e59ff97941044f85df5297e1c302d260',
|
167
|
+
'CONTENT_TYPE' => 'text/plain')
|
168
|
+
ApiAuth.sign!(@request, "some access id", "some secret key")
|
169
|
+
@request.headers['DATE'].should_not be_nil
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should not set the DATE header just by asking for the canonical_string" do
|
173
|
+
request = ActionController::Request.new(
|
174
|
+
'PATH_INFO' => '/resource.xml',
|
175
|
+
'QUERY_STRING' => 'foo=bar&bar=foo',
|
176
|
+
'REQUEST_METHOD' => 'PUT',
|
177
|
+
'CONTENT_MD5' => 'e59ff97941044f85df5297e1c302d260',
|
178
|
+
'CONTENT_TYPE' => 'text/plain')
|
179
|
+
headers = ApiAuth::Headers.new(request)
|
180
|
+
headers.canonical_string
|
181
|
+
request.headers['DATE'].should be_nil
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "with Rack::Request" do
|
186
|
+
|
187
|
+
before(:each) do
|
188
|
+
headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
|
189
|
+
'Content-Type' => "text/plain",
|
190
|
+
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT"
|
191
|
+
}
|
192
|
+
@request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put).merge!(headers))
|
193
|
+
@headers = ApiAuth::Headers.new(@request)
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should generate the proper canonical string" do
|
197
|
+
@headers.canonical_string.should == CANONICAL_STRING
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should set the authorization header" do
|
201
|
+
@headers.sign_header("alpha")
|
202
|
+
@headers.authorization_header.should == "alpha"
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should set the DATE header if one is not already present" do
|
206
|
+
headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
|
207
|
+
'Content-Type' => "text/plain" }
|
208
|
+
@request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put).merge!(headers))
|
209
|
+
ApiAuth.sign!(@request, "some access id", "some secret key")
|
210
|
+
@request.env['DATE'].should_not be_nil
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should not set the DATE header just by asking for the canonical_string" do
|
214
|
+
headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
|
215
|
+
'Content-Type' => "text/plain" }
|
216
|
+
request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put).merge!(headers))
|
217
|
+
headers = ApiAuth::Headers.new(request)
|
218
|
+
headers.canonical_string
|
219
|
+
request.env['DATE'].should be_nil
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "ApiAuth::Helpers" do
|
4
|
+
|
5
|
+
it "should strip the new line character on a Base64 encoding" do
|
6
|
+
ApiAuth.b64_encode("some string").should_not match(/\n/)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should properly upcase a hash's keys" do
|
10
|
+
hsh = { "JoE" => "rOOLz" }
|
11
|
+
ApiAuth.capitalize_keys(hsh)["JOE"].should == "rOOLz"
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|