api-auth 1.0.1 → 1.0.2

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/.gitignore CHANGED
@@ -1,3 +1,5 @@
1
+ .rvmrc
2
+
1
3
  # rcov generated
2
4
  coverage
3
5
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- api-auth (1.0.1)
4
+ api-auth (1.0.2)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.0.2
data/lib/api_auth.rb CHANGED
@@ -9,6 +9,7 @@ require 'api_auth/request_drivers/curb'
9
9
  require 'api_auth/request_drivers/rest_client'
10
10
  require 'api_auth/request_drivers/action_controller'
11
11
  require 'api_auth/request_drivers/action_dispatch'
12
+ require 'api_auth/request_drivers/rack'
12
13
 
13
14
  require 'api_auth/headers'
14
15
  require 'api_auth/base'
data/lib/api_auth/base.rb CHANGED
@@ -60,7 +60,7 @@ module ApiAuth
60
60
  def request_too_old?(request)
61
61
  headers = Headers.new(request)
62
62
  # 900 seconds is 15 minutes
63
- Time.parse(headers.timestamp).utc < (Time.current.utc - 900)
63
+ Time.parse(headers.timestamp).utc < (Time.now.utc - 900)
64
64
  end
65
65
 
66
66
  def md5_mismatch?(request)
@@ -25,6 +25,8 @@ module ApiAuth
25
25
  end
26
26
  when /ActionDispatch::Request/
27
27
  @request = ActionDispatchRequest.new(request)
28
+ when /Rack::Request/
29
+ @request = RackRequest.new(request)
28
30
  else
29
31
  raise UnknownHTTPRequest, "#{request.class.to_s} is not yet supported."
30
32
  end
@@ -0,0 +1,85 @@
1
+ module ApiAuth
2
+
3
+ module RequestDrivers # :nodoc:
4
+
5
+ class RackRequest # :nodoc:
6
+
7
+ include ApiAuth::Helpers
8
+
9
+ def initialize(request)
10
+ @request = request
11
+ @headers = fetch_headers
12
+ true
13
+ end
14
+
15
+ def set_auth_header(header)
16
+ @request.env.merge!({ "Authorization" => header })
17
+ @headers = fetch_headers
18
+ @request
19
+ end
20
+
21
+ def calculated_md5
22
+ if @request.body
23
+ body = @request.body.read
24
+ else
25
+ body = ''
26
+ end
27
+ Digest::MD5.base64digest(body)
28
+ end
29
+
30
+ def populate_content_md5
31
+ if ['POST', 'PUT'].include?(@request.request_method)
32
+ @request.env["Content-MD5"] = calculated_md5
33
+ end
34
+ end
35
+
36
+ def md5_mismatch?
37
+ if ['POST', 'PUT'].include?(@request.request_method)
38
+ calculated_md5 != content_md5
39
+ else
40
+ false
41
+ end
42
+ end
43
+
44
+ def fetch_headers
45
+ capitalize_keys @request.env
46
+ end
47
+
48
+ def content_type
49
+ value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
50
+ value.nil? ? "" : value
51
+ end
52
+
53
+ def content_md5
54
+ value = find_header(%w(CONTENT-MD5 CONTENT_MD5))
55
+ value.nil? ? "" : value
56
+ end
57
+
58
+ def request_uri
59
+ @request.url
60
+ end
61
+
62
+ def set_date
63
+ @request.env.merge!({ "DATE" => Time.now.utc.httpdate })
64
+ end
65
+
66
+ def timestamp
67
+ value = find_header(%w(DATE HTTP_DATE))
68
+ value.nil? ? "" : value
69
+ end
70
+
71
+ def authorization_header
72
+ find_header %w(Authorization AUTHORIZATION HTTP_AUTHORIZATION)
73
+ end
74
+
75
+ private
76
+
77
+ def find_header(keys)
78
+ keys.map {|key| @headers[key] }.compact.first
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -329,6 +329,79 @@ describe "ApiAuth" do
329
329
 
330
330
  end
331
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
+
332
405
  end
333
406
 
334
407
  end
data/spec/headers_spec.rb CHANGED
@@ -182,4 +182,42 @@ describe "ApiAuth::Headers" do
182
182
  end
183
183
  end
184
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 == "text/plain,e59ff97941044f85df5297e1c302d260,http://example.org/resource.xml?foo=bar&bar=foo,Mon, 23 Jan 1984 03:29:56 GMT"
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
+
185
223
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-30 00:00:00.000000000 Z
12
+ date: 2013-02-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -166,6 +166,7 @@ files:
166
166
  - lib/api_auth/request_drivers/action_dispatch.rb
167
167
  - lib/api_auth/request_drivers/curb.rb
168
168
  - lib/api_auth/request_drivers/net_http.rb
169
+ - lib/api_auth/request_drivers/rack.rb
169
170
  - lib/api_auth/request_drivers/rest_client.rb
170
171
  - spec/api_auth_spec.rb
171
172
  - spec/application_helper.rb
@@ -188,7 +189,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
188
189
  version: '0'
189
190
  segments:
190
191
  - 0
191
- hash: -1454681296772684747
192
+ hash: 3429846398106267766
192
193
  required_rubygems_version: !ruby/object:Gem::Requirement
193
194
  none: false
194
195
  requirements:
@@ -197,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
198
  version: '0'
198
199
  segments:
199
200
  - 0
200
- hash: -1454681296772684747
201
+ hash: 3429846398106267766
201
202
  requirements: []
202
203
  rubyforge_project:
203
204
  rubygems_version: 1.8.24