api-auth 2.2.0 → 2.4.1
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +11 -52
- data/.rubocop_todo.yml +92 -0
- data/.travis.yml +15 -14
- data/CHANGELOG.md +28 -0
- data/Gemfile +1 -1
- data/README.md +77 -38
- data/VERSION +1 -1
- data/api_auth.gemspec +15 -11
- data/gemfiles/http2.gemfile +7 -0
- data/gemfiles/http3.gemfile +7 -0
- data/gemfiles/http4.gemfile +7 -0
- data/gemfiles/rails_5.gemfile +5 -7
- data/gemfiles/rails_51.gemfile +5 -5
- data/gemfiles/rails_52.gemfile +9 -0
- data/gemfiles/rails_60.gemfile +11 -0
- data/lib/api_auth.rb +3 -0
- data/lib/api_auth/base.rb +2 -2
- data/lib/api_auth/headers.rb +19 -8
- data/lib/api_auth/railtie.rb +9 -5
- data/lib/api_auth/request_drivers/action_controller.rb +1 -0
- data/lib/api_auth/request_drivers/faraday.rb +2 -1
- data/lib/api_auth/request_drivers/grape_request.rb +87 -0
- data/lib/api_auth/request_drivers/http.rb +96 -0
- data/lib/api_auth/request_drivers/httpi.rb +1 -0
- data/lib/api_auth/request_drivers/net_http.rb +1 -1
- data/lib/api_auth/request_drivers/rack.rb +1 -0
- data/lib/api_auth/request_drivers/rest_client.rb +3 -2
- data/spec/api_auth_spec.rb +7 -0
- data/spec/headers_spec.rb +26 -8
- data/spec/request_drivers/action_controller_spec.rb +10 -4
- data/spec/request_drivers/action_dispatch_spec.rb +17 -11
- data/spec/request_drivers/curb_spec.rb +9 -3
- data/spec/request_drivers/faraday_spec.rb +6 -0
- data/spec/request_drivers/grape_request_spec.rb +279 -0
- data/spec/request_drivers/http_spec.rb +190 -0
- data/spec/request_drivers/httpi_spec.rb +6 -0
- data/spec/request_drivers/net_http_spec.rb +6 -0
- data/spec/request_drivers/rack_spec.rb +6 -0
- data/spec/request_drivers/rest_client_spec.rb +93 -15
- data/spec/spec_helper.rb +3 -4
- metadata +102 -66
- data/gemfiles/rails_4.gemfile +0 -11
- data/gemfiles/rails_41.gemfile +0 -11
- data/gemfiles/rails_42.gemfile +0 -11
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ApiAuth::RequestDrivers::HttpRequest do
|
4
|
+
let(:timestamp) { Time.now.utc.httpdate }
|
5
|
+
|
6
|
+
let(:request) do
|
7
|
+
HTTP::Request.new(
|
8
|
+
verb: verb,
|
9
|
+
uri: uri,
|
10
|
+
headers: headers,
|
11
|
+
body: body
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:verb) { :put }
|
16
|
+
let(:uri) { 'http://localhost/resource.xml?foo=bar&bar=foo' }
|
17
|
+
let(:body) { "hello\nworld" }
|
18
|
+
|
19
|
+
let(:headers) do
|
20
|
+
{
|
21
|
+
'Authorization' => 'APIAuth 1044:12345',
|
22
|
+
'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
|
23
|
+
'content-type' => 'text/plain',
|
24
|
+
'date' => timestamp
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
subject(:driven_request) { described_class.new(request) }
|
29
|
+
|
30
|
+
describe 'getting headers correctly' do
|
31
|
+
it 'gets the content_type' do
|
32
|
+
expect(driven_request.content_type).to eq('text/plain')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'gets the content_md5' do
|
36
|
+
expect(driven_request.content_md5).to eq('1B2M2Y8AsgTpgAmY7PhCfg==')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'gets the request_uri' do
|
40
|
+
expect(driven_request.request_uri).to eq('/resource.xml?foo=bar&bar=foo')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'gets the timestamp' do
|
44
|
+
expect(driven_request.timestamp).to eq(timestamp)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'gets the authorization_header' do
|
48
|
+
expect(driven_request.authorization_header).to eq('APIAuth 1044:12345')
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#calculated_md5' do
|
52
|
+
it 'calculates md5 from the body' do
|
53
|
+
expect(driven_request.calculated_md5).to eq('kZXQvrKoieG+Be1rsZVINw==')
|
54
|
+
expect(driven_request.body.bytesize).to eq(11)
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'no body' do
|
58
|
+
let(:body) { nil }
|
59
|
+
|
60
|
+
it 'treats no body as empty string' do
|
61
|
+
expect(driven_request.calculated_md5).to eq('1B2M2Y8AsgTpgAmY7PhCfg==')
|
62
|
+
expect(driven_request.body.bytesize).to eq(0)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'multipart content' do
|
67
|
+
let(:body) { File.new('spec/fixtures/upload.png') }
|
68
|
+
|
69
|
+
it 'calculates correctly for multipart content' do
|
70
|
+
expect(driven_request.calculated_md5).to eq('k4U8MTA3RHDcewBzymVNEQ==')
|
71
|
+
expect(driven_request.body.bytesize).to eq(5112)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'http_method' do
|
77
|
+
context 'when put request' do
|
78
|
+
let(:verb) { :put }
|
79
|
+
|
80
|
+
it 'returns upcased put' do
|
81
|
+
expect(driven_request.http_method).to eq('PUT')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'when get request' do
|
86
|
+
let(:verb) { :get }
|
87
|
+
|
88
|
+
it 'returns upcased get' do
|
89
|
+
expect(driven_request.http_method).to eq('GET')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'setting headers correctly' do
|
96
|
+
let(:headers) do
|
97
|
+
{
|
98
|
+
'content-type' => 'text/plain'
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#populate_content_md5' do
|
103
|
+
context 'when request type has no body' do
|
104
|
+
let(:verb) { :get }
|
105
|
+
|
106
|
+
it "doesn't populate content-md5" do
|
107
|
+
driven_request.populate_content_md5
|
108
|
+
expect(request['Content-MD5']).to be_nil
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'when request type has a body' do
|
113
|
+
let(:verb) { :put }
|
114
|
+
|
115
|
+
it 'populates content-md5' do
|
116
|
+
driven_request.populate_content_md5
|
117
|
+
expect(request['Content-MD5']).to eq('kZXQvrKoieG+Be1rsZVINw==')
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'refreshes the cached headers' do
|
121
|
+
driven_request.populate_content_md5
|
122
|
+
expect(driven_request.content_md5).to eq('kZXQvrKoieG+Be1rsZVINw==')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe '#set_date' do
|
128
|
+
before do
|
129
|
+
allow(Time).to receive_message_chain(:now, :utc, :httpdate).and_return(timestamp)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'sets the date header of the request' do
|
133
|
+
driven_request.set_date
|
134
|
+
expect(request['DATE']).to eq(timestamp)
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'refreshes the cached headers' do
|
138
|
+
driven_request.set_date
|
139
|
+
expect(driven_request.timestamp).to eq(timestamp)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe '#set_auth_header' do
|
144
|
+
it 'sets the auth header' do
|
145
|
+
driven_request.set_auth_header('APIAuth 1044:54321')
|
146
|
+
expect(request['Authorization']).to eq('APIAuth 1044:54321')
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe 'md5_mismatch?' do
|
152
|
+
context 'when request type has no body' do
|
153
|
+
let(:verb) { :get }
|
154
|
+
|
155
|
+
it 'is false' do
|
156
|
+
expect(driven_request.md5_mismatch?).to be false
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context 'when request type has a body' do
|
161
|
+
let(:verb) { :put }
|
162
|
+
|
163
|
+
context 'when calculated matches sent' do
|
164
|
+
before do
|
165
|
+
request['Content-MD5'] = 'kZXQvrKoieG+Be1rsZVINw=='
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'is false' do
|
169
|
+
expect(driven_request.md5_mismatch?).to be false
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context "when calculated doesn't match sent" do
|
174
|
+
before do
|
175
|
+
request['Content-MD5'] = '3'
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'is true' do
|
179
|
+
expect(driven_request.md5_mismatch?).to be true
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe 'fetch_headers' do
|
186
|
+
it 'returns request headers' do
|
187
|
+
expect(driven_request.fetch_headers).to include('CONTENT-TYPE' => 'text/plain')
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -151,4 +151,10 @@ describe ApiAuth::RequestDrivers::HttpiRequest do
|
|
151
151
|
end
|
152
152
|
end
|
153
153
|
end
|
154
|
+
|
155
|
+
describe 'fetch_headers' do
|
156
|
+
it 'returns request headers' do
|
157
|
+
expect(driven_request.fetch_headers).to include('CONTENT-TYPE' => 'text/plain')
|
158
|
+
end
|
159
|
+
end
|
154
160
|
end
|
@@ -193,4 +193,10 @@ describe ApiAuth::RequestDrivers::NetHttpRequest do
|
|
193
193
|
end
|
194
194
|
end
|
195
195
|
end
|
196
|
+
|
197
|
+
describe 'fetch_headers' do
|
198
|
+
it 'returns request headers' do
|
199
|
+
expect(driven_request.fetch_headers).to include('content-type' => ['text/plain'])
|
200
|
+
end
|
201
|
+
end
|
196
202
|
end
|
@@ -301,4 +301,10 @@ describe ApiAuth::RequestDrivers::RackRequest do
|
|
301
301
|
end
|
302
302
|
end
|
303
303
|
end
|
304
|
+
|
305
|
+
describe 'fetch_headers' do
|
306
|
+
it 'returns request headers' do
|
307
|
+
expect(driven_request.fetch_headers).to include('CONTENT-TYPE' => 'text/plain')
|
308
|
+
end
|
309
|
+
end
|
304
310
|
end
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe ApiAuth::RequestDrivers::RestClientRequest do
|
4
4
|
let(:timestamp) { Time.now.utc.httpdate }
|
5
5
|
|
6
|
-
let(:request_path) { '/resource.xml?foo=bar&bar=foo' }
|
6
|
+
let(:request_path) { 'http://localhost/resource.xml?foo=bar&bar=foo' }
|
7
7
|
|
8
8
|
let(:request_headers) do
|
9
9
|
{
|
@@ -16,7 +16,7 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
16
16
|
|
17
17
|
let(:request) do
|
18
18
|
RestClient::Request.new(
|
19
|
-
url: '/resource.xml?foo=bar&bar=foo',
|
19
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
20
20
|
headers: request_headers,
|
21
21
|
method: :put,
|
22
22
|
payload: "hello\nworld"
|
@@ -35,7 +35,7 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
35
35
|
end
|
36
36
|
|
37
37
|
it 'gets the request_uri' do
|
38
|
-
expect(driven_request.request_uri).to eq('/resource.xml?foo=bar&bar=foo')
|
38
|
+
expect(driven_request.request_uri).to eq('http://localhost/resource.xml?foo=bar&bar=foo')
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'gets the timestamp' do
|
@@ -53,7 +53,7 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
53
53
|
|
54
54
|
it 'treats no body as empty string' do
|
55
55
|
request = RestClient::Request.new(
|
56
|
-
url: '/resource.xml?foo=bar&bar=foo',
|
56
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
57
57
|
headers: request_headers,
|
58
58
|
method: :put
|
59
59
|
)
|
@@ -66,7 +66,7 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
66
66
|
context 'when put request' do
|
67
67
|
let(:request) do
|
68
68
|
RestClient::Request.new(
|
69
|
-
url: '/resource.xml?foo=bar&bar=foo',
|
69
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
70
70
|
headers: request_headers,
|
71
71
|
method: :put
|
72
72
|
)
|
@@ -80,7 +80,7 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
80
80
|
context 'when get request' do
|
81
81
|
let(:request) do
|
82
82
|
RestClient::Request.new(
|
83
|
-
url: '/resource.xml?foo=bar&bar=foo',
|
83
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
84
84
|
headers: request_headers,
|
85
85
|
method: :get
|
86
86
|
)
|
@@ -104,7 +104,7 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
104
104
|
context 'when getting' do
|
105
105
|
let(:request) do
|
106
106
|
RestClient::Request.new(
|
107
|
-
url: '/resource.xml?foo=bar&bar=foo',
|
107
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
108
108
|
headers: request_headers,
|
109
109
|
method: :get
|
110
110
|
)
|
@@ -119,7 +119,7 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
119
119
|
context 'when posting' do
|
120
120
|
let(:request) do
|
121
121
|
RestClient::Request.new(
|
122
|
-
url: '/resource.xml?foo=bar&bar=foo',
|
122
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
123
123
|
headers: request_headers,
|
124
124
|
method: :post,
|
125
125
|
payload: "hello\nworld"
|
@@ -140,7 +140,7 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
140
140
|
context 'when putting' do
|
141
141
|
let(:request) do
|
142
142
|
RestClient::Request.new(
|
143
|
-
url: '/resource.xml?foo=bar&bar=foo',
|
143
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
144
144
|
headers: request_headers,
|
145
145
|
method: :put,
|
146
146
|
payload: "hello\nworld"
|
@@ -161,7 +161,7 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
161
161
|
context 'when deleting' do
|
162
162
|
let(:request) do
|
163
163
|
RestClient::Request.new(
|
164
|
-
url: '/resource.xml?foo=bar&bar=foo',
|
164
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
165
165
|
headers: request_headers,
|
166
166
|
method: :delete
|
167
167
|
)
|
@@ -203,7 +203,7 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
203
203
|
context 'when getting' do
|
204
204
|
let(:request) do
|
205
205
|
RestClient::Request.new(
|
206
|
-
url: '/resource.xml?foo=bar&bar=foo',
|
206
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
207
207
|
headers: request_headers,
|
208
208
|
method: :get
|
209
209
|
)
|
@@ -217,7 +217,7 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
217
217
|
context 'when posting' do
|
218
218
|
let(:request) do
|
219
219
|
RestClient::Request.new(
|
220
|
-
url: '/resource.xml?foo=bar&bar=foo',
|
220
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
221
221
|
headers: request_headers,
|
222
222
|
method: :post,
|
223
223
|
payload: "hello\nworld"
|
@@ -258,7 +258,7 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
258
258
|
context 'when putting' do
|
259
259
|
let(:request) do
|
260
260
|
RestClient::Request.new(
|
261
|
-
url: '/resource.xml?foo=bar&bar=foo',
|
261
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
262
262
|
headers: request_headers,
|
263
263
|
method: :put,
|
264
264
|
payload: "hello\nworld"
|
@@ -299,7 +299,7 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
299
299
|
context 'when deleting' do
|
300
300
|
let(:request) do
|
301
301
|
RestClient::Request.new(
|
302
|
-
url: '/resource.xml?foo=bar&bar=foo',
|
302
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
303
303
|
headers: request_headers,
|
304
304
|
method: :delete
|
305
305
|
)
|
@@ -311,16 +311,94 @@ describe ApiAuth::RequestDrivers::RestClientRequest do
|
|
311
311
|
end
|
312
312
|
end
|
313
313
|
|
314
|
+
describe 'authentics?' do
|
315
|
+
context 'when getting' do
|
316
|
+
let(:request) do
|
317
|
+
RestClient::Request.new(
|
318
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
319
|
+
method: :get
|
320
|
+
)
|
321
|
+
end
|
322
|
+
|
323
|
+
let(:signed_request) do
|
324
|
+
ApiAuth.sign!(request, '1044', '123')
|
325
|
+
end
|
326
|
+
|
327
|
+
it 'validates that the signature in the request header matches the way we sign it' do
|
328
|
+
expect(ApiAuth.authentic?(signed_request, '123')).to eq true
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
context 'when posting' do
|
333
|
+
let(:request) do
|
334
|
+
RestClient::Request.new(
|
335
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
336
|
+
method: :post,
|
337
|
+
payload: "hello\nworld"
|
338
|
+
)
|
339
|
+
end
|
340
|
+
|
341
|
+
let(:signed_request) do
|
342
|
+
ApiAuth.sign!(request, '1044', '123')
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'validates that the signature in the request header matches the way we sign it' do
|
346
|
+
expect(ApiAuth.authentic?(signed_request, '123')).to eq true
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
context 'when putting' do
|
351
|
+
let(:request) do
|
352
|
+
RestClient::Request.new(
|
353
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
354
|
+
method: :put,
|
355
|
+
payload: "hello\nworld"
|
356
|
+
)
|
357
|
+
end
|
358
|
+
|
359
|
+
let(:signed_request) do
|
360
|
+
ApiAuth.sign!(request, '1044', '123')
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'validates that the signature in the request header matches the way we sign it' do
|
364
|
+
expect(ApiAuth.authentic?(signed_request, '123')).to eq true
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
context 'when deleting' do
|
369
|
+
let(:request) do
|
370
|
+
RestClient::Request.new(
|
371
|
+
url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
372
|
+
method: :delete
|
373
|
+
)
|
374
|
+
end
|
375
|
+
|
376
|
+
let(:signed_request) do
|
377
|
+
ApiAuth.sign!(request, '1044', '123')
|
378
|
+
end
|
379
|
+
|
380
|
+
it 'validates that the signature in the request header matches the way we sign it' do
|
381
|
+
expect(ApiAuth.authentic?(signed_request, '123')).to eq true
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
314
386
|
describe 'edge cases' do
|
315
387
|
it "doesn't mess up symbol based headers" do
|
316
388
|
headers = { 'Content-MD5' => 'e59ff97941044f85df5297e1c302d260',
|
317
389
|
:content_type => 'text/plain',
|
318
390
|
'Date' => 'Mon, 23 Jan 1984 03:29:56 GMT' }
|
319
|
-
request = RestClient::Request.new(url: '/resource.xml?foo=bar&bar=foo',
|
391
|
+
request = RestClient::Request.new(url: 'http://localhost/resource.xml?foo=bar&bar=foo',
|
320
392
|
headers: headers,
|
321
393
|
method: :put)
|
322
394
|
ApiAuth.sign!(request, 'some access id', 'some secret key')
|
323
395
|
expect(request.processed_headers).to have_key('Content-Type')
|
324
396
|
end
|
325
397
|
end
|
398
|
+
|
399
|
+
describe 'fetch_headers' do
|
400
|
+
it 'returns request headers' do
|
401
|
+
expect(driven_request.fetch_headers).to include('CONTENT-TYPE' => 'text/plain')
|
402
|
+
end
|
403
|
+
end
|
326
404
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -13,13 +13,12 @@ require 'api_auth'
|
|
13
13
|
require 'amatch'
|
14
14
|
require 'rest_client'
|
15
15
|
require 'curb'
|
16
|
+
require 'http'
|
16
17
|
require 'httpi'
|
17
18
|
require 'faraday'
|
19
|
+
require 'grape'
|
18
20
|
require 'net/http/post/multipart'
|
19
21
|
|
20
22
|
# Requires supporting files with custom matchers and macros, etc,
|
21
23
|
# in ./support/ and its subdirectories.
|
22
|
-
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
23
|
-
|
24
|
-
RSpec.configure do |config|
|
25
|
-
end
|
24
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }
|