api-auth 2.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.
@@ -0,0 +1,102 @@
1
+ module ApiAuth
2
+ module RequestDrivers # :nodoc:
3
+ # Internally, Faraday uses the class Faraday::Env to represent requests. The class is not meant
4
+ # to be directly exposed to users, but this is what Faraday middlewares work with. See
5
+ # <https://lostisland.github.io/faraday/middleware/>.
6
+ class FaradayEnv
7
+ include ApiAuth::Helpers
8
+
9
+ def initialize(env)
10
+ @env = env
11
+ end
12
+
13
+ def set_auth_header(header)
14
+ @env.request_headers['Authorization'] = header
15
+ @env
16
+ end
17
+
18
+ def calculated_hash
19
+ sha256_base64digest(body)
20
+ end
21
+
22
+ def populate_content_hash
23
+ return unless %w[POST PUT PATCH].include?(http_method)
24
+
25
+ @env.request_headers['X-Authorization-Content-SHA256'] = calculated_hash
26
+ end
27
+
28
+ def content_hash_mismatch?
29
+ if %w[POST PUT PATCH].include?(http_method)
30
+ calculated_hash != content_hash
31
+ else
32
+ false
33
+ end
34
+ end
35
+
36
+ def http_method
37
+ @env.method.to_s.upcase
38
+ end
39
+
40
+ def content_type
41
+ type = find_header(%w[CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE])
42
+
43
+ # When sending a body-less POST request, the Content-Type is set at the last minute by the
44
+ # Net::HTTP adapter, which states in the documentation for Net::HTTP#post:
45
+ #
46
+ # > You should set Content-Type: header field for POST. If no Content-Type: field given,
47
+ # > this method uses “application/x-www-form-urlencoded” by default.
48
+ #
49
+ # The same applies to PATCH and PUT. Hopefully the other HTTP adapters behave similarly.
50
+ #
51
+ type ||= 'application/x-www-form-urlencoded' if %w[POST PATCH PUT].include?(http_method)
52
+
53
+ type
54
+ end
55
+
56
+ def content_hash
57
+ find_header(%w[X-AUTHORIZATION-CONTENT-SHA256])
58
+ end
59
+
60
+ def original_uri
61
+ find_header(%w[X-ORIGINAL-URI X_ORIGINAL_URI HTTP_X_ORIGINAL_URI])
62
+ end
63
+
64
+ def request_uri
65
+ @env.url.request_uri
66
+ end
67
+
68
+ def set_date
69
+ @env.request_headers['Date'] = Time.now.utc.httpdate
70
+ end
71
+
72
+ def timestamp
73
+ find_header(%w[DATE HTTP_DATE])
74
+ end
75
+
76
+ def authorization_header
77
+ find_header(%w[Authorization AUTHORIZATION HTTP_AUTHORIZATION])
78
+ end
79
+
80
+ def body
81
+ body_source = @env.request_body
82
+ if body_source.respond_to?(:read)
83
+ result = body_source.read
84
+ body_source.rewind
85
+ result
86
+ else
87
+ body_source.to_s
88
+ end
89
+ end
90
+
91
+ def fetch_headers
92
+ capitalize_keys @env.request_headers
93
+ end
94
+
95
+ private
96
+
97
+ def find_header(keys)
98
+ keys.map { |key| @env.request_headers[key] }.compact.first
99
+ end
100
+ end
101
+ end
102
+ end
data/lib/api_auth.rb CHANGED
@@ -14,6 +14,7 @@ require 'api_auth/request_drivers/action_dispatch'
14
14
  require 'api_auth/request_drivers/rack'
15
15
  require 'api_auth/request_drivers/httpi'
16
16
  require 'api_auth/request_drivers/faraday'
17
+ require 'api_auth/request_drivers/faraday_env'
17
18
  require 'api_auth/request_drivers/http'
18
19
 
19
20
  require 'api_auth/headers'
