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
data/spec/headers_spec.rb
CHANGED
|
@@ -1,26 +1,17 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe ApiAuth::Headers do
|
|
4
|
-
|
|
5
4
|
describe '#canonical_string' do
|
|
6
|
-
context
|
|
7
|
-
let(:request) { RestClient::Request.new(:
|
|
5
|
+
context 'uri edge cases' do
|
|
6
|
+
let(:request) { RestClient::Request.new(url: uri, method: :get) }
|
|
8
7
|
subject(:headers) { described_class.new(request) }
|
|
9
8
|
let(:uri) { '' }
|
|
10
9
|
|
|
11
|
-
context 'empty uri' do
|
|
12
|
-
let(:uri) { ''.freeze }
|
|
13
|
-
|
|
14
|
-
it 'adds / to canonical string' do
|
|
15
|
-
expect(subject.canonical_string).to eq(',,/,')
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
10
|
context 'uri with just host without /' do
|
|
20
|
-
let(:uri) { '
|
|
11
|
+
let(:uri) { 'https://google.com'.freeze }
|
|
21
12
|
|
|
22
13
|
it 'return / as canonical string path' do
|
|
23
|
-
expect(subject.canonical_string).to eq('
|
|
14
|
+
expect(subject.canonical_string).to eq('GET,,,/,')
|
|
24
15
|
end
|
|
25
16
|
|
|
26
17
|
it 'does not change request url (by removing host)' do
|
|
@@ -29,153 +20,202 @@ describe ApiAuth::Headers do
|
|
|
29
20
|
end
|
|
30
21
|
|
|
31
22
|
context 'uri with host and /' do
|
|
32
|
-
let(:uri) { '
|
|
23
|
+
let(:uri) { 'https://google.com/'.freeze }
|
|
33
24
|
|
|
34
25
|
it 'return / as canonical string path' do
|
|
35
|
-
expect(subject.canonical_string).to eq('
|
|
26
|
+
expect(subject.canonical_string).to eq('GET,,,/,')
|
|
36
27
|
end
|
|
37
28
|
|
|
38
29
|
it 'does not change request url (by removing host)' do
|
|
39
30
|
expect(request.url).to eq(uri)
|
|
40
31
|
end
|
|
41
32
|
end
|
|
42
|
-
end
|
|
43
33
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
expect(headers.canonical_string).to eq "text/html,12345,/foo,Mon, 23 Jan 1984 03:29:56 GMT"
|
|
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
|
+
|
|
37
|
+
it 'return /?redirect_to=https://www.example.com as canonical string path' do
|
|
38
|
+
expect(subject.canonical_string).to eq('GET,,,/?redirect_to=https://www.example.com,')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'does not change request url (by removing host)' do
|
|
42
|
+
expect(request.url).to eq(uri)
|
|
43
|
+
end
|
|
55
44
|
end
|
|
56
45
|
end
|
|
57
|
-
end
|
|
58
46
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
47
|
+
context 'string construction' do
|
|
48
|
+
context 'with a driver that supplies http_method' do
|
|
49
|
+
let(:request) { RestClient::Request.new(url: 'https://google.com', method: :get) }
|
|
50
|
+
subject(:headers) { described_class.new(request) }
|
|
51
|
+
let(:driver) { headers.instance_variable_get('@request') }
|
|
52
|
+
|
|
53
|
+
before do
|
|
54
|
+
allow(driver).to receive(:http_method).and_return 'GET'
|
|
55
|
+
allow(driver).to receive(:content_type).and_return 'text/html'
|
|
56
|
+
allow(driver).to receive(:content_hash).and_return '12345'
|
|
57
|
+
allow(driver).to receive(:request_uri).and_return '/foo'
|
|
58
|
+
allow(driver).to receive(:timestamp).and_return 'Mon, 23 Jan 1984 03:29:56 GMT'
|
|
59
|
+
end
|
|
72
60
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
61
|
+
context 'when not passed an override' do
|
|
62
|
+
it "constructs the canonical_string with the driver's http method" do
|
|
63
|
+
expect(headers.canonical_string).to eq 'GET,text/html,12345,/foo,Mon, 23 Jan 1984 03:29:56 GMT'
|
|
64
|
+
end
|
|
76
65
|
end
|
|
77
|
-
end
|
|
78
66
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
67
|
+
context 'when passed an override' do
|
|
68
|
+
it 'constructs the canonical_string with the overridden http method' do
|
|
69
|
+
expect(headers.canonical_string('put')).to eq 'PUT,text/html,12345,/foo,Mon, 23 Jan 1984 03:29:56 GMT'
|
|
70
|
+
end
|
|
82
71
|
end
|
|
83
72
|
end
|
|
84
|
-
end
|
|
85
73
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
74
|
+
context "when a driver that doesn't supply http_method" do
|
|
75
|
+
let(:request) do
|
|
76
|
+
Curl::Easy.new('/resource.xml?foo=bar&bar=foo') do |curl|
|
|
77
|
+
curl.headers = { 'Content-Type' => 'text/plain' }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
subject(:headers) { described_class.new(request) }
|
|
81
|
+
let(:driver) { headers.instance_variable_get('@request') }
|
|
82
|
+
|
|
83
|
+
before do
|
|
84
|
+
allow(driver).to receive(:http_method).and_return nil
|
|
85
|
+
allow(driver).to receive(:content_type).and_return 'text/html'
|
|
86
|
+
allow(driver).to receive(:content_hash).and_return '12345'
|
|
87
|
+
allow(driver).to receive(:request_uri).and_return '/foo'
|
|
88
|
+
allow(driver).to receive(:timestamp).and_return 'Mon, 23 Jan 1984 03:29:56 GMT'
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
context 'when not passed an override' do
|
|
92
|
+
it 'raises an error' do
|
|
93
|
+
expect { headers.canonical_string }.to raise_error(ArgumentError)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
context 'when passed an override' do
|
|
98
|
+
it 'constructs the canonical_string with the overridden http method' do
|
|
99
|
+
expect(headers.canonical_string('put')).to eq 'PUT,text/html,12345,/foo,Mon, 23 Jan 1984 03:29:56 GMT'
|
|
100
|
+
end
|
|
90
101
|
end
|
|
91
|
-
end
|
|
92
|
-
subject(:headers) { described_class.new(request) }
|
|
93
|
-
let(:driver){ headers.instance_variable_get("@request")}
|
|
94
|
-
|
|
95
|
-
before do
|
|
96
|
-
allow(driver).to receive(:http_method).and_return nil
|
|
97
|
-
allow(driver).to receive(:content_type).and_return "text/html"
|
|
98
|
-
allow(driver).to receive(:content_md5).and_return "12345"
|
|
99
|
-
allow(driver).to receive(:request_uri).and_return "/foo"
|
|
100
|
-
allow(driver).to receive(:timestamp).and_return "Mon, 23 Jan 1984 03:29:56 GMT"
|
|
101
102
|
end
|
|
102
103
|
|
|
103
|
-
context "when
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
context "when there's a proxy server (e.g. Nginx) with rewrite rules" do
|
|
105
|
+
let(:request) do
|
|
106
|
+
Faraday::Request.create('GET') do |req|
|
|
107
|
+
req.options = Faraday::RequestOptions.new(Faraday::FlatParamsEncoder)
|
|
108
|
+
req.params = Faraday::Utils::ParamsHash.new
|
|
109
|
+
req.url('/resource.xml?foo=bar&bar=foo')
|
|
110
|
+
req.headers = { 'X-Original-URI' => '/api/resource.xml?foo=bar&bar=foo' }
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
subject(:headers) { described_class.new(request) }
|
|
114
|
+
let(:driver) { headers.instance_variable_get('@request') }
|
|
115
|
+
|
|
116
|
+
before do
|
|
117
|
+
allow(driver).to receive(:content_type).and_return 'text/html'
|
|
118
|
+
allow(driver).to receive(:content_hash).and_return '12345'
|
|
119
|
+
allow(driver).to receive(:timestamp).and_return 'Mon, 23 Jan 1984 03:29:56 GMT'
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
context 'the driver uses the original_uri' do
|
|
123
|
+
it 'constructs the canonical_string with the original_uri' do
|
|
124
|
+
expect(headers.canonical_string).to eq 'GET,text/html,12345,/api/resource.xml?foo=bar&bar=foo,Mon, 23 Jan 1984 03:29:56 GMT'
|
|
125
|
+
end
|
|
106
126
|
end
|
|
107
127
|
end
|
|
108
128
|
|
|
109
|
-
context
|
|
110
|
-
|
|
111
|
-
|
|
129
|
+
context 'when headers to sign are provided' do
|
|
130
|
+
let(:request) do
|
|
131
|
+
Faraday::Request.create('GET') do |req|
|
|
132
|
+
req.options = Faraday::RequestOptions.new(Faraday::FlatParamsEncoder)
|
|
133
|
+
req.params = Faraday::Utils::ParamsHash.new
|
|
134
|
+
req.url('/resource.xml?foo=bar&bar=foo')
|
|
135
|
+
req.headers = { 'X-Forwarded-For' => '192.168.1.1' }
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
subject(:headers) { described_class.new(request) }
|
|
139
|
+
let(:driver) { headers.instance_variable_get('@request') }
|
|
140
|
+
|
|
141
|
+
before do
|
|
142
|
+
allow(driver).to receive(:content_type).and_return 'text/html'
|
|
143
|
+
allow(driver).to receive(:content_hash).and_return '12345'
|
|
144
|
+
allow(driver).to receive(:timestamp).and_return 'Mon, 23 Jan 1984 03:29:56 GMT'
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
context 'the driver uses the original_uri' do
|
|
148
|
+
it 'constructs the canonical_string with the original_uri' do
|
|
149
|
+
expect(headers.canonical_string(nil, %w[X-FORWARDED-FOR]))
|
|
150
|
+
.to eq 'GET,text/html,12345,/resource.xml?bar=foo&foo=bar,Mon, 23 Jan 1984 03:29:56 GMT,192.168.1.1'
|
|
151
|
+
end
|
|
112
152
|
end
|
|
113
153
|
end
|
|
114
154
|
end
|
|
115
155
|
end
|
|
116
156
|
|
|
117
|
-
describe '#
|
|
118
|
-
subject(:headers){ described_class.new(request) }
|
|
119
|
-
let(:driver){ headers.instance_variable_get(
|
|
157
|
+
describe '#calculate_hash' do
|
|
158
|
+
subject(:headers) { described_class.new(request) }
|
|
159
|
+
let(:driver) { headers.instance_variable_get('@request') }
|
|
120
160
|
|
|
121
|
-
context
|
|
122
|
-
let(:request)
|
|
161
|
+
context 'no content hash already calculated' do
|
|
162
|
+
let(:request) do
|
|
123
163
|
RestClient::Request.new(
|
|
124
|
-
:
|
|
125
|
-
:
|
|
126
|
-
:
|
|
164
|
+
url: 'https://google.com',
|
|
165
|
+
method: :post,
|
|
166
|
+
payload: "hello\nworld"
|
|
127
167
|
)
|
|
128
|
-
|
|
168
|
+
end
|
|
129
169
|
|
|
130
|
-
it
|
|
131
|
-
expect(driver).to receive(:
|
|
132
|
-
headers.
|
|
170
|
+
it 'populates the content hash header' do
|
|
171
|
+
expect(driver).to receive(:populate_content_hash)
|
|
172
|
+
headers.calculate_hash
|
|
133
173
|
end
|
|
134
174
|
end
|
|
135
175
|
|
|
136
|
-
context
|
|
137
|
-
let(:request)
|
|
176
|
+
context 'hash already calculated' do
|
|
177
|
+
let(:request) do
|
|
138
178
|
RestClient::Request.new(
|
|
139
|
-
:
|
|
140
|
-
:
|
|
141
|
-
:
|
|
142
|
-
:
|
|
179
|
+
url: 'https://google.com',
|
|
180
|
+
method: :post,
|
|
181
|
+
payload: "hello\nworld",
|
|
182
|
+
headers: { 'X-Authorization-Content-SHA256' => 'abcd' }
|
|
143
183
|
)
|
|
144
|
-
|
|
184
|
+
end
|
|
145
185
|
|
|
146
|
-
it "doesn't populate the
|
|
147
|
-
expect(driver).not_to receive(:
|
|
148
|
-
headers.
|
|
186
|
+
it "doesn't populate the X-Authorization-Content-SHA256 header" do
|
|
187
|
+
expect(driver).not_to receive(:populate_content_hash)
|
|
188
|
+
headers.calculate_hash
|
|
149
189
|
end
|
|
150
190
|
end
|
|
151
191
|
end
|
|
152
192
|
|
|
153
|
-
describe
|
|
154
|
-
let(:request){ RestClient::Request.new(:
|
|
155
|
-
subject(:headers){ described_class.new(request) }
|
|
156
|
-
let(:driver){ headers.instance_variable_get(
|
|
193
|
+
describe '#content_hash_mismatch?' do
|
|
194
|
+
let(:request) { RestClient::Request.new(url: 'https://google.com', method: :get) }
|
|
195
|
+
subject(:headers) { described_class.new(request) }
|
|
196
|
+
let(:driver) { headers.instance_variable_get('@request') }
|
|
157
197
|
|
|
158
|
-
context
|
|
159
|
-
it
|
|
160
|
-
allow(driver).to receive(:
|
|
198
|
+
context 'when request has X-Authorization-Content-SHA256 header' do
|
|
199
|
+
it 'asks the driver' do
|
|
200
|
+
allow(driver).to receive(:content_hash).and_return '1234'
|
|
161
201
|
|
|
162
|
-
expect(driver).to receive(:
|
|
163
|
-
headers.
|
|
202
|
+
expect(driver).to receive(:content_hash_mismatch?).and_call_original
|
|
203
|
+
headers.content_hash_mismatch?
|
|
164
204
|
end
|
|
165
205
|
end
|
|
166
206
|
|
|
167
|
-
context
|
|
207
|
+
context 'when request has no content hash' do
|
|
168
208
|
it "doesn't ask the driver" do
|
|
169
|
-
allow(driver).to receive(:
|
|
209
|
+
allow(driver).to receive(:content_hash).and_return nil
|
|
170
210
|
|
|
171
|
-
expect(driver).not_to receive(:
|
|
172
|
-
headers.
|
|
211
|
+
expect(driver).not_to receive(:content_hash_mismatch?).and_call_original
|
|
212
|
+
headers.content_hash_mismatch?
|
|
173
213
|
end
|
|
174
214
|
|
|
175
|
-
it
|
|
176
|
-
allow(driver).to receive(:
|
|
215
|
+
it 'returns false' do
|
|
216
|
+
allow(driver).to receive(:content_hash).and_return nil
|
|
177
217
|
|
|
178
|
-
expect(headers.
|
|
218
|
+
expect(headers.content_hash_mismatch?).to be false
|
|
179
219
|
end
|
|
180
220
|
end
|
|
181
221
|
end
|
data/spec/helpers_spec.rb
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
|
-
describe
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
expect(ApiAuth.b64_encode("some string")).not_to match(/\n/)
|
|
3
|
+
describe 'ApiAuth::Helpers' do
|
|
4
|
+
it 'should strip the new line character on a Base64 encoding' do
|
|
5
|
+
expect(ApiAuth.b64_encode('some string')).not_to match(/\n/)
|
|
7
6
|
end
|
|
8
|
-
|
|
7
|
+
|
|
9
8
|
it "should properly upcase a hash's keys" do
|
|
10
|
-
hsh = {
|
|
11
|
-
expect(ApiAuth.capitalize_keys(hsh)[
|
|
9
|
+
hsh = { 'JoE' => 'rOOLz' }
|
|
10
|
+
expect(ApiAuth.capitalize_keys(hsh)['JOE']).to eq('rOOLz')
|
|
12
11
|
end
|
|
13
|
-
|
|
14
|
-
end
|
|
12
|
+
end
|
data/spec/railtie_spec.rb
CHANGED
|
@@ -1,159 +1,140 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'action_controller/test_case'
|
|
2
3
|
|
|
3
|
-
describe
|
|
4
|
-
|
|
5
|
-
API_KEY_STORE = { "1044" => "l16imAXie1sRMcJODpOG7UwC1VyoqvO13jejkfpKWX4Z09W8DC9IrU23DvCwMry7pgSFW6c5S1GIfV0OY6F/vUA==" }
|
|
6
|
-
|
|
7
|
-
describe "Rails controller integration" do
|
|
4
|
+
describe 'Rails integration' do
|
|
5
|
+
API_KEY_STORE = { '1044' => 'l16imAXie1sRMcJODpOG7UwC1VyoqvO13jejkfpKWX4Z09W8DC9IrU23DvCwMry7pgSFW6c5S1GIfV0OY6F/vUA==' }.freeze
|
|
8
6
|
|
|
7
|
+
describe 'Rails controller integration' do
|
|
9
8
|
class ApplicationController < ActionController::Base
|
|
10
|
-
|
|
11
|
-
private
|
|
9
|
+
private
|
|
12
10
|
|
|
13
11
|
def require_api_auth
|
|
14
|
-
if (access_id = get_api_access_id_from_request)
|
|
15
|
-
return true
|
|
12
|
+
if (access_id = get_api_access_id_from_request) && api_authenticated?(API_KEY_STORE[access_id])
|
|
13
|
+
return true
|
|
16
14
|
end
|
|
17
15
|
|
|
18
16
|
respond_to do |format|
|
|
19
|
-
format.xml { render :
|
|
20
|
-
format.json { render :
|
|
21
|
-
format.html { render :
|
|
17
|
+
format.xml { render xml: 'You are unauthorized to perform this action.', status: 401 }
|
|
18
|
+
format.json { render json: 'You are unauthorized to perform this action.', status: 401 }
|
|
19
|
+
format.html { render plain: 'You are unauthorized to perform this action', status: 401 }
|
|
22
20
|
end
|
|
23
21
|
end
|
|
24
|
-
|
|
25
22
|
end
|
|
26
23
|
|
|
27
24
|
class TestController < ApplicationController
|
|
28
|
-
|
|
25
|
+
before_action :require_api_auth, only: [:index]
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
ActionDispatch::Routing::RouteSet.new
|
|
33
|
-
end
|
|
27
|
+
def self._routes
|
|
28
|
+
ActionDispatch::Routing::RouteSet.new
|
|
34
29
|
end
|
|
35
30
|
|
|
36
31
|
def index
|
|
37
|
-
render :
|
|
32
|
+
render json: 'OK'
|
|
38
33
|
end
|
|
39
34
|
|
|
40
35
|
def public
|
|
41
|
-
render :
|
|
36
|
+
render json: 'OK'
|
|
42
37
|
end
|
|
43
38
|
|
|
44
|
-
def rescue_action(e)
|
|
39
|
+
def rescue_action(e)
|
|
40
|
+
raise(e)
|
|
41
|
+
end
|
|
45
42
|
end
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
def generated_response(request, action = :index)
|
|
45
|
+
response = ActionDispatch::TestResponse.new
|
|
46
|
+
controller = TestController.new
|
|
47
|
+
controller.request = request
|
|
48
|
+
controller.response = response
|
|
49
|
+
controller.process(action)
|
|
50
|
+
response
|
|
49
51
|
end
|
|
50
52
|
|
|
51
|
-
def
|
|
52
|
-
if
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
end
|
|
53
|
+
def generated_request
|
|
54
|
+
request = if ActionController::TestRequest.respond_to?(:create)
|
|
55
|
+
if Gem.loaded_specs['actionpack'].version < Gem::Version.new('5.1.0')
|
|
56
|
+
ActionController::TestRequest.create
|
|
57
|
+
else
|
|
58
|
+
ActionController::TestRequest.create(TestController)
|
|
59
|
+
end
|
|
60
|
+
else
|
|
61
|
+
ActionController::TestRequest.new
|
|
62
|
+
end
|
|
63
|
+
request.accept = ['application/json']
|
|
64
|
+
request
|
|
64
65
|
end
|
|
65
66
|
|
|
66
|
-
it
|
|
67
|
-
request =
|
|
67
|
+
it 'should permit a request with properly signed headers' do
|
|
68
|
+
request = generated_request
|
|
68
69
|
request.env['DATE'] = Time.now.utc.httpdate
|
|
69
|
-
ApiAuth.sign!(request,
|
|
70
|
+
ApiAuth.sign!(request, '1044', API_KEY_STORE['1044'])
|
|
71
|
+
response = generated_response(request, :index)
|
|
72
|
+
expect(response.code).to eq('200')
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'should forbid a request with properly signed headers but timestamp > 15 minutes ago' do
|
|
76
|
+
request = generated_request
|
|
77
|
+
request.env['DATE'] = 'Mon, 23 Jan 1984 03:29:56 GMT'
|
|
78
|
+
ApiAuth.sign!(request, '1044', API_KEY_STORE['1044'])
|
|
70
79
|
response = generated_response(request, :index)
|
|
71
|
-
expect(response.code).to eq(
|
|
80
|
+
expect(response.code).to eq('401')
|
|
72
81
|
end
|
|
73
82
|
|
|
74
|
-
it
|
|
75
|
-
request =
|
|
76
|
-
request.env['DATE'] =
|
|
77
|
-
ApiAuth.sign!(request,
|
|
83
|
+
it 'should forbid a request with properly signed headers but timestamp > 15 minutes in the future' do
|
|
84
|
+
request = generated_request
|
|
85
|
+
request.env['DATE'] = 'Mon, 23 Jan 2100 03:29:56 GMT'
|
|
86
|
+
ApiAuth.sign!(request, '1044', API_KEY_STORE['1044'])
|
|
78
87
|
response = generated_response(request, :index)
|
|
79
|
-
expect(response.code).to eq(
|
|
88
|
+
expect(response.code).to eq('401')
|
|
80
89
|
end
|
|
81
90
|
|
|
82
91
|
it "should insert a DATE header in the request when one hasn't been specified" do
|
|
83
|
-
request =
|
|
84
|
-
ApiAuth.sign!(request,
|
|
92
|
+
request = generated_request
|
|
93
|
+
ApiAuth.sign!(request, '1044', API_KEY_STORE['1044'])
|
|
85
94
|
expect(request.headers['DATE']).not_to be_nil
|
|
86
95
|
end
|
|
87
96
|
|
|
88
|
-
it
|
|
89
|
-
request =
|
|
97
|
+
it 'should forbid an unsigned request to a protected controller action' do
|
|
98
|
+
request = generated_request
|
|
90
99
|
response = generated_response(request, :index)
|
|
91
|
-
expect(response.code).to eq(
|
|
100
|
+
expect(response.code).to eq('401')
|
|
92
101
|
end
|
|
93
102
|
|
|
94
|
-
it
|
|
95
|
-
request =
|
|
96
|
-
request.env['Authorization'] =
|
|
103
|
+
it 'should forbid a request with a bogus signature' do
|
|
104
|
+
request = generated_request
|
|
105
|
+
request.env['Authorization'] = 'APIAuth bogus:bogus'
|
|
97
106
|
response = generated_response(request, :index)
|
|
98
|
-
expect(response.code).to eq(
|
|
107
|
+
expect(response.code).to eq('401')
|
|
99
108
|
end
|
|
100
109
|
|
|
101
|
-
it
|
|
102
|
-
request =
|
|
110
|
+
it 'should allow non-protected controller actions to function as before' do
|
|
111
|
+
request = generated_request
|
|
103
112
|
response = generated_response(request, :public)
|
|
104
|
-
expect(response.code).to eq(
|
|
113
|
+
expect(response.code).to eq('200')
|
|
105
114
|
end
|
|
106
|
-
|
|
107
115
|
end
|
|
108
116
|
|
|
109
|
-
describe
|
|
110
|
-
|
|
117
|
+
describe 'Rails ActiveResource integration' do
|
|
111
118
|
class TestResource < ActiveResource::Base
|
|
112
|
-
with_api_auth
|
|
113
|
-
self.site =
|
|
119
|
+
with_api_auth '1044', API_KEY_STORE['1044']
|
|
120
|
+
self.site = 'https://localhost/'
|
|
114
121
|
self.format = :xml
|
|
115
122
|
end
|
|
116
123
|
|
|
117
|
-
it
|
|
118
|
-
timestamp = Time.parse(
|
|
124
|
+
it 'should send signed requests automagically' do
|
|
125
|
+
timestamp = Time.parse('Mon, 23 Jan 1984 03:29:56 GMT')
|
|
119
126
|
allow(Time).to receive(:now).at_least(1).times.and_return(timestamp)
|
|
120
127
|
ActiveResource::HttpMock.respond_to do |mock|
|
|
121
|
-
mock.get
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
+
mock.get '/test_resources/1.xml',
|
|
129
|
+
{
|
|
130
|
+
'Authorization' => 'APIAuth 1044:LZ1jujf3x1nnGR70/208WPXdUHw=',
|
|
131
|
+
'Accept' => 'application/xml',
|
|
132
|
+
'DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT'
|
|
133
|
+
},
|
|
134
|
+
{ id: '1' }.to_xml(root: 'test_resource')
|
|
128
135
|
end
|
|
129
|
-
expect(ApiAuth).to receive(:sign!).with(anything,
|
|
136
|
+
expect(ApiAuth).to receive(:sign!).with(anything, '1044', API_KEY_STORE['1044'], {}).and_call_original
|
|
130
137
|
TestResource.find(1)
|
|
131
138
|
end
|
|
132
|
-
|
|
133
|
-
context "when telling it to include the http method in the signature" do
|
|
134
|
-
class TestResourceForHttpMethod < ActiveResource::Base
|
|
135
|
-
with_api_auth "1044", API_KEY_STORE["1044"], :sign_with_http_method => true
|
|
136
|
-
self.site = "http://localhost/"
|
|
137
|
-
self.format = :xml
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
it "signs with the http method" do
|
|
141
|
-
timestamp = Time.parse("Mon, 23 Jan 1984 03:29:56 GMT")
|
|
142
|
-
allow(Time).to receive(:now).at_least(1).times.and_return(timestamp)
|
|
143
|
-
ActiveResource::HttpMock.respond_to do |mock|
|
|
144
|
-
mock.get "/test_resource_for_http_methods/1.xml",
|
|
145
|
-
{
|
|
146
|
-
'Authorization' => 'APIAuth 1044:Gq190cxgKm7oWH1fc+y8+wmD9ts=',
|
|
147
|
-
'Accept' => 'application/xml',
|
|
148
|
-
'DATE' => "Mon, 23 Jan 1984 03:29:56 GMT"
|
|
149
|
-
},
|
|
150
|
-
{ :id => "1" }.to_xml(:root => 'test_resource')
|
|
151
|
-
end
|
|
152
|
-
expect(ApiAuth).to receive(:sign!).with(anything, "1044", API_KEY_STORE["1044"], { :with_http_method => true }).and_call_original
|
|
153
|
-
TestResourceForHttpMethod.find(1)
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
|
|
157
139
|
end
|
|
158
|
-
|
|
159
140
|
end
|