api-auth 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +17 -0
- data/Appraisals +29 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +42 -11
- data/LICENSE.txt +1 -1
- data/README.md +15 -13
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/api_auth.gemspec +5 -3
- data/gemfiles/rails_23.gemfile +7 -0
- data/gemfiles/rails_23.gemfile.lock +57 -0
- data/gemfiles/rails_30.gemfile +7 -0
- data/gemfiles/rails_30.gemfile.lock +79 -0
- data/gemfiles/rails_31.gemfile +9 -0
- data/gemfiles/rails_31.gemfile.lock +85 -0
- data/gemfiles/rails_32.gemfile +9 -0
- data/gemfiles/rails_32.gemfile.lock +84 -0
- data/gemfiles/rails_4.gemfile +9 -0
- data/gemfiles/rails_4.gemfile.lock +81 -0
- data/lib/api_auth.rb +1 -0
- data/lib/api_auth/base.rb +1 -1
- data/lib/api_auth/headers.rb +4 -0
- data/lib/api_auth/helpers.rb +18 -5
- data/lib/api_auth/request_drivers/action_controller.rb +2 -2
- data/lib/api_auth/request_drivers/httpi.rb +80 -0
- data/lib/api_auth/request_drivers/net_http.rb +3 -1
- data/lib/api_auth/request_drivers/rack.rb +1 -1
- data/lib/api_auth/request_drivers/rest_client.rb +4 -4
- data/spec/api_auth_spec.rb +97 -22
- data/spec/headers_spec.rb +64 -3
- data/spec/railtie_spec.rb +51 -36
- data/spec/spec_helper.rb +2 -1
- metadata +55 -9
data/spec/headers_spec.rb
CHANGED
@@ -91,6 +91,18 @@ describe "ApiAuth::Headers" do
|
|
91
91
|
headers.canonical_string
|
92
92
|
request.headers['DATE'].should be_nil
|
93
93
|
end
|
94
|
+
|
95
|
+
it "doesn't mess up symbol based headers" do
|
96
|
+
headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
|
97
|
+
:content_type => "text/plain",
|
98
|
+
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
99
|
+
@request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
100
|
+
:headers => headers,
|
101
|
+
:method => :put)
|
102
|
+
@headers = ApiAuth::Headers.new(@request)
|
103
|
+
ApiAuth.sign!(@request, "some access id", "some secret key")
|
104
|
+
@request.processed_headers.should have_key('Content-Type')
|
105
|
+
end
|
94
106
|
end
|
95
107
|
|
96
108
|
describe "with Curb" do
|
@@ -138,8 +150,10 @@ describe "ApiAuth::Headers" do
|
|
138
150
|
|
139
151
|
describe "with ActionController" do
|
140
152
|
|
153
|
+
let(:request_klass){ ActionDispatch::Request rescue ActionController::Request }
|
154
|
+
|
141
155
|
before(:each) do
|
142
|
-
@request =
|
156
|
+
@request = request_klass.new(
|
143
157
|
'PATH_INFO' => '/resource.xml',
|
144
158
|
'QUERY_STRING' => 'foo=bar&bar=foo',
|
145
159
|
'REQUEST_METHOD' => 'PUT',
|
@@ -159,7 +173,7 @@ describe "ApiAuth::Headers" do
|
|
159
173
|
end
|
160
174
|
|
161
175
|
it "should set the DATE header if one is not already present" do
|
162
|
-
@request =
|
176
|
+
@request = request_klass.new(
|
163
177
|
'PATH_INFO' => '/resource.xml',
|
164
178
|
'QUERY_STRING' => 'foo=bar&bar=foo',
|
165
179
|
'REQUEST_METHOD' => 'PUT',
|
@@ -170,7 +184,7 @@ describe "ApiAuth::Headers" do
|
|
170
184
|
end
|
171
185
|
|
172
186
|
it "should not set the DATE header just by asking for the canonical_string" do
|
173
|
-
request =
|
187
|
+
request = request_klass.new(
|
174
188
|
'PATH_INFO' => '/resource.xml',
|
175
189
|
'QUERY_STRING' => 'foo=bar&bar=foo',
|
176
190
|
'REQUEST_METHOD' => 'PUT',
|
@@ -220,4 +234,51 @@ describe "ApiAuth::Headers" do
|
|
220
234
|
end
|
221
235
|
end
|
222
236
|
|
237
|
+
describe "with HTTPI" do
|
238
|
+
before(:each) do
|
239
|
+
@request = HTTPI::Request.new("http://localhost/resource.xml?foo=bar&bar=foo")
|
240
|
+
@request.headers.merge!({
|
241
|
+
'content-type' => 'text/plain',
|
242
|
+
'content-md5' => 'e59ff97941044f85df5297e1c302d260',
|
243
|
+
'date' => "Mon, 23 Jan 1984 03:29:56 GMT"
|
244
|
+
})
|
245
|
+
@headers = ApiAuth::Headers.new(@request)
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should generate the proper canonical string" do
|
249
|
+
@headers.canonical_string.should == CANONICAL_STRING
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should set the authorization header" do
|
253
|
+
@headers.sign_header("alpha")
|
254
|
+
@headers.authorization_header.should == "alpha"
|
255
|
+
end
|
256
|
+
|
257
|
+
it "should set the DATE header if one is not already present" do
|
258
|
+
@request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
259
|
+
'content-type' => 'text/plain',
|
260
|
+
'content-md5' => 'e59ff97941044f85df5297e1c302d260')
|
261
|
+
ApiAuth.sign!(@request, "some access id", "some secret key")
|
262
|
+
@request['DATE'].should_not be_nil
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should not set the DATE header just by asking for the canonical_string" do
|
266
|
+
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
267
|
+
'content-type' => 'text/plain',
|
268
|
+
'content-md5' => 'e59ff97941044f85df5297e1c302d260')
|
269
|
+
headers = ApiAuth::Headers.new(request)
|
270
|
+
headers.canonical_string
|
271
|
+
request['DATE'].should be_nil
|
272
|
+
end
|
273
|
+
|
274
|
+
context "md5_mismatch?" do
|
275
|
+
it "is false if no md5 header is present" do
|
276
|
+
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
277
|
+
'content-type' => 'text/plain')
|
278
|
+
headers = ApiAuth::Headers.new(request)
|
279
|
+
headers.md5_mismatch?.should be_false
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
223
284
|
end
|
data/spec/railtie_spec.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
3
|
describe "Rails integration" do
|
4
|
-
|
4
|
+
|
5
5
|
API_KEY_STORE = { "1044" => "l16imAXie1sRMcJODpOG7UwC1VyoqvO13jejkfpKWX4Z09W8DC9IrU23DvCwMry7pgSFW6c5S1GIfV0OY6F/vUA==" }
|
6
|
-
|
6
|
+
|
7
7
|
describe "Rails controller integration" do
|
8
|
-
|
8
|
+
|
9
9
|
class ApplicationController < ActionController::Base
|
10
|
-
|
10
|
+
|
11
11
|
private
|
12
|
-
|
12
|
+
|
13
13
|
def require_api_auth
|
14
14
|
if (access_id = get_api_access_id_from_request)
|
15
15
|
return true if api_authenticated?(API_KEY_STORE[access_id])
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
respond_to do |format|
|
19
19
|
format.xml { render :xml => "You are unauthorized to perform this action.", :status => 401 }
|
20
20
|
format.json { render :json => "You are unauthorized to perform this action.", :status => 401 }
|
@@ -23,82 +23,97 @@ describe "Rails integration" do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
class TestController < ApplicationController
|
28
28
|
before_filter :require_api_auth, :only => [:index]
|
29
|
-
|
29
|
+
|
30
|
+
if defined?(ActionDispatch)
|
31
|
+
def self._routes
|
32
|
+
ActionDispatch::Routing::RouteSet.new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
30
36
|
def index
|
31
37
|
render :text => "OK"
|
32
38
|
end
|
33
|
-
|
39
|
+
|
34
40
|
def public
|
35
41
|
render :text => "OK"
|
36
42
|
end
|
37
43
|
|
38
44
|
def rescue_action(e); raise(e); end
|
39
45
|
end
|
40
|
-
|
41
|
-
|
46
|
+
|
47
|
+
unless defined?(ActionDispatch)
|
48
|
+
ActionController::Routing::Routes.draw {|map| map.resources :test }
|
49
|
+
end
|
50
|
+
|
51
|
+
def generated_response(request, action = :index)
|
52
|
+
if defined?(ActionDispatch)
|
53
|
+
TestController.action(action).call(request.env).last
|
54
|
+
else
|
55
|
+
request.action = action.to_s
|
56
|
+
request.path = "/#{action.to_s}"
|
57
|
+
TestController.new.process(request, ActionController::TestResponse.new)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
42
61
|
it "should permit a request with properly signed headers" do
|
43
62
|
request = ActionController::TestRequest.new
|
44
63
|
request.env['DATE'] = Time.now.utc.httpdate
|
45
|
-
request.action = 'index'
|
46
|
-
request.path = "/index"
|
47
64
|
ApiAuth.sign!(request, "1044", API_KEY_STORE["1044"])
|
48
|
-
|
65
|
+
response = generated_response(request, :index)
|
66
|
+
response.code.should == "200"
|
49
67
|
end
|
50
|
-
|
68
|
+
|
51
69
|
it "should forbid a request with properly signed headers but timestamp > 15 minutes" do
|
52
70
|
request = ActionController::TestRequest.new
|
53
71
|
request.env['DATE'] = "Mon, 23 Jan 1984 03:29:56 GMT"
|
54
|
-
request.action = 'index'
|
55
|
-
request.path = "/index"
|
56
72
|
ApiAuth.sign!(request, "1044", API_KEY_STORE["1044"])
|
57
|
-
|
73
|
+
response = generated_response(request, :index)
|
74
|
+
response.code.should == "401"
|
58
75
|
end
|
59
|
-
|
76
|
+
|
60
77
|
it "should insert a DATE header in the request when one hasn't been specified" do
|
61
78
|
request = ActionController::TestRequest.new
|
62
|
-
request.action = 'index'
|
63
|
-
request.path = "/index"
|
64
79
|
ApiAuth.sign!(request, "1044", API_KEY_STORE["1044"])
|
65
80
|
request.headers['DATE'].should_not be_nil
|
66
81
|
end
|
67
82
|
|
68
83
|
it "should forbid an unsigned request to a protected controller action" do
|
69
84
|
request = ActionController::TestRequest.new
|
70
|
-
|
71
|
-
|
85
|
+
response = generated_response(request, :index)
|
86
|
+
response.code.should == "401"
|
72
87
|
end
|
73
88
|
|
74
89
|
it "should forbid a request with a bogus signature" do
|
75
90
|
request = ActionController::TestRequest.new
|
76
|
-
request.action = 'index'
|
77
91
|
request.env['Authorization'] = "APIAuth bogus:bogus"
|
78
|
-
|
92
|
+
response = generated_response(request, :index)
|
93
|
+
response.code.should == "401"
|
79
94
|
end
|
80
|
-
|
95
|
+
|
81
96
|
it "should allow non-protected controller actions to function as before" do
|
82
97
|
request = ActionController::TestRequest.new
|
83
|
-
|
84
|
-
|
85
|
-
TestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
|
98
|
+
response = generated_response(request, :public)
|
99
|
+
response.code.should == "200"
|
86
100
|
end
|
87
|
-
|
101
|
+
|
88
102
|
end
|
89
|
-
|
103
|
+
|
90
104
|
describe "Rails ActiveResource integration" do
|
91
|
-
|
105
|
+
|
92
106
|
class TestResource < ActiveResource::Base
|
93
107
|
with_api_auth "1044", API_KEY_STORE["1044"]
|
94
108
|
self.site = "http://localhost/"
|
109
|
+
self.format = :xml
|
95
110
|
end
|
96
|
-
|
111
|
+
|
97
112
|
it "should send signed requests automagically" do
|
98
113
|
timestamp = Time.parse("Mon, 23 Jan 1984 03:29:56 GMT")
|
99
114
|
Time.should_receive(:now).at_least(1).times.and_return(timestamp)
|
100
115
|
ActiveResource::HttpMock.respond_to do |mock|
|
101
|
-
mock.get "/test_resources/1.xml",
|
116
|
+
mock.get "/test_resources/1.xml",
|
102
117
|
{
|
103
118
|
'Authorization' => 'APIAuth 1044:IbTx7VzSOGU55HNbV4y2jZDnVis=',
|
104
119
|
'Accept' => 'application/xml',
|
@@ -108,7 +123,7 @@ describe "Rails integration" do
|
|
108
123
|
end
|
109
124
|
TestResource.find(1)
|
110
125
|
end
|
111
|
-
|
126
|
+
|
112
127
|
end
|
113
|
-
|
128
|
+
|
114
129
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,11 +5,12 @@ require 'api_auth'
|
|
5
5
|
require 'amatch'
|
6
6
|
require 'rest_client'
|
7
7
|
require 'curb'
|
8
|
+
require 'httpi'
|
8
9
|
|
9
10
|
require 'active_support'
|
10
11
|
require 'active_support/test_case'
|
11
12
|
require 'action_controller'
|
12
|
-
require 'action_controller/
|
13
|
+
require 'action_controller/test_case'
|
13
14
|
require 'active_resource'
|
14
15
|
require 'active_resource/http_mock'
|
15
16
|
|
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.
|
4
|
+
version: 1.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,24 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-05-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: appraisal
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
14
30
|
- !ruby/object:Gem::Dependency
|
15
31
|
name: rake
|
16
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,7 +82,7 @@ dependencies:
|
|
66
82
|
requirements:
|
67
83
|
- - ~>
|
68
84
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
85
|
+
version: 3.0.0
|
70
86
|
type: :development
|
71
87
|
prerelease: false
|
72
88
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -74,7 +90,7 @@ dependencies:
|
|
74
90
|
requirements:
|
75
91
|
- - ~>
|
76
92
|
- !ruby/object:Gem::Version
|
77
|
-
version:
|
93
|
+
version: 3.0.0
|
78
94
|
- !ruby/object:Gem::Dependency
|
79
95
|
name: activesupport
|
80
96
|
requirement: !ruby/object:Gem::Requirement
|
@@ -82,7 +98,7 @@ dependencies:
|
|
82
98
|
requirements:
|
83
99
|
- - ~>
|
84
100
|
- !ruby/object:Gem::Version
|
85
|
-
version:
|
101
|
+
version: 3.0.0
|
86
102
|
type: :development
|
87
103
|
prerelease: false
|
88
104
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -90,7 +106,7 @@ dependencies:
|
|
90
106
|
requirements:
|
91
107
|
- - ~>
|
92
108
|
- !ruby/object:Gem::Version
|
93
|
-
version:
|
109
|
+
version: 3.0.0
|
94
110
|
- !ruby/object:Gem::Dependency
|
95
111
|
name: activeresource
|
96
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -98,7 +114,7 @@ dependencies:
|
|
98
114
|
requirements:
|
99
115
|
- - ~>
|
100
116
|
- !ruby/object:Gem::Version
|
101
|
-
version:
|
117
|
+
version: 3.0.0
|
102
118
|
type: :development
|
103
119
|
prerelease: false
|
104
120
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -106,7 +122,7 @@ dependencies:
|
|
106
122
|
requirements:
|
107
123
|
- - ~>
|
108
124
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
125
|
+
version: 3.0.0
|
110
126
|
- !ruby/object:Gem::Dependency
|
111
127
|
name: rest-client
|
112
128
|
requirement: !ruby/object:Gem::Requirement
|
@@ -139,6 +155,22 @@ dependencies:
|
|
139
155
|
- - ~>
|
140
156
|
- !ruby/object:Gem::Version
|
141
157
|
version: 0.8.1
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: httpi
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
142
174
|
description: Full HMAC auth implementation for use in your gems and Rails apps.
|
143
175
|
email: mauricio@edge14.com
|
144
176
|
executables: []
|
@@ -148,6 +180,9 @@ files:
|
|
148
180
|
- .document
|
149
181
|
- .gitignore
|
150
182
|
- .rspec
|
183
|
+
- .travis.yml
|
184
|
+
- Appraisals
|
185
|
+
- CHANGELOG.md
|
151
186
|
- Gemfile
|
152
187
|
- Gemfile.lock
|
153
188
|
- LICENSE.txt
|
@@ -155,6 +190,16 @@ files:
|
|
155
190
|
- Rakefile
|
156
191
|
- VERSION
|
157
192
|
- api_auth.gemspec
|
193
|
+
- gemfiles/rails_23.gemfile
|
194
|
+
- gemfiles/rails_23.gemfile.lock
|
195
|
+
- gemfiles/rails_30.gemfile
|
196
|
+
- gemfiles/rails_30.gemfile.lock
|
197
|
+
- gemfiles/rails_31.gemfile
|
198
|
+
- gemfiles/rails_31.gemfile.lock
|
199
|
+
- gemfiles/rails_32.gemfile
|
200
|
+
- gemfiles/rails_32.gemfile.lock
|
201
|
+
- gemfiles/rails_4.gemfile
|
202
|
+
- gemfiles/rails_4.gemfile.lock
|
158
203
|
- lib/api-auth.rb
|
159
204
|
- lib/api_auth.rb
|
160
205
|
- lib/api_auth/base.rb
|
@@ -165,6 +210,7 @@ files:
|
|
165
210
|
- lib/api_auth/request_drivers/action_controller.rb
|
166
211
|
- lib/api_auth/request_drivers/action_dispatch.rb
|
167
212
|
- lib/api_auth/request_drivers/curb.rb
|
213
|
+
- lib/api_auth/request_drivers/httpi.rb
|
168
214
|
- lib/api_auth/request_drivers/net_http.rb
|
169
215
|
- lib/api_auth/request_drivers/rack.rb
|
170
216
|
- lib/api_auth/request_drivers/rest_client.rb
|
@@ -195,7 +241,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
241
|
version: '0'
|
196
242
|
requirements: []
|
197
243
|
rubyforge_project:
|
198
|
-
rubygems_version: 1.8.
|
244
|
+
rubygems_version: 1.8.23.2
|
199
245
|
signing_key:
|
200
246
|
specification_version: 3
|
201
247
|
summary: Simple HMAC authentication for your APIs
|