api-auth 1.3.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -2
  3. data/.travis.yml +4 -0
  4. data/Appraisals +6 -0
  5. data/CHANGELOG.md +36 -0
  6. data/Gemfile.lock +77 -44
  7. data/README.md +15 -8
  8. data/VERSION +1 -1
  9. data/api_auth.gemspec +4 -4
  10. data/gemfiles/rails_23.gemfile +1 -1
  11. data/gemfiles/rails_23.gemfile.lock +19 -11
  12. data/gemfiles/rails_30.gemfile +1 -1
  13. data/gemfiles/rails_30.gemfile.lock +19 -11
  14. data/gemfiles/rails_31.gemfile +1 -1
  15. data/gemfiles/rails_31.gemfile.lock +19 -11
  16. data/gemfiles/rails_32.gemfile +1 -1
  17. data/gemfiles/rails_32.gemfile.lock +19 -11
  18. data/gemfiles/rails_4.gemfile +1 -1
  19. data/gemfiles/rails_4.gemfile.lock +19 -11
  20. data/gemfiles/rails_41.gemfile +1 -1
  21. data/gemfiles/rails_41.gemfile.lock +19 -11
  22. data/gemfiles/rails_42.gemfile +9 -0
  23. data/gemfiles/rails_42.gemfile.lock +115 -0
  24. data/lib/api_auth/base.rb +37 -23
  25. data/lib/api_auth/headers.rb +23 -3
  26. data/lib/api_auth/request_drivers/action_controller.rb +4 -0
  27. data/lib/api_auth/request_drivers/curb.rb +4 -0
  28. data/lib/api_auth/request_drivers/faraday.rb +4 -0
  29. data/lib/api_auth/request_drivers/httpi.rb +5 -1
  30. data/lib/api_auth/request_drivers/net_http.rb +4 -0
  31. data/lib/api_auth/request_drivers/rack.rb +5 -1
  32. data/lib/api_auth/request_drivers/rest_client.rb +4 -0
  33. data/spec/api_auth_spec.rb +112 -628
  34. data/spec/headers_spec.rb +132 -289
  35. data/spec/helpers_spec.rb +2 -2
  36. data/spec/railtie_spec.rb +13 -8
  37. data/spec/request_drivers/action_controller_spec.rb +218 -0
  38. data/spec/request_drivers/action_dispatch_spec.rb +219 -0
  39. data/spec/request_drivers/curb_spec.rb +89 -0
  40. data/spec/request_drivers/faraday_spec.rb +243 -0
  41. data/spec/request_drivers/httpi_spec.rb +147 -0
  42. data/spec/request_drivers/net_http_spec.rb +185 -0
  43. data/spec/request_drivers/rack_spec.rb +288 -0
  44. data/spec/request_drivers/rest_client_spec.rb +311 -0
  45. metadata +44 -19
  46. data/spec/application_helper.rb +0 -2
  47. data/spec/test_helper.rb +0 -2
@@ -3,12 +3,12 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
  describe "ApiAuth::Helpers" do
4
4
 
5
5
  it "should strip the new line character on a Base64 encoding" do
6
- ApiAuth.b64_encode("some string").should_not match(/\n/)
6
+ expect(ApiAuth.b64_encode("some string")).not_to match(/\n/)
7
7
  end
8
8
 
9
9
  it "should properly upcase a hash's keys" do
10
10
  hsh = { "JoE" => "rOOLz" }
11
- ApiAuth.capitalize_keys(hsh)["JOE"].should == "rOOLz"
11
+ expect(ApiAuth.capitalize_keys(hsh)["JOE"]).to eq("rOOLz")
12
12
  end
13
13
 
14
14
  end
@@ -50,7 +50,12 @@ describe "Rails integration" do
50
50
 
51
51
  def generated_response(request, action = :index)
52
52
  if defined?(ActionDispatch)
53
- TestController.action(action).call(request.env).last
53
+ response = ActionDispatch::TestResponse.new
54
+ controller = TestController.new
55
+ controller.request = request
56
+ controller.response = response
57
+ controller.process(action)
58
+ response
54
59
  else
55
60
  request.action = action.to_s
56
61
  request.path = "/#{action.to_s}"
@@ -63,7 +68,7 @@ describe "Rails integration" do
63
68
  request.env['DATE'] = Time.now.utc.httpdate
64
69
  ApiAuth.sign!(request, "1044", API_KEY_STORE["1044"])
65
70
  response = generated_response(request, :index)