@@ -0,0 +1,35 @@
1
+ require 'api_auth'
2
+
3
+ module Faraday
4
+ module ApiAuth
5
+ # Request middleware for Faraday. It takes the same arguments as ApiAuth.sign!.
6
+ #
7
+ # You will usually need to include it after the other middlewares since ApiAuth needs to hash
8
+ # the final request.
9
+ #
10
+ # Usage:
11
+ #
12
+ # ```ruby
13
+ # require 'faraday/api_auth'
14
+ #
15
+ # conn = Faraday.new do |f|
16
+ # f.request :api_auth, access_id, secret_key
17
+ # # Alternatively:
18
+ # # f.use Faraday::ApiAuth::Middleware, access_id, secret_key
19
+ # end
20
+ # ```
21
+ #
22
+ class Middleware < Faraday::Middleware
23
+ def initialize(app, access_id, secret_key, options = {})
24
+ super(app)
25
+ @access_id = access_id
26
+ @secret_key = secret_key
27
+ @options = options
28
+ end
29
+
30
+ def on_request(env)
31
+ ::ApiAuth.sign!(env, @access_id, @secret_key, @options)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'api_auth/middleware'
2
+
3
+ module Faraday
4
+ # Integrate ApiAuth into Faraday.
5
+ module ApiAuth
6
+ Faraday::Request.register_middleware(api_auth: Middleware)
7
+ end
8
+ end
@@ -24,7 +24,7 @@ describe 'ApiAuth' do
24
24
  end
25
25
 
26
26
  describe '.sign!' do
27
- let(:request) { RestClient::Request.new(url: 'http://google.com', method: :get) }
27
+ let(:request) { RestClient::Request.new(url: 'https://google.com', method: :get) }
28
28
  let(:headers) { ApiAuth::Headers.new(request) }
29
29
 
30
30
  it 'generates date header before signing' do
@@ -182,7 +182,7 @@ describe 'ApiAuth' do
182
182
  context 'normal APIAuth Auth header' do
183
183
  let(:request) do
184
184
  RestClient::Request.new(
185
- url: 'http://google.com',
185
+ url: 'https://google.com',
186
186
  method: :get,
187
187
  headers: { authorization: 'APIAuth 1044:aGVsbG8gd29ybGQ=' }
188
188
  )
@@ -196,7 +196,7 @@ describe 'ApiAuth' do
196
196
  context 'Corporate prefixed APIAuth header' do
197
197
  let(:request) do
198
198
  RestClient::Request.new(
199
- url: 'http://google.com',
199
+ url: 'https://google.com',
200
200
  method: :get,
201
201
  headers: { authorization: 'Corporate APIAuth 1044:aGVsbG8gd29ybGQ=' }
202
202
  )
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+ require 'faraday/api_auth'
3
+
4
+ describe Faraday::ApiAuth::Middleware do
5
+ it 'adds the Authorization headers' do
6
+ conn = Faraday.new('http://localhost/') do |f|
7
+ f.request :api_auth, 'foo', 'secret', digest: 'sha256'
8
+ f.adapter :test do |stub|
9
+ stub.get('http://localhost/test') do |env|
10
+ [200, {}, env.request_headers['Authorization']]
11
+ end
12
+ end
13
+ end
14
+ response = conn.get('test', nil, { 'Date' => 'Tue, 02 Aug 2022 09:29:24 GMT' })
15
+ expect(response.body).to eq 'APIAuth-HMAC-SHA256 foo:Tn/lIZ9kphcO32DwG4wFHenqBt37miDEIkA5ykLgGiQ='
16
+ end
17
+ end
data/spec/headers_spec.rb CHANGED
@@ -8,7 +8,7 @@ describe ApiAuth::Headers do
8
8
  let(:uri) { '' }
9
9
 
10
10
  context 'uri with just host without /' do
11
- let(:uri) { 'http://google.com'.freeze }
11
+ let(:uri) { 'https://google.com'.freeze }
12
12
 
13
13
  it 'return / as canonical string path' do
14
14
  expect(subject.canonical_string).to eq('GET,,,/,')
