api-auth 1.5.0 → 2.6.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.
- checksums.yaml +5 -5
- data/.github/workflows/main.yml +71 -0
- data/.gitignore +13 -44
- data/.rubocop.yml +39 -0
- data/.rubocop_todo.yml +83 -0
- data/Appraisals +12 -36
- data/CHANGELOG.md +75 -1
- data/README.md +155 -52
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/api_auth.gemspec +35 -23
- data/gemfiles/rails_60.gemfile +9 -0
- data/gemfiles/rails_61.gemfile +9 -0
- data/gemfiles/rails_70.gemfile +9 -0
- data/lib/api-auth.rb +1 -1
- data/lib/api_auth/base.rb +41 -35
- data/lib/api_auth/errors.rb +4 -3
- data/lib/api_auth/headers.rb +38 -42
- data/lib/api_auth/helpers.rb +7 -16
- data/lib/api_auth/railtie.rb +34 -74
- data/lib/api_auth/request_drivers/action_controller.rb +27 -27
- data/lib/api_auth/request_drivers/action_dispatch.rb +0 -6
- data/lib/api_auth/request_drivers/curb.rb +16 -21
- data/lib/api_auth/request_drivers/faraday.rb +25 -34
- data/lib/api_auth/request_drivers/faraday_env.rb +102 -0
- 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 +22 -27
- data/lib/api_auth/request_drivers/net_http.rb +21 -26
- data/lib/api_auth/request_drivers/rack.rb +23 -28
- data/lib/api_auth/request_drivers/rest_client.rb +24 -29
- data/lib/api_auth.rb +4 -0
- data/lib/faraday/api_auth/middleware.rb +35 -0
- data/lib/faraday/api_auth.rb +8 -0
- data/spec/api_auth_spec.rb +135 -96
- data/spec/faraday_middleware_spec.rb +17 -0
- data/spec/headers_spec.rb +148 -108
- data/spec/helpers_spec.rb +8 -10
- data/spec/railtie_spec.rb +80 -99
- data/spec/request_drivers/action_controller_spec.rb +122 -79
- data/spec/request_drivers/action_dispatch_spec.rb +212 -85
- data/spec/request_drivers/curb_spec.rb +36 -33
- data/spec/request_drivers/faraday_env_spec.rb +188 -0
- data/spec/request_drivers/faraday_spec.rb +87 -83
- data/spec/request_drivers/grape_request_spec.rb +280 -0
- data/spec/request_drivers/http_spec.rb +190 -0
- data/spec/request_drivers/httpi_spec.rb +59 -59
- data/spec/request_drivers/net_http_spec.rb +70 -66
- data/spec/request_drivers/rack_spec.rb +101 -97
- data/spec/request_drivers/rest_client_spec.rb +218 -144
- data/spec/spec_helper.rb +15 -12
- metadata +144 -83
- data/.travis.yml +0 -40
- data/Gemfile.lock +0 -115
- data/gemfiles/rails_23.gemfile +0 -9
- data/gemfiles/rails_23.gemfile.lock +0 -70
- data/gemfiles/rails_30.gemfile +0 -9
- data/gemfiles/rails_30.gemfile.lock +0 -92
- data/gemfiles/rails_31.gemfile +0 -9
- data/gemfiles/rails_31.gemfile.lock +0 -98
- data/gemfiles/rails_32.gemfile +0 -9
- data/gemfiles/rails_32.gemfile.lock +0 -97
- data/gemfiles/rails_4.gemfile +0 -9
- data/gemfiles/rails_4.gemfile.lock +0 -94
- data/gemfiles/rails_41.gemfile +0 -9
- data/gemfiles/rails_41.gemfile.lock +0 -98
- data/gemfiles/rails_42.gemfile +0 -9
- data/gemfiles/rails_42.gemfile.lock +0 -115
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ApiAuth::RequestDrivers::GrapeRequest do
|
|
4
|
+
let(:default_method) { 'PUT' }
|
|
5
|
+
let(:default_params) do
|
|
6
|
+
{ 'message' => "hello\nworld" }
|
|
7
|
+
end
|
|
8
|
+
let(:default_options) do
|
|
9
|
+
{
|
|
10
|
+
method: method,
|
|
11
|
+
params: params
|
|
12
|
+
}
|
|
13
|
+
end
|
|
14
|
+
let(:default_env) do
|
|
15
|
+
Rack::MockRequest.env_for('/', options)
|
|
16
|
+
end
|
|
17
|
+
let(:method) { default_method }
|
|
18
|
+
let(:params) { default_params }
|
|
19
|
+
let(:options) { default_options.merge(request_headers) }
|
|
20
|
+
let(:env) { default_env }
|
|
21
|
+
|
|
22
|
+
let(:request) do
|
|
23
|
+
Grape::Request.new(env)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
let(:timestamp) { Time.now.utc.httpdate }
|
|
27
|
+
let(:request_headers) do
|
|
28
|
+
{
|
|
29
|
+
'HTTP_X_AUTHORIZATION' => 'APIAuth 1044:12345',
|
|
30
|
+
'HTTP_X_AUTHORIZATION_CONTENT_SHA256' => 'bxVSdFeR6aHBtw7+EBi5Bt8KllUZpUutOg9ChQmaSPA=',
|
|
31
|
+
'HTTP_X_HMAC_CONTENT_TYPE' => 'text/plain',
|
|
32
|
+
'HTTP_X_HMAC_DATE' => timestamp
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
subject(:driven_request) { ApiAuth::RequestDrivers::GrapeRequest.new(request) }
|
|
37
|
+
|
|
38
|
+
describe 'getting headers correctly' do
|
|
39
|
+
it 'gets the content_type' do
|
|
40
|
+
expect(driven_request.content_type).to eq('text/plain')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'gets the content_hash' do
|
|
44
|
+
expect(driven_request.content_hash).to eq('bxVSdFeR6aHBtw7+EBi5Bt8KllUZpUutOg9ChQmaSPA=')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'gets the request_uri' do
|
|
48
|
+
expect(driven_request.request_uri).to eq('http://example.org/')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'gets the timestamp' do
|
|
52
|
+
expect(driven_request.timestamp).to eq(timestamp)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'gets the authorization_header' do
|
|
56
|
+
expect(driven_request.authorization_header).to eq('APIAuth 1044:12345')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
describe '#calculated_hash' do
|
|
60
|
+
it 'calculates hash from the body' do
|
|
61
|
+
expect(driven_request.calculated_hash).to eq('bxVSdFeR6aHBtw7+EBi5Bt8KllUZpUutOg9ChQmaSPA=')
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context 'no body' do
|
|
65
|
+
let(:params) { {} }
|
|
66
|
+
|
|
67
|
+
it 'treats no body as empty string' do
|
|
68
|
+
expect(driven_request.calculated_hash).to eq('47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=')
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe 'http_method' do
|
|
74
|
+
context 'when put request' do
|
|
75
|
+
let(:method) { 'put' }
|
|
76
|
+
|
|
77
|
+
it 'returns upcased put' do
|
|
78
|
+
expect(driven_request.http_method).to eq('PUT')
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
context 'when get request' do
|
|
83
|
+
let(:method) { 'get' }
|
|
84
|
+
|
|
85
|
+
it 'returns upcased get' do
|
|
86
|
+
expect(driven_request.http_method).to eq('GET')
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe 'setting headers correctly' do
|
|
93
|
+
let(:request_headers) do
|
|
94
|
+
{
|
|
95
|
+
'HTTP_X_HMAC_CONTENT_TYPE' => 'text/plain'
|
|
96
|
+
}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe '#populate_content_hash' do
|
|
100
|
+
context 'when getting' do
|
|
101
|
+
let(:method) { 'get' }
|
|
102
|
+
|
|
103
|
+
it "doesn't populate content hash" do
|
|
104
|
+
driven_request.populate_content_hash
|
|
105
|
+
expect(request.headers['X-Authorization-Content-Sha256']).to be_nil
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
context 'when posting' do
|
|
110
|
+
let(:method) { 'post' }
|
|
111
|
+
|
|
112
|
+
it 'populates content bash' do
|
|
113
|
+
driven_request.populate_content_hash
|
|
114
|
+
expect(request.headers['X-Authorization-Content-Sha256']).to eq('bxVSdFeR6aHBtw7+EBi5Bt8KllUZpUutOg9ChQmaSPA=')
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'refreshes the cached headers' do
|
|
118
|
+
driven_request.populate_content_hash
|
|
119
|
+
expect(driven_request.content_hash).to eq('bxVSdFeR6aHBtw7+EBi5Bt8KllUZpUutOg9ChQmaSPA=')
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
context 'when putting' do
|
|
124
|
+
let(:method) { 'put' }
|
|
125
|
+
|
|
126
|
+
it 'populates content hash' do
|
|
127
|
+
driven_request.populate_content_hash
|
|
128
|
+
expect(request.headers['X-Authorization-Content-Sha256']).to eq('bxVSdFeR6aHBtw7+EBi5Bt8KllUZpUutOg9ChQmaSPA=')
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'refreshes the cached headers' do
|
|
132
|
+
driven_request.populate_content_hash
|
|
133
|
+
expect(driven_request.content_hash).to eq('bxVSdFeR6aHBtw7+EBi5Bt8KllUZpUutOg9ChQmaSPA=')
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
context 'when deleting' do
|
|
138
|
+
let(:method) { 'delete' }
|
|
139
|
+
|
|
140
|
+
it "doesn't populate content hash" do
|
|
141
|
+
driven_request.populate_content_hash
|
|
142
|
+
expect(request.headers['X-Authorization-Content-Sha256']).to be_nil
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
describe '#set_date' do
|
|
148
|
+
before do
|
|
149
|
+
allow(Time).to receive_message_chain(:now, :utc, :httpdate).and_return(timestamp)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it 'sets the date header of the request' do
|
|
153
|
+
allow(Time).to receive_message_chain(:now, :utc, :httpdate).and_return(timestamp)
|
|
154
|
+
driven_request.set_date
|
|
155
|
+
expect(request.headers['Date']).to eq(timestamp)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it 'refreshes the cached headers' do
|
|
159
|
+
driven_request.set_date
|
|
160
|
+
expect(driven_request.timestamp).to eq(timestamp)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
describe '#set_auth_header' do
|
|
165
|
+
it 'sets the auth header' do
|
|
166
|
+
driven_request.set_auth_header('APIAuth 1044:54321')
|
|
167
|
+
expect(request.headers['Authorization']).to eq('APIAuth 1044:54321')
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
describe 'content_hash_mismatch?' do
|
|
173
|
+
context 'when getting' do
|
|
174
|
+
let(:method) { 'get' }
|
|
175
|
+
|
|
176
|
+
it 'is false' do
|
|
177
|
+
expect(driven_request.content_hash_mismatch?).to be false
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
context 'when posting' do
|
|
182
|
+
let(:method) { 'post' }
|
|
183
|
+
|
|
184
|
+
context 'when calculated matches sent' do
|
|
185
|
+
it 'is false' do
|
|
186
|
+
expect(driven_request.content_hash_mismatch?).to be false
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
context "when calculated doesn't match sent" do
|
|
191
|
+
let(:params) { { 'message' => 'hello only' } }
|
|
192
|
+
|
|
193
|
+
it 'is true' do
|
|
194
|
+
expect(driven_request.content_hash_mismatch?).to be true
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
context 'when putting' do
|
|
200
|
+
let(:method) { 'put' }
|
|
201
|
+
|
|
202
|
+
context 'when calculated matches sent' do
|
|
203
|
+
it 'is false' do
|
|
204
|
+
puts driven_request.calculated_hash
|
|
205
|
+
expect(driven_request.content_hash_mismatch?).to be false
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
context "when calculated doesn't match sent" do
|
|
210
|
+
let(:params) { { 'message' => 'hello only' } }
|
|
211
|
+
it 'is true' do
|
|
212
|
+
expect(driven_request.content_hash_mismatch?).to be true
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
context 'when deleting' do
|
|
218
|
+
let(:method) { 'delete' }
|
|
219
|
+
|
|
220
|
+
it 'is false' do
|
|
221
|
+
expect(driven_request.content_hash_mismatch?).to be false
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
describe 'authentics?' do
|
|
227
|
+
let(:request_headers) { {} }
|
|
228
|
+
let(:signed_request) do
|
|
229
|
+
ApiAuth.sign!(request, '1044', '123')
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
context 'when getting' do
|
|
233
|
+
let(:method) { 'get' }
|
|
234
|
+
|
|
235
|
+
it 'validates that the signature in the request header matches the way we sign it' do
|
|
236
|
+
expect(ApiAuth.authentic?(signed_request, '123')).to eq true
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
context 'when posting' do
|
|
241
|
+
let(:method) { 'post' }
|
|
242
|
+
|
|
243
|
+
it 'validates that the signature in the request header matches the way we sign it' do
|
|
244
|
+
expect(ApiAuth.authentic?(signed_request, '123')).to eq true
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
context 'when putting' do
|
|
249
|
+
let(:method) { 'put' }
|
|
250
|
+
|
|
251
|
+
let(:signed_request) do
|
|
252
|
+
ApiAuth.sign!(request, '1044', '123')
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
it 'validates that the signature in the request header matches the way we sign it' do
|
|
256
|
+
expect(ApiAuth.authentic?(signed_request, '123')).to eq true
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
context 'when deleting' do
|
|
261
|
+
let(:method) { 'delete' }
|
|
262
|
+
|
|
263
|
+
let(:signed_request) do
|
|
264
|
+
ApiAuth.sign!(request, '1044', '123')
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
it 'validates that the signature in the request header matches the way we sign it' do
|
|
268
|
+
expect(ApiAuth.authentic?(signed_request, '123')).to eq true
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
describe 'fetch_headers' do
|
|
274
|
+
it 'returns request headers' do
|
|
275
|
+
expect(driven_request.fetch_headers).to include(
|
|
276
|
+
'CONTENT_TYPE' => 'application/x-www-form-urlencoded'
|
|
277
|
+
)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
@@ -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) { 'https://localhost/resource.xml?foo=bar&bar=foo' }
|
|
17
|
+
let(:body) { "hello\nworld" }
|
|
18
|
+
|
|
19
|
+
let(:headers) do
|
|
20
|
+
{
|
|
21
|
+
'Authorization' => 'APIAuth 1044:12345',
|
|
22
|
+
'X-Authorization-Content-SHA256' => '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=',
|
|
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_hash' do
|
|
36
|
+
expect(driven_request.content_hash).to eq('47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=')
|
|
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_hash' do
|
|
52
|
+
it 'calculates hash from the body' do
|
|
53
|
+
expect(driven_request.calculated_hash).to eq('JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g=')
|
|
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_hash).to eq('47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=')
|
|
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_hash).to eq('AlKDe7kjMQhuKgKuNG8I7GA93MasHcaVJkJLaUT7+dY=')
|
|
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_hash' do
|
|
103
|
+
context 'when request type has no body' do
|
|
104
|
+
let(:verb) { :get }
|
|
105
|
+
|
|
106
|
+
it "doesn't populate content hash" do
|
|
107
|
+
driven_request.populate_content_hash
|
|
108
|
+
expect(request['X-Authorization-Content-SHA256']).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 hash' do
|
|
116
|
+
driven_request.populate_content_hash
|
|
117
|
+
expect(request['X-Authorization-Content-SHA256']).to eq('JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g=')
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it 'refreshes the cached headers' do
|
|
121
|
+
driven_request.populate_content_hash
|
|
122
|
+
expect(driven_request.content_hash).to eq('JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g=')
|
|
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 'content_hash_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.content_hash_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['X-Authorization-Content-SHA256'] = 'JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g='
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'is false' do
|
|
169
|
+
expect(driven_request.content_hash_mismatch?).to be false
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
context "when calculated doesn't match sent" do
|
|
174
|
+
before do
|
|
175
|
+
request['X-Authorization-Content-SHA256'] = '3'
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it 'is true' do
|
|
179
|
+
expect(driven_request.content_hash_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
|
|
@@ -1,160 +1,160 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe ApiAuth::RequestDrivers::HttpiRequest do
|
|
4
|
-
|
|
5
|
-
let(:timestamp){ Time.now.utc.httpdate }
|
|
4
|
+
let(:timestamp) { Time.now.utc.httpdate }
|
|
6
5
|
|
|
7
6
|
let(:request) do
|
|
8
|
-
httpi_request = HTTPI::Request.new(
|
|
9
|
-
httpi_request.headers.merge!(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
'date' => timestamp
|
|
14
|
-
})
|
|
7
|
+
httpi_request = HTTPI::Request.new('https://localhost/resource.xml?foo=bar&bar=foo')
|
|
8
|
+
httpi_request.headers.merge!('Authorization' => 'APIAuth 1044:12345',
|
|
9
|
+
'X-Authorization-Content-SHA256' => '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=',
|
|
10
|
+
'content-type' => 'text/plain',
|
|
11
|
+
'date' => timestamp)
|
|
15
12
|
httpi_request.body = "hello\nworld"
|
|
16
13
|
httpi_request
|
|
17
14
|
end
|
|
18
15
|
|
|
19
|
-
subject(:driven_request){ ApiAuth::RequestDrivers::HttpiRequest.new(request) }
|
|
16
|
+
subject(:driven_request) { ApiAuth::RequestDrivers::HttpiRequest.new(request) }
|
|
20
17
|
|
|
21
|
-
describe
|
|
22
|
-
it
|
|
18
|
+
describe 'getting headers correctly' do
|
|
19
|
+
it 'gets the content_type' do
|
|
23
20
|
expect(driven_request.content_type).to eq('text/plain')
|
|
24
21
|
end
|
|
25
22
|
|
|
26
|
-
it
|
|
27
|
-
expect(driven_request.
|
|
23
|
+
it 'gets the content_hash' do
|
|
24
|
+
expect(driven_request.content_hash).to eq('47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=')
|
|
28
25
|
end
|
|
29
26
|
|
|
30
|
-
it
|
|
27
|
+
it 'gets the request_uri' do
|
|
31
28
|
expect(driven_request.request_uri).to eq('/resource.xml?foo=bar&bar=foo')
|
|
32
29
|
end
|
|
33
30
|
|
|
34
|
-
it
|
|
31
|
+
it 'gets the timestamp' do
|
|
35
32
|
expect(driven_request.timestamp).to eq(timestamp)
|
|
36
33
|
end
|
|
37
34
|
|
|
38
|
-
it
|
|
35
|
+
it 'gets the authorization_header' do
|
|
39
36
|
expect(driven_request.authorization_header).to eq('APIAuth 1044:12345')
|
|
40
37
|
end
|
|
41
38
|
|
|
42
|
-
describe
|
|
43
|
-
it
|
|
44
|
-
expect(driven_request.
|
|
39
|
+
describe '#calculated_hash' do
|
|
40
|
+
it 'calculates hash from the body' do
|
|
41
|
+
expect(driven_request.calculated_hash).to eq('JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g=')
|
|
45
42
|
end
|
|
46
43
|
|
|
47
|
-
it
|
|
44
|
+
it 'treats no body as empty string' do
|
|
48
45
|
request.body = nil
|
|
49
|
-
expect(driven_request.
|
|
46
|
+
expect(driven_request.calculated_hash).to eq('47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=')
|
|
50
47
|
end
|
|
51
48
|
end
|
|
52
49
|
|
|
53
|
-
describe
|
|
54
|
-
it
|
|
50
|
+
describe 'http_method' do
|
|
51
|
+
it 'is always nil' do
|
|
55
52
|
expect(driven_request.http_method).to be_nil
|
|
56
53
|
end
|
|
57
54
|
end
|
|
58
55
|
end
|
|
59
56
|
|
|
60
|
-
describe
|
|
57
|
+
describe 'setting headers correctly' do
|
|
61
58
|
let(:request) do
|
|
62
|
-
httpi_request = HTTPI::Request.new(
|
|
63
|
-
httpi_request.headers
|
|
64
|
-
'content-type' => 'text/plain'
|
|
65
|
-
})
|
|
59
|
+
httpi_request = HTTPI::Request.new('https://localhost/resource.xml?foo=bar&bar=foo')
|
|
60
|
+
httpi_request.headers['content-type'] = 'text/plain'
|
|
66
61
|
httpi_request
|
|
67
62
|
end
|
|
68
63
|
|
|
69
|
-
describe
|
|
70
|
-
context
|
|
64
|
+
describe '#populate_content_hash' do
|
|
65
|
+
context 'when there is no content body' do
|
|
71
66
|
before do
|
|
72
67
|
request.body = nil
|
|
73
68
|
end
|
|
74
69
|
|
|
75
|
-
it "doesn't populate content
|
|
76
|
-
driven_request.
|
|
77
|
-
expect(request.headers[
|
|
70
|
+
it "doesn't populate content hash" do
|
|
71
|
+
driven_request.populate_content_hash
|
|
72
|
+
expect(request.headers['X-Authorization-Content-SHA256']).to be_nil
|
|
78
73
|
end
|
|
79
74
|
end
|
|
80
75
|
|
|
81
|
-
context
|
|
76
|
+
context 'when there is a content body' do
|
|
82
77
|
before do
|
|
83
78
|
request.body = "hello\nworld"
|
|
84
79
|
end
|
|
85
80
|
|
|
86
|
-
it
|
|
87
|
-
driven_request.
|
|
88
|
-
expect(request.headers[
|
|
81
|
+
it 'populates content hash' do
|
|
82
|
+
driven_request.populate_content_hash
|
|
83
|
+
expect(request.headers['X-Authorization-Content-SHA256']).to eq('JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g=')
|
|
89
84
|
end
|
|
90
85
|
|
|
91
|
-
it
|
|
92
|
-
driven_request.
|
|
93
|
-
expect(driven_request.
|
|
86
|
+
it 'refreshes the cached headers' do
|
|
87
|
+
driven_request.populate_content_hash
|
|
88
|
+
expect(driven_request.content_hash).to eq('JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g=')
|
|
94
89
|
end
|
|
95
90
|
end
|
|
96
91
|
end
|
|
97
92
|
|
|
98
|
-
describe
|
|
93
|
+
describe '#set_date' do
|
|
99
94
|
before do
|
|
100
95
|
allow(Time).to receive_message_chain(:now, :utc, :httpdate).and_return(timestamp)
|
|
101
96
|
end
|
|
102
97
|
|
|
103
|
-
it
|
|
98
|
+
it 'sets the date header of the request' do
|
|
104
99
|
driven_request.set_date
|
|
105
100
|
expect(request.headers['DATE']).to eq(timestamp)
|
|
106
101
|
end
|
|
107
102
|
|
|
108
|
-
it
|
|
103
|
+
it 'refreshes the cached headers' do
|
|
109
104
|
driven_request.set_date
|
|
110
105
|
expect(driven_request.timestamp).to eq(timestamp)
|
|
111
106
|
end
|
|
112
107
|
end
|
|
113
108
|
|
|
114
|
-
describe
|
|
115
|
-
it
|
|
109
|
+
describe '#set_auth_header' do
|
|
110
|
+
it 'sets the auth header' do
|
|
116
111
|
driven_request.set_auth_header('APIAuth 1044:54321')
|
|
117
112
|
expect(request.headers['Authorization']).to eq('APIAuth 1044:54321')
|
|
118
113
|
end
|
|
119
114
|
end
|
|
120
115
|
end
|
|
121
116
|
|
|
122
|
-
describe
|
|
123
|
-
context
|
|
117
|
+
describe 'content_hash_mismatch?' do
|
|
118
|
+
context 'when there is no content body' do
|
|
124
119
|
before do
|
|
125
120
|
request.body = nil
|
|
126
121
|
end
|
|
127
122
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
expect(driven_request.md5_mismatch?).to be false
|
|
123
|
+
it 'is false' do
|
|
124
|
+
expect(driven_request.content_hash_mismatch?).to be false
|
|
131
125
|
end
|
|
132
126
|
end
|
|
133
127
|
|
|
134
|
-
context
|
|
128
|
+
context 'when there is a content body' do
|
|
135
129
|
before do
|
|
136
130
|
request.body = "hello\nworld"
|
|
137
131
|
end
|
|
138
132
|
|
|
139
|
-
context
|
|
133
|
+
context 'when calculated matches sent' do
|
|
140
134
|
before do
|
|
141
|
-
request.headers[
|
|
135
|
+
request.headers['X-Authorization-Content-SHA256'] = 'JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g='
|
|
142
136
|
end
|
|
143
137
|
|
|
144
|
-
it
|
|
145
|
-
expect(driven_request.
|
|
138
|
+
it 'is false' do
|
|
139
|
+
expect(driven_request.content_hash_mismatch?).to be false
|
|
146
140
|
end
|
|
147
141
|
end
|
|
148
142
|
|
|
149
143
|
context "when calculated doesn't match sent" do
|
|
150
144
|
before do
|
|
151
|
-
request.headers[
|
|
145
|
+
request.headers['X-Authorization-Content-SHA256'] = '3'
|
|
152
146
|
end
|
|
153
147
|
|
|
154
|
-
it
|
|
155
|
-
expect(driven_request.
|
|
148
|
+
it 'is true' do
|
|
149
|
+
expect(driven_request.content_hash_mismatch?).to be true
|
|
156
150
|
end
|
|
157
151
|
end
|
|
158
152
|
end
|
|
159
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
|
|
160
160
|
end
|