66
- response.code.should == "200"
71
+ expect(response.code).to eq("200")
67
72
  end
68
73
 
69
74
  it "should forbid a request with properly signed headers but timestamp > 15 minutes" do
@@ -71,32 +76,32 @@ describe "Rails integration" do
71
76
  request.env['DATE'] = "Mon, 23 Jan 1984 03:29:56 GMT"
72
77
  ApiAuth.sign!(request, "1044", API_KEY_STORE["1044"])
73
78
  response = generated_response(request, :index)
74
- response.code.should == "401"
79
+ expect(response.code).to eq("401")
75
80
  end
76
81
 
77
82
  it "should insert a DATE header in the request when one hasn't been specified" do
78
83
  request = ActionController::TestRequest.new
79
84
  ApiAuth.sign!(request, "1044", API_KEY_STORE["1044"])
80
- request.headers['DATE'].should_not be_nil
85
+ expect(request.headers['DATE']).not_to be_nil
81
86
  end
82
87
 
83
88
  it "should forbid an unsigned request to a protected controller action" do
84
89
  request = ActionController::TestRequest.new
85
90
  response = generated_response(request, :index)
86
- response.code.should == "401"
91
+ expect(response.code).to eq("401")
87
92
  end
88
93
 
89
94
  it "should forbid a request with a bogus signature" do
90
95
  request = ActionController::TestRequest.new
91
96
  request.env['Authorization'] = "APIAuth bogus:bogus"
92
97
  response = generated_response(request, :index)
93
- response.code.should == "401"
98
+ expect(response.code).to eq("401")
94
99
  end
95
100
 
96
101
  it "should allow non-protected controller actions to function as before" do
97
102
  request = ActionController::TestRequest.new
98
103
  response = generated_response(request, :public)
99
- response.code.should == "200"
104
+ expect(response.code).to eq("200")
100
105
  end
101
106
 
102
107
  end
@@ -111,7 +116,7 @@ describe "Rails integration" do
111
116
 
112
117
  it "should send signed requests automagically" do
113
118
  timestamp = Time.parse("Mon, 23 Jan 1984 03:29:56 GMT")
114
- Time.should_receive(:now).at_least(1).times.and_return(timestamp)
119
+ expect(Time).to receive(:now).at_least(1).times.and_return(timestamp)
115
120
  ActiveResource::HttpMock.respond_to do |mock|
116
121
  mock.get "/test_resources/1.xml",
