drillbit 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/LICENSE.txt +19 -0
- data/README.md +2 -0
- data/Rakefile +2 -0
- data/lib/drillbit.rb +19 -0
- data/lib/drillbit/accept_header.rb +50 -0
- data/lib/drillbit/authorizable_resource.rb +160 -0
- data/lib/drillbit/authorizers/parameters.rb +24 -0
- data/lib/drillbit/authorizers/parameters/filtering.rb +50 -0
- data/lib/drillbit/authorizers/parameters/resource.rb +11 -0
- data/lib/drillbit/authorizers/query.rb +40 -0
- data/lib/drillbit/authorizers/scope.rb +30 -0
- data/lib/drillbit/configuration.rb +36 -0
- data/lib/drillbit/errors/invalid_api_request.rb +29 -0
- data/lib/drillbit/errors/invalid_subdomain.rb +29 -0
- data/lib/drillbit/errors/invalid_token.rb +22 -0
- data/lib/drillbit/matchers/accept_header.rb +16 -0
- data/lib/drillbit/matchers/generic.rb +30 -0
- data/lib/drillbit/matchers/subdomain.rb +31 -0
- data/lib/drillbit/matchers/version.rb +30 -0
- data/lib/drillbit/middleware/api_request.rb +49 -0
- data/lib/drillbit/parameters.rb +22 -0
- data/lib/drillbit/parameters/filter.rb +57 -0
- data/lib/drillbit/parameters/index.rb +31 -0
- data/lib/drillbit/parameters/page.rb +28 -0
- data/lib/drillbit/parameters/sort.rb +32 -0
- data/lib/drillbit/requests/base.rb +114 -0
- data/lib/drillbit/requests/rack.rb +50 -0
- data/lib/drillbit/requests/rails.rb +44 -0
- data/lib/drillbit/resource.rb +14 -0
- data/lib/drillbit/resource/model.rb +41 -0
- data/lib/drillbit/resource/naming.rb +33 -0
- data/lib/drillbit/resource/processors/filtering.rb +66 -0
- data/lib/drillbit/resource/processors/indexing.rb +40 -0
- data/lib/drillbit/resource/processors/paging.rb +46 -0
- data/lib/drillbit/resource/processors/sorting.rb +42 -0
- data/lib/drillbit/responses/invalid_api_request.rb +18 -0
- data/lib/drillbit/responses/invalid_subdomain.rb +18 -0
- data/lib/drillbit/responses/invalid_token.rb +20 -0
- data/lib/drillbit/serializers/json_api.rb +10 -0
- data/lib/drillbit/tokens/base64.rb +45 -0
- data/lib/drillbit/tokens/base64s/invalid.rb +14 -0
- data/lib/drillbit/tokens/base64s/null.rb +14 -0
- data/lib/drillbit/tokens/invalid.rb +26 -0
- data/lib/drillbit/tokens/json_web_token.rb +112 -0
- data/lib/drillbit/tokens/json_web_tokens/invalid.rb +14 -0
- data/lib/drillbit/tokens/json_web_tokens/null.rb +14 -0
- data/lib/drillbit/tokens/null.rb +26 -0
- data/lib/drillbit/version.rb +4 -0
- data/spec/drillbit/accept_header_spec.rb +112 -0
- data/spec/drillbit/authorizers/parameters/filtering_spec.rb +71 -0
- data/spec/drillbit/authorizers/parameters/resource_spec.rb +12 -0
- data/spec/drillbit/authorizers/parameters_spec.rb +17 -0
- data/spec/drillbit/authorizers/query_spec.rb +21 -0
- data/spec/drillbit/authorizers/scope_spec.rb +20 -0
- data/spec/drillbit/errors/invalid_api_request_spec.rb +31 -0
- data/spec/drillbit/errors/invalid_subdomain_spec.rb +31 -0
- data/spec/drillbit/errors/invalid_token_spec.rb +24 -0
- data/spec/drillbit/invalid_subdomain_spec.rb +46 -0
- data/spec/drillbit/invalid_token_spec.rb +44 -0
- data/spec/drillbit/matchers/accept_header_spec.rb +114 -0
- data/spec/drillbit/matchers/subdomain_spec.rb +78 -0
- data/spec/drillbit/matchers/version_spec.rb +86 -0
- data/spec/drillbit/middleware/api_request_spec.rb +220 -0
- data/spec/drillbit/parameters_spec.rb +49 -0
- data/spec/drillbit/requests/base_spec.rb +37 -0
- data/spec/drillbit/requests/rack_spec.rb +253 -0
- data/spec/drillbit/requests/rails_spec.rb +264 -0
- data/spec/drillbit/resource/model_spec.rb +64 -0
- data/spec/drillbit/resource/processors/filtering_spec.rb +106 -0
- data/spec/drillbit/resource/processors/indexing_spec.rb +46 -0
- data/spec/drillbit/resource/processors/paging_spec.rb +74 -0
- data/spec/drillbit/resource/processors/sorting_spec.rb +66 -0
- data/spec/drillbit/tokens/base64_spec.rb +44 -0
- data/spec/drillbit/tokens/json_web_token_spec.rb +135 -0
- data/spec/fixtures/test_rsa_key +27 -0
- data/spec/fixtures/test_rsa_key.pub +9 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/private_keys.rb +42 -0
- metadata +244 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'drillbit/requests/base'
|
4
|
+
require 'drillbit/matchers/version'
|
5
|
+
|
6
|
+
module Drillbit
|
7
|
+
module Matchers
|
8
|
+
RSpec.describe Version do
|
9
|
+
context 'when the version is passed in the accept header' do
|
10
|
+
it 'does not match if the subdomain is API but the requested version does not ' \
|
11
|
+
'equal the version constraint' do
|
12
|
+
|
13
|
+
env = {
|
14
|
+
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
15
|
+
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=10.0',
|
16
|
+
}
|
17
|
+
request = Requests::Base.resolve(env)
|
18
|
+
|
19
|
+
matcher = Version.new(version_constraint: '10.1')
|
20
|
+
|
21
|
+
expect(matcher.matches?(request)).to be_a FalseClass
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'does match if the subdomain is API and the requested version equals the ' \
|
25
|
+
'version constraint' do
|
26
|
+
|
27
|
+
env = {
|
28
|
+
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
29
|
+
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=10.0',
|
30
|
+
}
|
31
|
+
request = Requests::Base.resolve(env)
|
32
|
+
|
33
|
+
matcher = Version.new(version_constraint: '10.0')
|
34
|
+
|
35
|
+
expect(matcher.matches?(request)).to be_a TrueClass
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when the version is not passed in the accept header' do
|
40
|
+
it 'does not match if the subdomain is API but the requested version does not ' \
|
41
|
+
'equal the version constraint' do
|
42
|
+
|
43
|
+
env = {
|
44
|
+
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
45
|
+
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep',
|
46
|
+
}
|
47
|
+
request = Requests::Base.resolve(env)
|
48
|
+
|
49
|
+
matcher = Version.new(version_constraint: '10.1',
|
50
|
+
default_version: '10.0')
|
51
|
+
|
52
|
+
expect(matcher.matches?(request)).to be_a FalseClass
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'does match if the subdomain is API and the requested version equals the ' \
|
56
|
+
'version constraint' do
|
57
|
+
|
58
|
+
env = {
|
59
|
+
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
60
|
+
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep',
|
61
|
+
}
|
62
|
+
request = Requests::Base.resolve(env)
|
63
|
+
|
64
|
+
matcher = Version.new(version_constraint: '10.0',
|
65
|
+
default_version: '10.0')
|
66
|
+
|
67
|
+
expect(matcher.matches?(request)).to be_a TrueClass
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'matches the default version in the configuration if none is passed in' do
|
72
|
+
Drillbit.configuration.default_api_version = '100.0'
|
73
|
+
|
74
|
+
env = {
|
75
|
+
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
76
|
+
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep',
|
77
|
+
}
|
78
|
+
request = Requests::Base.resolve(env)
|
79
|
+
|
80
|
+
matcher = Version.new(version_constraint: '100.0')
|
81
|
+
|
82
|
+
expect(matcher.matches?(request)).to be_a TrueClass
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'drillbit/middleware/api_request'
|
4
|
+
|
5
|
+
# rubocop:disable Metrics/LineLength
|
6
|
+
module Drillbit
|
7
|
+
module Middleware
|
8
|
+
RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
9
|
+
let(:app) { ->(_env) { [200, {}, 'response'] } }
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
Erratum.configuration.url_mappings = {
|
13
|
+
'external_documentation_urls' => {
|
14
|
+
'errors.responses.invalid_subdomain' => 'http://example.com/foo',
|
15
|
+
},
|
16
|
+
'developer_documentation_urls' => {
|
17
|
+
'errors.responses.invalid_subdomain' => 'http://example.com/foo',
|
18
|
+
},
|
19
|
+
}
|
20
|
+
|
21
|
+
Drillbit.configure do |config|
|
22
|
+
config.allowed_subdomains = %w{api westeros}
|
23
|
+
config.allowed_api_subdomains = %w{api}
|
24
|
+
config.application_name = 'westeros'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'allows requests for allowed subdomains without accept headers' do
|
29
|
+
api_request_middleware = ApiRequest.new(app)
|
30
|
+
|
31
|
+
request = {
|
32
|
+
'HTTP_HOST' => 'westeros.example.com',
|
33
|
+
'HTTP_ACCEPT' => '',
|
34
|
+
'QUERY_STRING' => '',
|
35
|
+
}
|
36
|
+
|
37
|
+
status, headers, response = api_request_middleware.call(request)
|
38
|
+
|
39
|
+
expect(status).to eql 200
|
40
|
+
expect(headers).to eql({})
|
41
|
+
expect(response).to eql 'response'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'does not allow requests if they are not for an allowed subdomain' do
|
45
|
+
api_request_middleware = ApiRequest.new(app)
|
46
|
+
|
47
|
+
request = {
|
48
|
+
'HTTP_HOST' => 'notvalid.example.com',
|
49
|
+
'HTTP_ACCEPT' => '',
|
50
|
+
'QUERY_STRING' => 'first=my_param&accept=application/vnd.silent+redkeep;version=1.0.0',
|
51
|
+
}
|
52
|
+
|
53
|
+
status, headers, response = api_request_middleware.call(request)
|
54
|
+
|
55
|
+
expect(status).to eql 404
|
56
|
+
expect(headers).to eql({})
|
57
|
+
expect(JSON.load(response[0])).to include(
|
58
|
+
'errors' => [
|
59
|
+
{
|
60
|
+
'id' => match(/[a-z0-9\-]+/),
|
61
|
+
'links' => {
|
62
|
+
'about' => nil,
|
63
|
+
'documentation' => nil,
|
64
|
+
},
|
65
|
+
'status' => 404,
|
66
|
+
'code' => 'errors.invalid_subdomain',
|
67
|
+
'title' => 'Invalid Subdomain',
|
68
|
+
'detail' => 'The resource you attempted to access is either not authorized ' \
|
69
|
+
'for the authenticated user or does not exist.',
|
70
|
+
'source' => {
|
71
|
+
'http_host' => 'notvalid.example.com',
|
72
|
+
},
|
73
|
+
},
|
74
|
+
],
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'does not allow requests if they are for an allowed subdomain but does ' \
|
79
|
+
'not have a valid accept header' do
|
80
|
+
|
81
|
+
api_request_middleware = ApiRequest.new(app)
|
82
|
+
|
83
|
+
request = {
|
84
|
+
'HTTP_HOST' => 'api.example.com',
|
85
|
+
'HTTP_ACCEPT' => '',
|
86
|
+
'QUERY_STRING' => 'first=my_param&accept=application/vnd.silent+redkeep;version=1.0.0',
|
87
|
+
}
|
88
|
+
|
89
|
+
status, headers, response = api_request_middleware.call(request)
|
90
|
+
|
91
|
+
expect(status).to eql 400
|
92
|
+
expect(headers).to eql({})
|
93
|
+
expect(JSON.load(response[0])).to include(
|
94
|
+
'errors' => [
|
95
|
+
{
|
96
|
+
'id' => match(/[a-z0-9\-]+/),
|
97
|
+
'links' => {
|
98
|
+
'about' => nil,
|
99
|
+
'documentation' => nil,
|
100
|
+
},
|
101
|
+
'status' => 400,
|
102
|
+
'code' => 'errors.invalid_api_request',
|
103
|
+
'title' => 'Invalid API Request',
|
104
|
+
'detail' => 'The accept header that you passed in the request cannot be ' \
|
105
|
+
'parsed, please refer to the documentation to verify.',
|
106
|
+
'source' => {
|
107
|
+
'accept_header' => '',
|
108
|
+
},
|
109
|
+
},
|
110
|
+
],
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'does allow requests if both the subdomain and the accept header are valid' do
|
115
|
+
api_request_middleware = ApiRequest.new(app)
|
116
|
+
|
117
|
+
request = {
|
118
|
+
'HTTP_HOST' => 'api.example.com',
|
119
|
+
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
120
|
+
'QUERY_STRING' => 'first=my_param&accept=application/vnd.westeros+redkeep;version=1.0.0',
|
121
|
+
}
|
122
|
+
|
123
|
+
status, headers, response = api_request_middleware.call(request)
|
124
|
+
|
125
|
+
expect(status).to eql 200
|
126
|
+
expect(headers).to eql({})
|
127
|
+
expect(response).to eql 'response'
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'does allow requests if the subdomain, the accept header and the token are valid' do
|
131
|
+
Drillbit.configuration.token_private_key = test_private_key
|
132
|
+
api_request_middleware = ApiRequest.new(app)
|
133
|
+
|
134
|
+
request = {
|
135
|
+
'HTTP_HOST' => 'api.example.com',
|
136
|
+
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
137
|
+
'HTTP_AUTHORIZATION' => "Token #{valid_jwe_token}",
|
138
|
+
'QUERY_STRING' => 'accept=application/vnd.westeros+redkeep;version=1.0.0',
|
139
|
+
}
|
140
|
+
|
141
|
+
status, headers, response = api_request_middleware.call(request)
|
142
|
+
|
143
|
+
expect(status).to eql 200
|
144
|
+
expect(headers).to eql({})
|
145
|
+
expect(response).to eql 'response'
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'returns the proper response if the token is invalid' do
|
149
|
+
Drillbit.configuration.token_private_key = test_private_key
|
150
|
+
api_request_middleware = ApiRequest.new(app)
|
151
|
+
|
152
|
+
request = {
|
153
|
+
'HTTP_HOST' => 'api.example.com',
|
154
|
+
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
155
|
+
'HTTP_AUTHORIZATION' => "Token #{invalid_jwe_token}",
|
156
|
+
'QUERY_STRING' => 'accept=application/vnd.westeros+redkeep;version=1.0.0',
|
157
|
+
}
|
158
|
+
|
159
|
+
_status, _headers, response = api_request_middleware.call(request)
|
160
|
+
|
161
|
+
expect(response.first).to include 'errors.invalid_token'
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'converts JSON API compliant dasherized query params to underscored' do
|
165
|
+
app = ->(env) { [200, env, 'response'] }
|
166
|
+
api_request_middleware = ApiRequest.new(app)
|
167
|
+
|
168
|
+
request = {
|
169
|
+
'HTTP_HOST' => 'api.example.com',
|
170
|
+
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
171
|
+
'QUERY_STRING' => 'hello-there=bob-jones&' \
|
172
|
+
'nice-to-meet=you-bob&' \
|
173
|
+
'hows-the-weather=today-bob',
|
174
|
+
}
|
175
|
+
|
176
|
+
_status, headers, _response = api_request_middleware.call(request)
|
177
|
+
|
178
|
+
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones&' \
|
179
|
+
'nice_to_meet=you-bob&' \
|
180
|
+
'hows_the_weather=today-bob'
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'properly converts the content type for Rails when it is the only one' do
|
184
|
+
api_request_middleware = ApiRequest.new(app)
|
185
|
+
|
186
|
+
request = {
|
187
|
+
'CONTENT_TYPE' => 'application/vnd.api+json',
|
188
|
+
'HTTP_HOST' => 'westeros.example.com',
|
189
|
+
'HTTP_ACCEPT' => '',
|
190
|
+
'QUERY_STRING' => '',
|
191
|
+
}
|
192
|
+
|
193
|
+
allow(app).to receive(:call)
|
194
|
+
|
195
|
+
_response = api_request_middleware.call(request)
|
196
|
+
|
197
|
+
expect(app).to have_received(:call).
|
198
|
+
with(a_hash_including('CONTENT_TYPE' => 'application/json'))
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'properly converts the content type for Rails when it is not the only one' do
|
202
|
+
api_request_middleware = ApiRequest.new(app)
|
203
|
+
|
204
|
+
request = {
|
205
|
+
'CONTENT_TYPE' => 'application/vnd.api+json;other',
|
206
|
+
'HTTP_HOST' => 'westeros.example.com',
|
207
|
+
'HTTP_ACCEPT' => '',
|
208
|
+
'QUERY_STRING' => '',
|
209
|
+
}
|
210
|
+
|
211
|
+
allow(app).to receive(:call)
|
212
|
+
|
213
|
+
_response = api_request_middleware.call(request)
|
214
|
+
|
215
|
+
expect(app).to have_received(:call).
|
216
|
+
with(a_hash_including('CONTENT_TYPE' => 'application/json;other'))
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'drillbit/parameters'
|
4
|
+
|
5
|
+
module Drillbit
|
6
|
+
RSpec.describe Parameters do
|
7
|
+
it 'can underscore the first parameter' do
|
8
|
+
query_params = 'hello-there=bob-jones'
|
9
|
+
|
10
|
+
expect(Parameters.process(query_params)).to eql 'hello_there=bob-jones'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'does not touch params with no dashes' do
|
14
|
+
query_params = 'hello_there=bob-jones'
|
15
|
+
|
16
|
+
expect(Parameters.process(query_params)).to eql 'hello_there=bob-jones'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'can underscore a middle parameter and a parameter at the end' do
|
20
|
+
query_params = 'hello-there=bob-jones&nice-to-meet=you-bob&hows-the-weather=today-bob'
|
21
|
+
|
22
|
+
expect(Parameters.process(query_params)).to eql 'hello_there=bob-jones&' \
|
23
|
+
'nice_to_meet=you-bob&' \
|
24
|
+
'hows_the_weather=today-bob'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'can handle weirdly formatted parameters' do
|
28
|
+
query_params = 'hello-there=bob-jones&nice-to-meet=you-bob&='
|
29
|
+
|
30
|
+
expect(Parameters.process(query_params)).to eql 'hello_there=bob-jones&' \
|
31
|
+
'nice_to_meet=you-bob&='
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'can handle parameters with no values' do
|
35
|
+
query_params = 'hello-there&nice-to-meet=you-bob&='
|
36
|
+
|
37
|
+
expect(Parameters.process(query_params)).to eql 'hello_there&' \
|
38
|
+
'nice_to_meet=you-bob&='
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'can handle values with no parameter name' do
|
42
|
+
query_params = 'hello-there=bob-jones&=you-bob&nice-to-meet=you-bob&='
|
43
|
+
|
44
|
+
expect(Parameters.process(query_params)).to eql 'hello_there=bob-jones&' \
|
45
|
+
'=you-bob&' \
|
46
|
+
'nice_to_meet=you-bob&='
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'ostruct'
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'drillbit/requests/base'
|
5
|
+
|
6
|
+
module Drillbit
|
7
|
+
module Requests
|
8
|
+
RSpec.describe Base do
|
9
|
+
it 'can resolve itself by returning itself' do
|
10
|
+
raw_request = Base.new(token_private_key: '', request: {})
|
11
|
+
resolved_request = Base.resolve(raw_request)
|
12
|
+
|
13
|
+
expect(resolved_request).to eql raw_request
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'can resolve a Rails request' do
|
17
|
+
raw_request = OpenStruct.new(
|
18
|
+
headers: {},
|
19
|
+
params: {},
|
20
|
+
)
|
21
|
+
resolved_request = Base.resolve(raw_request)
|
22
|
+
|
23
|
+
expect(resolved_request).to be_a Requests::Rails
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'can resolve a Rack request' do
|
27
|
+
raw_request = {
|
28
|
+
'HTTP_ACCEPT' => 'accept_string',
|
29
|
+
'QUERY_STRING' => '',
|
30
|
+
}
|
31
|
+
resolved_request = Base.resolve(raw_request)
|
32
|
+
|
33
|
+
expect(resolved_request).to be_a Requests::Rack
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,253 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'drillbit/requests/rack'
|
4
|
+
|
5
|
+
# rubocop:disable Metrics/LineLength
|
6
|
+
module Drillbit
|
7
|
+
module Requests
|
8
|
+
RSpec.describe Rack do
|
9
|
+
it 'finds the accept header from the headers if it is valid' do
|
10
|
+
raw_request = {
|
11
|
+
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=10.0',
|
12
|
+
'QUERY_STRING' => '',
|
13
|
+
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
14
|
+
}
|
15
|
+
request = Rack.new(request: raw_request)
|
16
|
+
|
17
|
+
expect(request.accept_header.to_s).to eql 'application/vnd.westeros+redkeep;version=10.0'
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'finds the accept header from the headers if it is invalid but there is no ' \
|
21
|
+
'accept header in the params' do
|
22
|
+
|
23
|
+
raw_request = {
|
24
|
+
'HTTP_ACCEPT' => 'invalid/vnd.westeros+redkeep;version=10.0',
|
25
|
+
'QUERY_STRING' => '',
|
26
|
+
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
27
|
+
}
|
28
|
+
request = Rack.new(request: raw_request)
|
29
|
+
|
30
|
+
expect(request.accept_header.to_s).to eql 'invalid/vnd.westeros+redkeep;version=10.0'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'finds the accept header from the params if it is valid' do
|
34
|
+
raw_request = {
|
35
|
+
'HTTP_ACCEPT' => '',
|
36
|
+
'QUERY_STRING' => 'accept=application/vnd.westeros+redkeep;version=10.0',
|
37
|
+
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
38
|
+
}
|
39
|
+
request = Rack.new(request: raw_request)
|
40
|
+
|
41
|
+
expect(request.accept_header.to_s).to eql 'application/vnd.westeros+redkeep;version=10.0'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'finds the accept header from the query string if it is encoded' do
|
45
|
+
raw_request = {
|
46
|
+
'HTTP_ACCEPT' => '',
|
47
|
+
'QUERY_STRING' => 'accept=application%2Fvnd.westeros%2Bredkeep%3Bversion%3D10.0',
|
48
|
+
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
49
|
+
}
|
50
|
+
request = Rack.new(request: raw_request)
|
51
|
+
|
52
|
+
expect(request.accept_header.to_s).to eql 'application/vnd.westeros+redkeep;version=10.0'
|
53
|
+
end
|
54
|
+
# rubocop:enable Metrics/LineLength
|
55
|
+
|
56
|
+
it 'finds the authorization token from the header' do
|
57
|
+
raw_request = {
|
58
|
+
'HTTP_AUTHORIZATION' => "Token #{valid_jwe_token}",
|
59
|
+
'QUERY_STRING' => '',
|
60
|
+
}
|
61
|
+
request = Rack.new(token_private_key: test_private_key,
|
62
|
+
request: raw_request)
|
63
|
+
|
64
|
+
expect(request.authorization_token).to be_valid
|
65
|
+
expect(request.authorization_token.to_h).to eql(
|
66
|
+
[
|
67
|
+
{ 'bar' => 'baz' },
|
68
|
+
{ 'typ' => 'JWT', 'alg' => 'RS256' },
|
69
|
+
],
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'finds the Base64 token from the header' do
|
74
|
+
raw_request = {
|
75
|
+
'HTTP_AUTHORIZATION' => "Basic #{valid_b64_token}",
|
76
|
+
'QUERY_STRING' => '',
|
77
|
+
}
|
78
|
+
request = Rack.new(token_private_key: test_private_key,
|
79
|
+
request: raw_request)
|
80
|
+
|
81
|
+
expect(request.authorization_token).to be_valid
|
82
|
+
expect(request.authorization_token.to_h).to eql(
|
83
|
+
[
|
84
|
+
{ 'token' => valid_b64_token },
|
85
|
+
{ 'typ' => 'base64' },
|
86
|
+
],
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'finds a null token from the header if there is no header' do
|
91
|
+
raw_request = {
|
92
|
+
'HTTP_AUTHORIZATION' => '',
|
93
|
+
'QUERY_STRING' => '',
|
94
|
+
}
|
95
|
+
request = Rack.new(token_private_key: test_private_key,
|
96
|
+
request: raw_request)
|
97
|
+
|
98
|
+
expect(request.authorization_token).to be_valid
|
99
|
+
expect(request.authorization_token).to be_blank
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'ignores incorrectly passed in tokens since we do not know what to do' do
|
103
|
+
raw_request = {
|
104
|
+
'HTTP_AUTHORIZATION' => valid_jwe_token.to_s,
|
105
|
+
'QUERY_STRING' => '',
|
106
|
+
}
|
107
|
+
request = Rack.new(token_private_key: test_private_key,
|
108
|
+
request: raw_request)
|
109
|
+
|
110
|
+
expect(request.authorization_token).to be_valid
|
111
|
+
expect(request.authorization_token).to be_blank
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'finds the authorization token from the params if the authorization token from ' \
|
115
|
+
'the header is invalid and the authorization token from the params is valid' do
|
116
|
+
|
117
|
+
raw_request = {
|
118
|
+
'HTTP_AUTHORIZATION' => "Token #{invalid_jwe_token}",
|
119
|
+
'QUERY_STRING' => "token_jwt=#{valid_jwe_token}",
|
120
|
+
}
|
121
|
+
request = Rack.new(token_private_key: test_private_key,
|
122
|
+
request: raw_request)
|
123
|
+
|
124
|
+
expect(request.authorization_token).to be_valid
|
125
|
+
expect(request.authorization_token.to_h).to eql(
|
126
|
+
[
|
127
|
+
{ 'bar' => 'baz' },
|
128
|
+
{ 'typ' => 'JWT', 'alg' => 'RS256' },
|
129
|
+
],
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'finds the authorization token from the params if the authorization token from ' \
|
134
|
+
'the header is not present and the authorization token from the params is valid' do
|
135
|
+
|
136
|
+
raw_request = {
|
137
|
+
'QUERY_STRING' => "token_jwt=#{valid_jwe_token}",
|
138
|
+
}
|
139
|
+
request = Rack.new(token_private_key: test_private_key,
|
140
|
+
request: raw_request)
|
141
|
+
|
142
|
+
expect(request.authorization_token).to be_valid
|
143
|
+
expect(request.authorization_token.to_h).to eql(
|
144
|
+
[
|
145
|
+
{ 'bar' => 'baz' },
|
146
|
+
{ 'typ' => 'JWT', 'alg' => 'RS256' },
|
147
|
+
],
|
148
|
+
)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'is a null authorization token if neither authorization token is present' do
|
152
|
+
raw_request = {
|
153
|
+
'QUERY_STRING' => '',
|
154
|
+
}
|
155
|
+
request = Rack.new(token_private_key: test_private_key,
|
156
|
+
request: raw_request)
|
157
|
+
|
158
|
+
expect(request.authorization_token).to be_valid
|
159
|
+
expect(request.authorization_token.to_h).to eql([{}, {}])
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'finds the JSON web token from the params' do
|
163
|
+
raw_request = {
|
164
|
+
'QUERY_STRING' => "token_jwt=#{valid_jwe_token}",
|
165
|
+
}
|
166
|
+
request = Rack.new(token_private_key: test_private_key,
|
167
|
+
request: raw_request)
|
168
|
+
|
169
|
+
expect(request.authorization_token).to be_valid
|
170
|
+
expect(request.authorization_token.to_h).to eql(
|
171
|
+
[
|
172
|
+
{ 'bar' => 'baz' },
|
173
|
+
{ 'typ' => 'JWT', 'alg' => 'RS256' },
|
174
|
+
],
|
175
|
+
)
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'finds the generic Base64 web token from the params' do
|
179
|
+
raw_request = {
|
180
|
+
'QUERY_STRING' => "token_b64=#{valid_b64_token}",
|
181
|
+
}
|
182
|
+
request = Rack.new(request: raw_request)
|
183
|
+
|
184
|
+
expect(request.authorization_token).to be_valid
|
185
|
+
expect(request.authorization_token.to_h).to eql(
|
186
|
+
[
|
187
|
+
{ 'token' => valid_b64_token },
|
188
|
+
{ 'typ' => 'base64' },
|
189
|
+
],
|
190
|
+
)
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'finds invalid tokens from the params' do
|
194
|
+
raw_request = {
|
195
|
+
'QUERY_STRING' => 'token_b64=bla.h',
|
196
|
+
}
|
197
|
+
request = Rack.new(request: raw_request)
|
198
|
+
|
199
|
+
expect(request.authorization_token_from_params).not_to be_valid
|
200
|
+
expect(request.authorization_token_from_params).not_to be_blank
|
201
|
+
|
202
|
+
raw_request = {
|
203
|
+
'QUERY_STRING' => "token_jwt=#{invalid_jwe_token}",
|
204
|
+
}
|
205
|
+
request = Rack.new(token_private_key: test_private_key,
|
206
|
+
request: raw_request)
|
207
|
+
|
208
|
+
expect(request.authorization_token_from_params).not_to be_valid
|
209
|
+
expect(request.authorization_token_from_params).not_to be_blank
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'finds the null token from the params if nothing is passed in' do
|
213
|
+
raw_request = {
|
214
|
+
'QUERY_STRING' => 'token_b64=',
|
215
|
+
}
|
216
|
+
request = Rack.new(request: raw_request)
|
217
|
+
|
218
|
+
expect(request.authorization_token_from_params).to be_valid
|
219
|
+
expect(request.authorization_token_from_params).to be_blank
|
220
|
+
|
221
|
+
raw_request = {
|
222
|
+
'QUERY_STRING' => 'token_jwt=',
|
223
|
+
}
|
224
|
+
request = Rack.new(request: raw_request)
|
225
|
+
|
226
|
+
expect(request.authorization_token_from_params).to be_valid
|
227
|
+
expect(request.authorization_token_from_params).to be_blank
|
228
|
+
|
229
|
+
raw_request = {
|
230
|
+
'QUERY_STRING' => '',
|
231
|
+
}
|
232
|
+
request = Rack.new(request: raw_request)
|
233
|
+
|
234
|
+
expect(request.authorization_token_from_params).to be_valid
|
235
|
+
expect(request.authorization_token_from_params).to be_blank
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'defaults to the application name in the configuration if none is found in ' \
|
239
|
+
'the header' do
|
240
|
+
|
241
|
+
Drillbit.configuration.application_name = 'redkeep'
|
242
|
+
|
243
|
+
raw_request = {
|
244
|
+
'HTTP_ACCEPT' => '',
|
245
|
+
'QUERY_STRING' => 'accept=application/vnd.redkeep+zion;version=10.0',
|
246
|
+
}
|
247
|
+
request = Rack.new(request: raw_request)
|
248
|
+
|
249
|
+
expect(request.accept_header.to_s).to eql 'application/vnd.redkeep+zion;version=10.0'
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|