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
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
# give access to RestClient @processed_headers
|
|
2
|
-
module RestClient;class Request;attr_accessor :processed_headers;end;end
|
|
2
|
+
module RestClient; class Request; attr_accessor :processed_headers; end; end
|
|
3
3
|
|
|
4
4
|
module ApiAuth
|
|
5
|
-
|
|
6
5
|
module RequestDrivers # :nodoc:
|
|
7
|
-
|
|
8
6
|
class RestClientRequest # :nodoc:
|
|
9
|
-
|
|
10
7
|
include ApiAuth::Helpers
|
|
11
8
|
|
|
12
9
|
def initialize(request)
|
|
@@ -16,31 +13,31 @@ module ApiAuth
|
|
|
16
13
|
end
|
|
17
14
|
|
|
18
15
|
def set_auth_header(header)
|
|
19
|
-
@request.headers
|
|
16
|
+
@request.headers['Authorization'] = header
|
|
20
17
|
save_headers # enforce update of processed_headers based on last updated headers
|
|
21
18
|
@request
|
|
22
19
|
end
|
|
23
20
|
|
|
24
|
-
def
|
|
21
|
+
def calculated_hash
|
|
25
22
|
if @request.payload
|
|
26
23
|
body = @request.payload.read
|
|
27
24
|
@request.payload.instance_variable_get(:@stream).seek(0)
|
|
28
25
|
else
|
|
29
26
|
body = ''
|
|
30
27
|
end
|
|
31
|
-
|
|
28
|
+
sha256_base64digest(body)
|
|
32
29
|
end
|
|
33
30
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
def populate_content_hash
|
|
32
|
+
return unless %w[post put].include?(@request.method.to_s)
|
|
33
|
+
|
|
34
|
+
@request.headers['X-Authorization-Content-SHA256'] = calculated_hash
|
|
35
|
+
save_headers
|
|
39
36
|
end
|
|
40
37
|
|
|
41
|
-
def
|
|
42
|
-
if [
|
|
43
|
-
|
|
38
|
+
def content_hash_mismatch?
|
|
39
|
+
if %w[post put].include?(@request.method.to_s)
|
|
40
|
+
calculated_hash != content_hash
|
|
44
41
|
else
|
|
45
42
|
false
|
|
46
43
|
end
|
|
@@ -55,13 +52,15 @@ module ApiAuth
|
|
|
55
52
|
end
|
|
56
53
|
|
|
57
54
|
def content_type
|
|
58
|
-
|
|
59
|
-
value.nil? ? "": value
|
|
55
|
+
find_header(%w[CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE])
|
|
60
56
|
end
|
|
61
57
|
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
def content_hash
|
|
59
|
+
find_header(%w[X-AUTHORIZATION-CONTENT-SHA256])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def original_uri
|
|
63
|
+
find_header(%w[X-ORIGINAL-URI X_ORIGINAL_URI HTTP_X_ORIGINAL_URI])
|
|
65
64
|
end
|
|
66
65
|
|
|
67
66
|
def request_uri
|
|
@@ -69,32 +68,28 @@ module ApiAuth
|
|
|
69
68
|
end
|
|
70
69
|
|
|
71
70
|
def set_date
|
|
72
|
-
@request.headers
|
|
71
|
+
@request.headers['DATE'] = Time.now.utc.httpdate
|
|
73
72
|
save_headers
|
|
74
73
|
end
|
|
75
74
|
|
|
76
75
|
def timestamp
|
|
77
|
-
|
|
78
|
-
value.nil? ? "" : value
|
|
76
|
+
find_header(%w[DATE HTTP_DATE])
|
|
79
77
|
end
|
|
80
78
|
|
|
81
79
|
def authorization_header
|
|
82
|
-
find_header %w
|
|
80
|
+
find_header %w[Authorization AUTHORIZATION HTTP_AUTHORIZATION]
|
|
83
81
|
end
|
|
84
82
|
|
|
85
|
-
|
|
83
|
+
private
|
|
86
84
|
|
|
87
85
|
def find_header(keys)
|
|
88
|
-
keys.map {|key| @headers[key] }.compact.first
|
|
86
|
+
keys.map { |key| @headers[key] }.compact.first
|
|
89
87
|
end
|
|
90
88
|
|
|
91
89
|
def save_headers
|
|
92
90
|
@request.processed_headers = @request.make_headers(@request.headers)
|
|
93
91
|
@headers = fetch_headers
|
|
94
92
|
end
|
|
95
|
-
|
|
96
93
|
end
|
|
97
|
-
|
|
98
94
|
end
|
|
99
|
-
|
|
100
95
|
end
|
data/lib/api_auth.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'openssl'
|
|
2
2
|
require 'base64'
|
|
3
|
+
require 'time'
|
|
3
4
|
|
|
4
5
|
require 'api_auth/errors'
|
|
5
6
|
require 'api_auth/helpers'
|
|
@@ -8,10 +9,13 @@ require 'api_auth/request_drivers/net_http'
|
|
|
8
9
|
require 'api_auth/request_drivers/curb'
|
|
9
10
|
require 'api_auth/request_drivers/rest_client'
|
|
10
11
|
require 'api_auth/request_drivers/action_controller'
|
|
12
|
+
require 'api_auth/request_drivers/grape_request'
|
|
11
13
|
require 'api_auth/request_drivers/action_dispatch'
|
|
12
14
|
require 'api_auth/request_drivers/rack'
|
|
13
15
|
require 'api_auth/request_drivers/httpi'
|
|
14
16
|
require 'api_auth/request_drivers/faraday'
|
|
17
|
+
require 'api_auth/request_drivers/faraday_env'
|
|
18
|
+
require 'api_auth/request_drivers/http'
|
|
15
19
|
|
|
16
20
|
require 'api_auth/headers'
|
|
17
21
|
require 'api_auth/base'
|
|
@@ -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
|
data/spec/api_auth_spec.rb
CHANGED
|
@@ -1,170 +1,209 @@
|
|
|
1
|
-
|
|
2
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
1
|
+
require 'spec_helper'
|
|
3
2
|
|
|
4
|
-
describe
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
it "should generate secret keys" do
|
|
3
|
+
describe 'ApiAuth' do
|
|
4
|
+
describe 'generating secret keys' do
|
|
5
|
+
it 'should generate secret keys' do
|
|
9
6
|
ApiAuth.generate_secret_key
|
|
10
7
|
end
|
|
11
8
|
|
|
12
|
-
it
|
|
9
|
+
it 'should generate secret keys that are 88 characters' do
|
|
13
10
|
expect(ApiAuth.generate_secret_key.size).to be(88)
|
|
14
11
|
end
|
|
15
12
|
|
|
16
|
-
it
|
|
13
|
+
it 'should generate keys that have a Hamming Distance of at least 65' do
|
|
17
14
|
key1 = ApiAuth.generate_secret_key
|
|
18
15
|
key2 = ApiAuth.generate_secret_key
|
|
19
16
|
expect(Amatch::Hamming.new(key1).match(key2)).to be > 65
|
|
20
17
|
end
|
|
21
|
-
|
|
22
18
|
end
|
|
23
19
|
|
|
24
|
-
def hmac(secret_key, request, canonical_string = nil)
|
|
20
|
+
def hmac(secret_key, request, canonical_string = nil, digest = 'sha1')
|
|
25
21
|
canonical_string ||= ApiAuth::Headers.new(request).canonical_string
|
|
26
|
-
digest = OpenSSL::Digest.new(
|
|
22
|
+
digest = OpenSSL::Digest.new(digest)
|
|
27
23
|
ApiAuth.b64_encode(OpenSSL::HMAC.digest(digest, secret_key, canonical_string))
|
|
28
24
|
end
|
|
29
25
|
|
|
30
|
-
describe
|
|
31
|
-
let(:request){ RestClient::Request.new(:
|
|
32
|
-
let(:headers){ ApiAuth::Headers.new(request) }
|
|
26
|
+
describe '.sign!' do
|
|
27
|
+
let(:request) { RestClient::Request.new(url: 'https://google.com', method: :get) }
|
|
28
|
+
let(:headers) { ApiAuth::Headers.new(request) }
|
|
33
29
|
|
|
34
|
-
it
|
|
30
|
+
it 'generates date header before signing' do
|
|
35
31
|
expect(ApiAuth::Headers).to receive(:new).and_return(headers)
|
|
36
32
|
|
|
37
33
|
expect(headers).to receive(:set_date).ordered
|
|
38
34
|
expect(headers).to receive(:sign_header).ordered
|
|
39
35
|
|
|
40
|
-
ApiAuth.sign!(request,
|
|
36
|
+
ApiAuth.sign!(request, 'abc', '123')
|
|
41
37
|
end
|
|
42
38
|
|
|
43
|
-
it
|
|
39
|
+
it 'generates X-Authorization-Content-SHA256 header before signing' do
|
|
44
40
|
expect(ApiAuth::Headers).to receive(:new).and_return(headers)
|
|
45
|
-
expect(headers).to receive(:
|
|
41
|
+
expect(headers).to receive(:calculate_hash).ordered
|
|
46
42
|
expect(headers).to receive(:sign_header).ordered
|
|
47
43
|
|
|
48
|
-
ApiAuth.sign!(request,
|
|
44
|
+
ApiAuth.sign!(request, 'abc', '123')
|
|
49
45
|
end
|
|
50
46
|
|
|
51
|
-
it
|
|
52
|
-
expect(ApiAuth.sign!(request,
|
|
47
|
+
it 'returns the same request object back' do
|
|
48
|
+
expect(ApiAuth.sign!(request, 'abc', '123')).to be request
|
|
53
49
|
end
|
|
54
50
|
|
|
55
|
-
it
|
|
56
|
-
ApiAuth.sign!(request,
|
|
57
|
-
signature = hmac(
|
|
51
|
+
it 'calculates the hmac_signature as expected' do
|
|
52
|
+
ApiAuth.sign!(request, '1044', '123')
|
|
53
|
+
signature = hmac('123', request)
|
|
58
54
|
expect(request.headers['Authorization']).to eq("APIAuth 1044:#{signature}")
|
|
59
55
|
end
|
|
60
56
|
|
|
61
|
-
context
|
|
62
|
-
let(:request)
|
|
63
|
-
Net::HTTP::Put.new(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
57
|
+
context 'when passed the hmac digest option' do
|
|
58
|
+
let(:request) do
|
|
59
|
+
Net::HTTP::Put.new('/resource.xml?foo=bar&bar=foo',
|
|
60
|
+
'content-type' => 'text/plain',
|
|
61
|
+
'content-hash' => '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=',
|
|
62
|
+
'date' => Time.now.utc.httpdate)
|
|
63
|
+
end
|
|
69
64
|
|
|
70
|
-
let(:canonical_string){ ApiAuth::Headers.new(request).
|
|
65
|
+
let(:canonical_string) { ApiAuth::Headers.new(request).canonical_string }
|
|
71
66
|
|
|
72
|
-
it
|
|
73
|
-
ApiAuth.sign!(request,
|
|
74
|
-
signature = hmac(
|
|
75
|
-
expect(request['Authorization']).to eq("APIAuth 1044:#{signature}")
|
|
67
|
+
it 'calculates the hmac_signature with http method' do
|
|
68
|
+
ApiAuth.sign!(request, '1044', '123', digest: 'sha256')
|
|
69
|
+
signature = hmac('123', request, canonical_string, 'sha256')
|
|
70
|
+
expect(request['Authorization']).to eq("APIAuth-HMAC-SHA256 1044:#{signature}")
|
|
76
71
|
end
|
|
77
72
|
end
|
|
78
73
|
end
|
|
79
74
|
|
|
80
|
-
describe
|
|
81
|
-
let(:request)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
75
|
+
describe '.authentic?' do
|
|
76
|
+
let(:request) do
|
|
77
|
+
Net::HTTP::Put.new('/resource.xml?foo=bar&bar=foo',
|
|
78
|
+
'content-type' => 'text/plain',
|
|
79
|
+
'X-Authorization-Content-SHA256' => '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=',
|
|
80
|
+
'date' => Time.now.utc.httpdate)
|
|
81
|
+
end
|
|
87
82
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
83
|
+
let(:signed_request) do
|
|
84
|
+
signature = hmac('123', request)
|
|
85
|
+
request['Authorization'] = "APIAuth 1044:#{signature}"
|
|
86
|
+
request
|
|
87
|
+
end
|
|
92
88
|
|
|
93
|
-
it
|
|
94
|
-
expect(ApiAuth.authentic?(
|
|
89
|
+
it 'validates that the signature in the request header matches the way we sign it' do
|
|
90
|
+
expect(ApiAuth.authentic?(signed_request, '123')).to eq true
|
|
95
91
|
end
|
|
96
92
|
|
|
97
|
-
it
|
|
98
|
-
expect(ApiAuth.authentic?(
|
|
93
|
+
it 'fails to validate a non matching signature' do
|
|
94
|
+
expect(ApiAuth.authentic?(signed_request, '456')).to eq false
|
|
99
95
|
end
|
|
100
96
|
|
|
101
|
-
it
|
|
102
|
-
request['
|
|
103
|
-
expect(ApiAuth.authentic?(
|
|
97
|
+
it 'fails to validate non matching hash' do
|
|
98
|
+
request['X-Authorization-Content-SHA256'] = '12345'
|
|
99
|
+
expect(ApiAuth.authentic?(signed_request, '123')).to eq false
|
|
104
100
|
end
|
|
105
101
|
|
|
106
|
-
it
|
|
102
|
+
it 'fails to validate expired requests' do
|
|
107
103
|
request['date'] = 16.minutes.ago.utc.httpdate
|
|
108
|
-
expect(ApiAuth.authentic?(
|
|
104
|
+
expect(ApiAuth.authentic?(signed_request, '123')).to eq false
|
|
109
105
|
end
|
|
110
106
|
|
|
111
|
-
it
|
|
112
|
-
request['date'] =
|
|
113
|
-
expect(ApiAuth.authentic?(
|
|
107
|
+
it 'fails to validate far future requests' do
|
|
108
|
+
request['date'] = 16.minutes.from_now.utc.httpdate
|
|
109
|
+
expect(ApiAuth.authentic?(signed_request, '123')).to eq false
|
|
114
110
|
end
|
|
115
111
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
112
|
+
it 'fails to validate if the date is invalid' do
|
|
113
|
+
request['date'] = '٢٠١٤-٠٩-٠٨ ١٦:٣١:١٤ +٠٣٠٠'
|
|
114
|
+
expect(ApiAuth.authentic?(signed_request, '123')).to eq false
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'fails to validate if the request method differs' do
|
|
118
|
+
canonical_string = ApiAuth::Headers.new(request).canonical_string('POST')
|
|
119
|
+
signature = hmac('123', request, canonical_string)
|
|
120
|
+
request['Authorization'] = "APIAuth 1044:#{signature}"
|
|
121
|
+
expect(ApiAuth.authentic?(request, '123')).to eq false
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
context 'when passed the hmac digest option' do
|
|
125
|
+
let(:request) do
|
|
126
|
+
new_request = Net::HTTP::Put.new('/resource.xml?foo=bar&bar=foo',
|
|
127
|
+
'content-type' => 'text/plain',
|
|
128
|
+
'X-Authorization-Content-SHA256' => '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=',
|
|
129
|
+
'date' => Time.now.utc.httpdate)
|
|
130
|
+
canonical_string = ApiAuth::Headers.new(new_request).canonical_string
|
|
131
|
+
signature = hmac('123', new_request, canonical_string, 'sha256')
|
|
132
|
+
new_request['Authorization'] = "APIAuth-HMAC-#{digest} 1044:#{signature}"
|
|
126
133
|
new_request
|
|
127
|
-
|
|
134
|
+
end
|
|
128
135
|
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
context 'valid request digest' do
|
|
137
|
+
let(:digest) { 'SHA256' }
|
|
138
|
+
|
|
139
|
+
context 'matching client digest' do
|
|
140
|
+
it 'validates matching digest' do
|
|
141
|
+
expect(ApiAuth.authentic?(request, '123', digest: 'sha256')).to eq true
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
context 'different client digest' do
|
|
146
|
+
it 'raises an exception' do
|
|
147
|
+
expect { ApiAuth.authentic?(request, '123', digest: 'sha512') }.to raise_error(ApiAuth::InvalidRequestDigest)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
131
150
|
end
|
|
132
151
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
152
|
+
context 'invalid request digest' do
|
|
153
|
+
let(:digest) { 'SHA111' }
|
|
154
|
+
|
|
155
|
+
it 'fails validation' do
|
|
156
|
+
expect(ApiAuth.authentic?(request, '123', digest: 'sha111')).to eq false
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
context 'when passed the clock_skew option' do
|
|
162
|
+
it 'fails to validate expired requests' do
|
|
163
|
+
request['date'] = 90.seconds.ago.utc.httpdate
|
|
164
|
+
expect(ApiAuth.authentic?(signed_request, '123', clock_skew: 60.seconds)).to eq false
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it 'fails to validate far future requests' do
|
|
168
|
+
request['date'] = 90.seconds.from_now.utc.httpdate
|
|
169
|
+
expect(ApiAuth.authentic?(signed_request, '123', clock_skew: 60.seconds)).to eq false
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
context 'when passed the headers_to_sign option' do
|
|
174
|
+
it 'validates the request' do
|
|
175
|
+
request['X-Forwarded-For'] = '192.168.1.1'
|
|
176
|
+
expect(ApiAuth.authentic?(signed_request, '123', headers_to_sign: %w[HTTP_X_FORWARDED_FOR])).to eq true
|
|
138
177
|
end
|
|
139
178
|
end
|
|
140
179
|
end
|
|
141
180
|
|
|
142
|
-
describe
|
|
143
|
-
context
|
|
144
|
-
let(:request)
|
|
181
|
+
describe '.access_id' do
|
|
182
|
+
context 'normal APIAuth Auth header' do
|
|
183
|
+
let(:request) do
|
|
145
184
|
RestClient::Request.new(
|
|
146
|
-
:
|
|
147
|
-
:
|
|
148
|
-
:
|
|
185
|
+
url: 'https://google.com',
|
|
186
|
+
method: :get,
|
|
187
|
+
headers: { authorization: 'APIAuth 1044:aGVsbG8gd29ybGQ=' }
|
|
149
188
|
)
|
|
150
|
-
|
|
189
|
+
end
|
|
151
190
|
|
|
152
|
-
it
|
|
153
|
-
expect(ApiAuth.access_id(request)).to eq(
|
|
191
|
+
it 'parses it from the Auth Header' do
|
|
192
|
+
expect(ApiAuth.access_id(request)).to eq('1044')
|
|
154
193
|
end
|
|
155
194
|
end
|
|
156
195
|
|
|
157
|
-
context
|
|
158
|
-
let(:request)
|
|
196
|
+
context 'Corporate prefixed APIAuth header' do
|
|
197
|
+
let(:request) do
|
|
159
198
|
RestClient::Request.new(
|
|
160
|
-
:
|
|
161
|
-
:
|
|
162
|
-
:
|
|
199
|
+
url: 'https://google.com',
|
|
200
|
+
method: :get,
|
|
201
|
+
headers: { authorization: 'Corporate APIAuth 1044:aGVsbG8gd29ybGQ=' }
|
|
163
202
|
)
|
|
164
|
-
|
|
203
|
+
end
|
|
165
204
|
|
|
166
|
-
it
|
|
167
|
-
expect(ApiAuth.access_id(request)).to eq(
|
|
205
|
+
it 'parses it from the Auth Header' do
|
|
206
|
+
expect(ApiAuth.access_id(request)).to eq('1044')
|
|
168
207
|
end
|
|
169
208
|
end
|
|
170
209
|
end
|
|
@@ -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
|