117
122
  {
@@ -0,0 +1,218 @@
1
+ require 'spec_helper'
2
+
3
+ if defined?(ActionController::Request)
4
+
5
+ describe ApiAuth::RequestDrivers::ActionControllerRequest do
6
+
7
+ let(:timestamp){ Time.now.utc.httpdate }
8
+
9
+ let(:request) do
10
+ ActionController::Request.new(
11
+ 'AUTHORIZATION' => 'APIAuth 1044:12345',
12
+ 'PATH_INFO' => '/resource.xml',
13
+ 'QUERY_STRING' => 'foo=bar&bar=foo',
14
+ 'REQUEST_METHOD' => 'PUT',
15
+ 'CONTENT_MD5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
16
+ 'CONTENT_TYPE' => 'text/plain',
17
+ 'CONTENT_LENGTH' => '11',
18
+ 'HTTP_DATE' => timestamp,
19
+ 'rack.input' => StringIO.new("hello\nworld")
20
+ )
21
+ end
22
+
23
+ subject(:driven_request){ ApiAuth::RequestDrivers::ActionControllerRequest.new(request) }
24
+
25
+ describe "getting headers correctly" do
26
+ it "gets the content_type" do
27
+ expect(driven_request.content_type).to eq('text/plain')
28
+ end
29
+
30
+ it "gets the content_md5" do
31
+ expect(driven_request.content_md5).to eq('1B2M2Y8AsgTpgAmY7PhCfg==')
32
+ end
33
+
34
+ it "gets the request_uri" do
35
+ expect(driven_request.request_uri).to eq('/resource.xml?foo=bar&bar=foo')
36
+ end
37
+
38
+ it "gets the timestamp" do
39
+ expect(driven_request.timestamp).to eq(timestamp)
40
+ end
41
+
42
+ it "gets the authorization_header" do
43
+ expect(driven_request.authorization_header).to eq('APIAuth 1044:12345')
44
+ end
45
+
46
+ describe "#calculated_md5" do
47
+ it "calculates md5 from the body" do
48
+ expect(driven_request.calculated_md5).to eq('kZXQvrKoieG+Be1rsZVINw==')
49
+ end
50
+
51
+ it "treats no body as empty string" do
52
+ request.env['rack.input'] = StringIO.new
53
+ request.env['CONTENT_LENGTH'] = 0
54
+ expect(driven_request.calculated_md5).to eq('1B2M2Y8AsgTpgAmY7PhCfg==')
55
+ end
56
+ end
57
+
58
+ describe "http_method" do
59
+ context "when put request" do
60
+ let(:request) do
61
+ ActionController::Request.new('REQUEST_METHOD' => 'PUT')
62
+ end
63
+
64
+ it "returns upcased put" do
65
+ expect(driven_request.http_method).to eq('PUT')
66
+ end
67
+ end
68
+
69
+ context "when get request" do
70
+ let(:request) do
71
+ ActionController::Request.new('REQUEST_METHOD' => 'GET')
72
+ end
73
+
74
+ it "returns upcased get" do
75
+ expect(driven_request.http_method).to eq('GET')
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ describe "setting headers correctly" do
82
+ let(:request) do
83
+ ActionController::Request.new(
84
+ 'PATH_INFO' => '/resource.xml',
85
+ 'QUERY_STRING' => 'foo=bar&bar=foo',
86
+ 'REQUEST_METHOD' => 'PUT',
87
+ 'CONTENT_TYPE' => 'text/plain',
88
+ 'CONTENT_LENGTH' => '11',
89
+ 'rack.input' => StringIO.new("hello\nworld")
90
+ )
91
+ end
92
+
93
+ describe "#populate_content_md5" do
94
+ context "when getting" do
95
+ it "doesn't populate content-md5" do
96
+ request.env['REQUEST_METHOD'] = 'GET'
97
+ driven_request.populate_content_md5
98
+ expect(request.env["Content-MD5"]).to be_nil
99
+ end
100
+ end
101
+
102
+ context "when posting" do
103
+ it "populates content-md5" do
104
+ request.env['REQUEST_METHOD'] = 'POST'
105
+ driven_request.populate_content_md5
106
+ expect(request.env["Content-MD5"]).to eq('kZXQvrKoieG+Be1rsZVINw==')
107
+ end
108
+ end
109
+
110
+ context "when putting" do
111
+ it "populates content-md5" do
112
+ request.env['REQUEST_METHOD'] = 'PUT'
113
+ driven_request.populate_content_md5
114
+ expect(request.env["Content-MD5"]).to eq('kZXQvrKoieG+Be1rsZVINw==')
115
+ end
116
+ end
117
+
118
+ context "when deleting" do
119
+ it "doesn't populate content-md5" do
120
+ request.env['REQUEST_METHOD'] = 'DELETE'
121
+ driven_request.populate_content_md5
122
+ expect(request.env["Content-MD5"]).to be_nil
123
+ end
124
+ end
125
+
126
+ end
127
+
128
+ describe "#set_date" do
129
+ it "sets the date" do
130
+ allow(Time).to receive_message_chain(:now, :utc, :httpdate).and_return(timestamp)
131
+ driven_request.set_date
132
+ expect(request.env['HTTP_DATE']).to eq(timestamp)
133
+ end
134
+ end
135
+
136
+ describe "#set_auth_header" do
137
+ it "sets the auth header" do
138
+ driven_request.set_auth_header('APIAuth 1044:54321')
139
+ expect(request.env['Authorization']).to eq('APIAuth 1044:54321')
140
+ end
141
+ end
142
+ end
143
+
144
+ describe "md5_mismatch?" do
145
+ context "when getting" do
146
+ before do
147
+ request.env['REQUEST_METHOD'] = 'GET'
148
+ end
149
+
150
+ it "is false" do
151
+ expect(driven_request.md5_mismatch?).to be false
152
+ end
153
+ end
154
+
155
+ context "when posting" do
156
+ before do
157
+ request.env['REQUEST_METHOD'] = 'POST'
158
+ end
159
+
160
+ context "when calculated matches sent" do
161
+ before do
162
+ request.env["CONTENT_MD5"] = 'kZXQvrKoieG+Be1rsZVINw=='
163
+ end
164
+
165
+ it "is false" do
166
+ expect(driven_request.md5_mismatch?).to be false
167
+ end
168
+ end
169
+
170
+ context "when calculated doesn't match sent" do
171
+ before do
172
+ request.env["CONTENT_MD5"] = "3"
173
+ end
174
+
175
+ it "is true" do
176
+ expect(driven_request.md5_mismatch?).to be true
177
+ end
178
+ end
179
+ end
180
+
181
+ context "when putting" do
182
+ before do
183
+ request.env['REQUEST_METHOD'] = 'PUT'
184
+ end
185
+
186
+ context "when calculated matches sent" do
187
+ before do
188
+ request.env["CONTENT_MD5"] = 'kZXQvrKoieG+Be1rsZVINw=='
189
+ end
190
+
191
+ it "is false" do
192
+ expect(driven_request.md5_mismatch?).to be false
193
+ end
194
+ end
195
+
196
+ context "when calculated doesn't match sent" do
197
+ before do
198
+ request.env["CONTENT_MD5"] = "3"
199
+ end
200
+
201
+ it "is true" do
202
+ expect(driven_request.md5_mismatch?).to be true
203
+ end
204
+ end
205
+ end
206
+
207
+ context "when deleting" do
208
+ before do
209
+ request.env['REQUEST_METHOD'] = 'DELETE'
210
+ end
211
+
212
+ it "is false" do
213
+ expect(driven_request.md5_mismatch?).to be false
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,219 @@
1
+ require 'spec_helper'
2
+
3
+ if defined?(ActionDispatch::Request)
4
+
5
+ describe ApiAuth::RequestDrivers::ActionDispatchRequest do
6
+
7
+ let(:timestamp){ Time.now.utc.httpdate }
8
+
9
+ let(:request) do
10
+ ActionDispatch::Request.new(
11
+ 'AUTHORIZATION' => 'APIAuth 1044:12345',
12
+ 'PATH_INFO' => '/resource.xml',
13
+ 'QUERY_STRING' => 'foo=bar&bar=foo',
14
+ 'REQUEST_METHOD' => 'PUT',
15
+ 'CONTENT_MD5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
16
+ 'CONTENT_TYPE' => 'text/plain',
17
+ 'CONTENT_LENGTH' => '11',
18
+ 'HTTP_DATE' => timestamp,
19
+ 'rack.input' => StringIO.new("hello\nworld")
20
+ )
21
+ end
22
+
23
+ subject(:driven_request){ ApiAuth::RequestDrivers::ActionDispatchRequest.new(request) }
24
+
25
+ describe "getting headers correctly" do
26
+ it "gets the content_type" do
27
+ expect(driven_request.content_type).to eq('text/plain')
28
+ end
29
+
30
+ it "gets the content_md5" do
31
+ expect(driven_request.content_md5).to eq('1B2M2Y8AsgTpgAmY7PhCfg==')
32
+ end
33
+
34
+ it "gets the request_uri" do
35
+ expect(driven_request.request_uri).to eq('/resource.xml?foo=bar&bar=foo')
36
+ end
37
+
38
+ it "gets the timestamp" do
39
+ expect(driven_request.timestamp).to eq(timestamp)
40
+ end
41
+
42
+ it "gets the authorization_header" do
43
+ expect(driven_request.authorization_header).to eq('APIAuth 1044:12345')
44
+ end
45
+
46
+ describe "#calculated_md5" do
47
+ it "calculates md5 from the body" do
48
+ expect(driven_request.calculated_md5).to eq('kZXQvrKoieG+Be1rsZVINw==')
49
+ end
50
+
51
+ it "treats no body as empty string" do
52
+ request.env['rack.input'] = StringIO.new
53
+ request.env['CONTENT_LENGTH'] = 0
54
+ expect(driven_request.calculated_md5).to eq('1B2M2Y8AsgTpgAmY7PhCfg==')
55
+ end
56
+ end
57
+
58
+
59
+ describe "http_method" do
60
+ context "when put request" do
61
+ let(:request) do
62
+ ActionDispatch::Request.new('REQUEST_METHOD' => 'PUT')
63
+ end
64
+
65
+ it "returns upcased put" do
66
+ expect(driven_request.http_method).to eq('PUT')
67
+ end
68
+ end
69
+
70
+ context "when get request" do
71
+ let(:request) do
72
+ ActionDispatch::Request.new('REQUEST_METHOD' => 'GET')
73
+ end
74
+
75
+ it "returns upcased get" do
76
+ expect(driven_request.http_method).to eq('GET')
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ describe "setting headers correctly" do
83
+ let(:request) do
84
+ ActionDispatch::Request.new(
85
+ 'PATH_INFO' => '/resource.xml',
86
+ 'QUERY_STRING' => 'foo=bar&bar=foo',
87
+ 'REQUEST_METHOD' => 'PUT',
88
+ 'CONTENT_TYPE' => 'text/plain',
89
+ 'CONTENT_LENGTH' => '11',
90
+ 'rack.input' => StringIO.new("hello\nworld")
91
+ )
92
+ end
93
+
94
+ describe "#populate_content_md5" do
95
+ context "when getting" do
96
+ it "doesn't populate content-md5" do
97
+ request.env['REQUEST_METHOD'] = 'GET'
98
+ driven_request.populate_content_md5
99
+ expect(request.env["Content-MD5"]).to be_nil
100
+ end
101
+ end
102
+
103
+ context "when posting" do
104
+ it "populates content-md5" do
105
+ request.env['REQUEST_METHOD'] = 'POST'
106
+ driven_request.populate_content_md5
107
+ expect(request.env["Content-MD5"]).to eq('kZXQvrKoieG+Be1rsZVINw==')
108
+ end
109
+ end
110
+
111
+ context "when putting" do
112
+ it "populates content-md5" do
113
+ request.env['REQUEST_METHOD'] = 'PUT'
114
+ driven_request.populate_content_md5
115
+ expect(request.env["Content-MD5"]).to eq('kZXQvrKoieG+Be1rsZVINw==')
116
+ end
117
+ end
118
+
119
+ context "when deleting" do
120
+ it "doesn't populate content-md5" do
121
+ request.env['REQUEST_METHOD'] = 'DELETE'
122
+ driven_request.populate_content_md5
123
+ expect(request.env["Content-MD5"]).to be_nil
124
+ end
125
+ end
126
+
127
+ end
128
+
129
+ describe "#set_date" do
130
+ it "sets the date" do
131
+ allow(Time).to receive_message_chain(:now, :utc, :httpdate).and_return(timestamp)
132
+ driven_request.set_date
133
+ expect(request.env['HTTP_DATE']).to eq(timestamp)
134
+ end
135
+ end
136
+
137
+ describe "#set_auth_header" do
138
+ it "sets the auth header" do
139
+ driven_request.set_auth_header('APIAuth 1044:54321')
140
+ expect(request.env['Authorization']).to eq('APIAuth 1044:54321')
141
+ end
142
+ end
143
+ end
144
+
145
+ describe "md5_mismatch?" do
146
+ context "when getting" do
147
+ before do
148
+ request.env['REQUEST_METHOD'] = 'GET'
149
+ end
150
+
151
+ it "is false" do
152
+ expect(driven_request.md5_mismatch?).to be false
153
+ end
154
+ end
155
+
156
+ context "when posting" do
157
+ before do
158
+ request.env['REQUEST_METHOD'] = 'POST'
159
+ end
160
+
161
+ context "when calculated matches sent" do
162
+ before do
163
+ request.env["CONTENT_MD5"] = 'kZXQvrKoieG+Be1rsZVINw=='
164
+ end
165
+
166
+ it "is false" do
167
+ expect(driven_request.md5_mismatch?).to be false
168
+ end
169
+ end
170
+
171
+ context "when calculated doesn't match sent" do
172
+ before do
173
+ request.env["CONTENT_MD5"] = "3"
174
+ end
175
+
176
+ it "is true" do
177
+ expect(driven_request.md5_mismatch?).to be true
178
+ end
179
+ end
180
+ end
181
+
182
+ context "when putting" do
183
+ before do
184
+ request.env['REQUEST_METHOD'] = 'PUT'
185
+ end
186
+
187
+ context "when calculated matches sent" do
188
+ before do
189
+ request.env["CONTENT_MD5"] = 'kZXQvrKoieG+Be1rsZVINw=='
190
+ end
191
+
192
+ it "is false" do
193
+ expect(driven_request.md5_mismatch?).to be false
194
+ end
195
+ end
196
+
197
+ context "when calculated doesn't match sent" do
198
+ before do
199
+ request.env["CONTENT_MD5"] = "3"
200
+ end
201
+
202
+ it "is true" do
203
+ expect(driven_request.md5_mismatch?).to be true
204
+ end
205
+ end
206
+ end
207
+
208
+ context "when deleting" do
209
+ before do
210
+ request.env['REQUEST_METHOD'] = 'DELETE'
211
+ end
212
+
213
+ it "is false" do
214
+ expect(driven_request.md5_mismatch?).to be false
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end