@@ -20,7 +20,7 @@ describe ApiAuth::Headers do
20
20
  end
21
21
 
22
22
  context 'uri with host and /' do
23
- let(:uri) { 'http://google.com/'.freeze }
23
+ let(:uri) { 'https://google.com/'.freeze }
24
24
 
25
25
  it 'return / as canonical string path' do
26
26
  expect(subject.canonical_string).to eq('GET,,,/,')
@@ -31,8 +31,8 @@ describe ApiAuth::Headers do
31
31
  end
32
32
  end
33
33
 
34
- context 'uri has a string matching http:// in it' do
35
- let(:uri) { 'http://google.com/?redirect_to=https://www.example.com'.freeze }
34
+ context 'uri has a string matching https:// in it' do
35
+ let(:uri) { 'https://google.com/?redirect_to=https://www.example.com'.freeze }
36
36
 
37
37
  it 'return /?redirect_to=https://www.example.com as canonical string path' do
38
38
  expect(subject.canonical_string).to eq('GET,,,/?redirect_to=https://www.example.com,')
@@ -46,7 +46,7 @@ describe ApiAuth::Headers do
46
46
 
47
47
  context 'string construction' do
48
48
  context 'with a driver that supplies http_method' do
49
- let(:request) { RestClient::Request.new(url: 'http://google.com', method: :get) }
49
+ let(:request) { RestClient::Request.new(url: 'https://google.com', method: :get) }
50
50
  subject(:headers) { described_class.new(request) }
51
51
  let(:driver) { headers.instance_variable_get('@request') }
52
52
 
@@ -161,7 +161,7 @@ describe ApiAuth::Headers do
161
161
  context 'no content hash already calculated' do
162
162
  let(:request) do
163
163
  RestClient::Request.new(
164
- url: 'http://google.com',
164
+ url: 'https://google.com',
165
165
  method: :post,
166
166
  payload: "hello\nworld"
167
167
  )
@@ -176,7 +176,7 @@ describe ApiAuth::Headers do
176
176
  context 'hash already calculated' do
177
177
  let(:request) do
