api-auth 1.3.2 → 1.4.0

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.
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