178
178
  RestClient::Request.new(
179
- url: 'http://google.com',
179
+ url: 'https://google.com',
180
180
  method: :post,
181
181
  payload: "hello\nworld",
182
182
  headers: { 'X-Authorization-Content-SHA256' => 'abcd' }
@@ -191,7 +191,7 @@ describe ApiAuth::Headers do
191
191
  end
192
192
 
193
193
  describe '#content_hash_mismatch?' do
194
- let(:request) { RestClient::Request.new(url: 'http://google.com', method: :get) }
194
+ let(:request) { RestClient::Request.new(url: 'https://google.com', method: :get) }
195
195
  subject(:headers) { described_class.new(request) }
196
196
  let(:driver) { headers.instance_variable_get('@request') }
197
197
 
data/spec/railtie_spec.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'action_controller/test_case'
2
3
 
3
4
  describe 'Rails integration' do
4
5
  API_KEY_STORE = { '1044' => 'l16imAXie1sRMcJODpOG7UwC1VyoqvO13jejkfpKWX4Z09W8DC9IrU23DvCwMry7pgSFW6c5S1GIfV0OY6F/vUA==' }.freeze
@@ -116,7 +117,7 @@ describe 'Rails integration' do
116
117
  describe 'Rails ActiveResource integration' do
117
118
  class TestResource < ActiveResource::Base
118
119
  with_api_auth '1044', API_KEY_STORE['1044']
119
- self.site = 'http://localhost/'
120
+ self.site = 'https://localhost/'
120
121
  self.format = :xml
121
122
  end
122
123
 
@@ -4,6 +4,7 @@ if defined?(ActionController::Request)
4
4
 
5
5
  describe ApiAuth::RequestDrivers::ActionControllerRequest do
6
6
  let(:timestamp) { Time.now.utc.httpdate }
7
+ let(:content_sha256) { '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' }
7
8
 
8
9
  let(:request) do
9
10
  ActionController::Request.new(
@@ -11,7 +12,35 @@ if defined?(ActionController::Request)
11
12
  'PATH_INFO' => '/resource.xml',
12
13
  'QUERY_STRING' => 'foo=bar&bar=foo',
13
14
  'REQUEST_METHOD' => 'PUT',
14
- 'X-Authorization-Content-SHA256' => '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=',
15
+ 'HTTP_X_AUTHORIZATION_CONTENT_SHA256' => content_sha256,
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
+ let(:request2) do
24
+ ActionController::Request.new(
25
+ 'AUTHORIZATION' => 'APIAuth 1044:12345',
26
+ 'PATH_INFO' => '/resource.xml',
27
+ 'QUERY_STRING' => 'foo=bar&bar=foo',
28
+ 'REQUEST_METHOD' => 'PUT',
29
+ 'X_AUTHORIZATION_CONTENT_SHA256' => content_sha256,
30
+ 'CONTENT_TYPE' => 'text/plain',
31
+ 'CONTENT_LENGTH' => '11',
32
+ 'HTTP_DATE' => timestamp,
33
+ 'rack.input' => StringIO.new("hello\nworld")
34
+ )
35
+ end
36
+
37
+ let(:request3) do
38
+ ActionController::Request.new(
39
+ 'AUTHORIZATION' => 'APIAuth 1044:12345',
40
+ 'PATH_INFO' => '/resource.xml',
41
+ 'QUERY_STRING' => 'foo=bar&bar=foo',
42
+ 'REQUEST_METHOD' => 'PUT',
43
+ 'X-AUTHORIZATION-CONTENT-SHA256' => content_sha256,
15
44
  'CONTENT_TYPE' => 'text/plain',
16
45
  'CONTENT_LENGTH' => '11',
17
46
  'HTTP_DATE' => timestamp,
@@ -27,7 +56,17 @@ if defined?(ActionController::Request)
27
56
  end
28
57
 
29
58
  it 'gets the content_hash' do
30
- expect(driven_request.content_hash).to eq('47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=')
59
+ expect(driven_request.content_hash).to eq(content_sha256)
60
+ end
61
+
62
+ it 'gets the content_hash for request 2' do
63
+ example_request = ApiAuth::RequestDrivers::ActionControllerRequest.new(request2)
64
+ expect(example_request.content_hash).to eq(content_sha256)
65
+ end
66
+
67
+ it 'gets the content_hash for request 3' do
68
+ example_request = ApiAuth::RequestDrivers::ActionControllerRequest.new(request3)
69
+ expect(example_request.content_hash).to eq(content_sha256)
31
70
  end
32
71
 
33
72
  it 'gets the request_uri' do
@@ -50,7 +89,7 @@ if defined?(ActionController::Request)
50
89
  it 'treats no body as empty string' do
51
90
  request.env['rack.input'] = StringIO.new
52
91
  request.env['CONTENT_LENGTH'] = 0
53
- expect(driven_request.calculated_hash).to eq('47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=')
92
+ expect(driven_request.calculated_hash).to eq(content_sha256)
54
93
  end
55
94
  end
56
95
 
@@ -4,6 +4,8 @@ if defined?(ActionDispatch::Request)
4
4
 
5
5
  describe ApiAuth::RequestDrivers::ActionDispatchRequest do
6
6
  let(:timestamp) { Time.now.utc.httpdate }
7
+ let(:content_sha256) { '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' }
8
+ let(:content_md5) { '+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' }
7
9
 
8
10
  let(:request) do
9
11
  ActionDispatch::Request.new(
@@ -11,7 +13,49 @@ if defined?(ActionDispatch::Request)
11
13
  'PATH_INFO' => '/resource.xml',
12
14
  'QUERY_STRING' => 'foo=bar&bar=foo',
13
15
  'REQUEST_METHOD' => 'PUT',
14
- 'X-Authorization-Content-SHA256' => '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=',
16
+ 'HTTP_X_AUTHORIZATION_CONTENT_SHA256' => content_sha256,
17
+ 'CONTENT_TYPE' => 'text/plain',
18
+ 'CONTENT_LENGTH' => '11',
19
+ 'HTTP_DATE' => timestamp,
20
+ 'rack.input' => StringIO.new("hello\nworld")
21
+ )
22
+ end
23
+
24
+ let(:request2) do
25
+ ActionDispatch::Request.new(
26
+ 'AUTHORIZATION' => 'APIAuth 1044:12345',
27
+ 'PATH_INFO' => '/resource.xml',
28
+ 'QUERY_STRING' => 'foo=bar&bar=foo',
29
+ 'REQUEST_METHOD' => 'PUT',
30
+ 'X_AUTHORIZATION_CONTENT_SHA256' => content_sha256,
31
+ 'CONTENT_TYPE' => 'text/plain',
32
+ 'CONTENT_LENGTH' => '11',
33
+ 'HTTP_DATE' => timestamp,
34
+ 'rack.input' => StringIO.new("hello\nworld")
35
+ )
36
+ end
37
+
38
+ let(:request3) do
39
+ ActionDispatch::Request.new(
40
+ 'AUTHORIZATION' => 'APIAuth 1044:12345',
41
+ 'PATH_INFO' => '/resource.xml',
42
+ 'QUERY_STRING' => 'foo=bar&bar=foo',
43
+ 'REQUEST_METHOD' => 'PUT',
44
+ 'X-AUTHORIZATION-CONTENT-SHA256' => content_sha256,
45
+ 'CONTENT_TYPE' => 'text/plain',
46
+ 'CONTENT_LENGTH' => '11',
47
+ 'HTTP_DATE' => timestamp,
48
+ 'rack.input' => StringIO.new("hello\nworld")
49
+ )
50
+ end
51
+
52
+ let(:request_md5) do
53
+ ActionDispatch::Request.new(
54
+ 'AUTHORIZATION' => 'APIAuth 1044:12345',
55
+ 'PATH_INFO' => '/resource.xml',
56
+ 'QUERY_STRING' => 'foo=bar&bar=foo',
57
+ 'REQUEST_METHOD' => 'PUT',
58
+ 'CONTENT_MD5' => content_md5,
15
59
  'CONTENT_TYPE' => 'text/plain',
16
60
  'CONTENT_LENGTH' => '11',
17
61
  'HTTP_DATE' => timestamp,
@@ -20,6 +64,11 @@ if defined?(ActionDispatch::Request)
20
64
  end
21
65
 
22
66
  subject(:driven_request) { ApiAuth::RequestDrivers::ActionDispatchRequest.new(request) }
67
+ subject(:driven_request_md5) do
68
+ ApiAuth::RequestDrivers::ActionDispatchRequest.new(request_md5,
69
+ authorize_md5: true)
70
+ end
71
+ subject(:driven_request_sha2_with_md5) { ApiAuth::RequestDrivers::ActionDispatchRequest.new(request, authorize_md5: true) }
23
72
 
24
73
  describe 'getting headers correctly' do
25
74
  it 'gets the content_type' do
@@ -27,7 +76,22 @@ if defined?(ActionDispatch::Request)
27
76
  end
28
77
 
29
78
  it 'gets the content_hash' do
30
- expect(driven_request.content_hash).to eq('47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=')
79
+ expect(driven_request.content_hash).to eq(content_sha256)
80
+ end
81
+
82
+ it 'gets the content_hash for request 2' do
83
+ example_request = ApiAuth::RequestDrivers::ActionDispatchRequest.new(request2)
84
+ expect(example_request.content_hash).to eq(content_sha256)
85
+ end
86
+
87
+ it 'gets the content_hash for request 3' do
88
+ example_request = ApiAuth::RequestDrivers::ActionDispatchRequest.new(request3)
89
+ expect(example_request.content_hash).to eq(content_sha256)
90
+ end
91
+
92
+ it 'gets the content_hash for request_md5' do
93
+ example_request = ApiAuth::RequestDrivers::ActionDispatchRequest.new(request_md5, authorize_md5: true)
94
+ expect(example_request.content_hash).to eq(content_md5)
31
95
  end
32
96
 
33
97
  it 'gets the request_uri' do
@@ -44,13 +108,17 @@ if defined?(ActionDispatch::Request)
44
108
 
45
109
  describe '#calculated_hash' do
46
110
  it 'calculates hash from the body' do
47
- expect(driven_request.calculated_hash).to eq('JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g=')
111
+ expect(driven_request.calculated_hash).to eq(['JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g='])
112
+ end
113
+
114
+ it 'calculates hashes from the body with md5 compatibility option' do
115
+ expect(driven_request_md5.calculated_hash).to eq(%w[JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g= kZXQvrKoieG+Be1rsZVINw==])
48
116
  end
49
117
 
50
118
  it 'treats no body as empty string' do
51
119
  request.env['rack.input'] = StringIO.new
52
120
  request.env['CONTENT_LENGTH'] = 0
53
- expect(driven_request.calculated_hash).to eq('47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=')
121
+ expect(driven_request.calculated_hash).to eq(['47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='])
54
122
  end
55
123
  end
56
124
 
@@ -102,12 +170,12 @@ if defined?(ActionDispatch::Request)
102
170
  it 'populates content hash' do
103
171
  request.env['REQUEST_METHOD'] = 'POST'
104
172
  driven_request.populate_content_hash
105
- expect(request.env['X-AUTHORIZATION-CONTENT-SHA256']).to eq('JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g=')
173
+ expect(request.env['X-AUTHORIZATION-CONTENT-SHA256']).to eq(['JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g='])
106
174
  end
107
175
 
108
176
  it 'refreshes the cached headers' do
109
177
  driven_request.populate_content_hash
110
- expect(driven_request.content_hash).to eq('JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g=')
178
+ expect(driven_request.content_hash).to eq(['JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g='])
111
179
  end
112
180
  end
113
181
 
@@ -115,12 +183,12 @@ if defined?(ActionDispatch::Request)
115
183
  it 'populates content hash' do
116
184
  request.env['REQUEST_METHOD'] = 'PUT'
117
185
  driven_request.populate_content_hash
118
- expect(request.env['X-AUTHORIZATION-CONTENT-SHA256']).to eq('JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g=')
186
+ expect(request.env['X-AUTHORIZATION-CONTENT-SHA256']).to eq(['JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g='])
119
187
  end
120
188
 
121
189
  it 'refreshes the cached headers' do
122
190
  driven_request.populate_content_hash
123
- expect(driven_request.content_hash).to eq('JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g=')
191
+ expect(driven_request.content_hash).to eq(['JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g='])
124
192
  end
125
193
  end
126
194
 
@@ -161,73 +229,129 @@ if defined?(ActionDispatch::Request)
161
229
  context 'when getting' do
162
230
  before do
163
231
  request.env['REQUEST_METHOD'] = 'GET'
232
+ request_md5.env['REQUEST_METHOD'] = 'GET'
164
233
  end
165
234
 
166
235
  it 'is false' do
167
236
  expect(driven_request.content_hash_mismatch?).to be false
168
237
  end
238
+
239
+ it 'is false with md5' do
240
+ expect(driven_request_md5.content_hash_mismatch?).to be false
241
+ end
242
+
243
+ it 'is false with sha2 and md5 compatibility on' do
244
+ expect(driven_request_sha2_with_md5.content_hash_mismatch?).to be false
245
+ end
169
246
  end
170
247
 
171
248
  context 'when posting' do
172
249
  before do
173
250
  request.env['REQUEST_METHOD'] = 'POST'
251
+ request_md5.env['REQUEST_METHOD'] = 'POST'
174
252
  end
175
253
 
176
254
  context 'when calculated matches sent' do
177
255
  before do
178
256
  request.env['X-AUTHORIZATION-CONTENT-SHA256'] = 'JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g='
257
+ request_md5.env['CONTENT_MD5'] = 'kZXQvrKoieG+Be1rsZVINw=='
179
258
  end
180
259
 
181
260
  it 'is false' do
182
261
  expect(driven_request.content_hash_mismatch?).to be false
183
262
  end
263
+
264
+ it 'is false with md5' do
265
+ expect(driven_request_md5.content_hash_mismatch?).to be false
266
+ end
267
+
268
+ it 'is false with sha2 and md5 compatibility on' do
269
+ expect(driven_request_sha2_with_md5.content_hash_mismatch?).to be false
270
+ end
184
271
  end
185
272
 
186
273
  context "when calculated doesn't match sent" do
187
274
  before do
188
275
  request.env['X-AUTHORIZATION-CONTENT-SHA256'] = '3'
276
+ request_md5.env['CONTENT_MD5'] = '3'
189
277
  end
190
278
 
191
279
  it 'is true' do
192
280
  expect(driven_request.content_hash_mismatch?).to be true
193
281
  end
282
+
283
+ it 'is true with md5' do
284
+ expect(driven_request.content_hash_mismatch?).to be true
285
+ end
286
+
287
+ it 'is true with sha2 and md5 compatibility on' do
288
+ expect(driven_request_sha2_with_md5.content_hash_mismatch?).to be true
289
+ end
194
290
  end
195
291
  end
196
292
 
197
293
  context 'when putting' do
198
294
  before do
199
295
  request.env['REQUEST_METHOD'] = 'PUT'
296
+ request_md5.env['REQUEST_METHOD'] = 'PUT'
200
297
  end
201
298
 
202
299
  context 'when calculated matches sent' do
203
300
  before do
204
301
  request.env['X-AUTHORIZATION-CONTENT-SHA256'] = 'JsYKYdAdtYNspw/v1EpqAWYgQTyO9fJZpsVhLU9507g='
302
+ request_md5.env['CONTENT_MD5'] = 'kZXQvrKoieG+Be1rsZVINw=='
205
303
  end
206
304
 
207
305
  it 'is false' do
208
306
  expect(driven_request.content_hash_mismatch?).to be false
209
307
  end
308
+
309
+ it 'is false with md5' do
310
+ expect(driven_request_md5.content_hash_mismatch?).to be false
311
+ end
312
+
313
+ it 'is false with sha2 and md5 compatibility on' do
314
+ expect(driven_request_sha2_with_md5.content_hash_mismatch?).to be false
315
+ end
210
316
  end
211
317
 
212
318
  context "when calculated doesn't match sent" do
213
319
  before do
214
320
  request.env['X-AUTHORIZATION-CONTENT-SHA256'] = '3'
321
+ request_md5.env['CONTENT_MD5'] = '3'
215
322
  end
216
323
 
217
324
  it 'is true' do
218
325
  expect(driven_request.content_hash_mismatch?).to be true
219
326
  end
327
+
328
+ it 'is true with md5' do
329
+ expect(driven_request_md5.content_hash_mismatch?).to be true
330
+ end
331
+
332
+ it 'is true with sha2 and md5 compatibility on' do
333
+ expect(driven_request_sha2_with_md5.content_hash_mismatch?).to be true
334
+ end
220
335
  end
221
336
  end
222
337
 
223
338
  context 'when deleting' do
224
339
  before do
225
340
  request.env['REQUEST_METHOD'] = 'DELETE'
341
+ request_md5.env['REQUEST_METHOD'] = 'DELETE'
226
342
  end
227
343
 
228
344
  it 'is false' do
229
345
  expect(driven_request.content_hash_mismatch?).to be false
230
346
  end
347
+
348
+ it 'is false with md5' do
349
+ expect(driven_request_md5.content_hash_mismatch?).to be false
350
+ end
351
+
352
+ it 'is false with sha2 and md5 compatibility on' do
353
+ expect(driven_request_sha2_with_md5.content_hash_mismatch?).to be false
354
+ end
231
355
  end
232
356
  end
233
357