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.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/LICENSE.txt +19 -0
  5. data/README.md +2 -0
  6. data/Rakefile +2 -0
  7. data/lib/drillbit.rb +19 -0
  8. data/lib/drillbit/accept_header.rb +50 -0
  9. data/lib/drillbit/authorizable_resource.rb +160 -0
  10. data/lib/drillbit/authorizers/parameters.rb +24 -0
  11. data/lib/drillbit/authorizers/parameters/filtering.rb +50 -0
  12. data/lib/drillbit/authorizers/parameters/resource.rb +11 -0
  13. data/lib/drillbit/authorizers/query.rb +40 -0
  14. data/lib/drillbit/authorizers/scope.rb +30 -0
  15. data/lib/drillbit/configuration.rb +36 -0
  16. data/lib/drillbit/errors/invalid_api_request.rb +29 -0
  17. data/lib/drillbit/errors/invalid_subdomain.rb +29 -0
  18. data/lib/drillbit/errors/invalid_token.rb +22 -0
  19. data/lib/drillbit/matchers/accept_header.rb +16 -0
  20. data/lib/drillbit/matchers/generic.rb +30 -0
  21. data/lib/drillbit/matchers/subdomain.rb +31 -0
  22. data/lib/drillbit/matchers/version.rb +30 -0
  23. data/lib/drillbit/middleware/api_request.rb +49 -0
  24. data/lib/drillbit/parameters.rb +22 -0
  25. data/lib/drillbit/parameters/filter.rb +57 -0
  26. data/lib/drillbit/parameters/index.rb +31 -0
  27. data/lib/drillbit/parameters/page.rb +28 -0
  28. data/lib/drillbit/parameters/sort.rb +32 -0
  29. data/lib/drillbit/requests/base.rb +114 -0
  30. data/lib/drillbit/requests/rack.rb +50 -0
  31. data/lib/drillbit/requests/rails.rb +44 -0
  32. data/lib/drillbit/resource.rb +14 -0
  33. data/lib/drillbit/resource/model.rb +41 -0
  34. data/lib/drillbit/resource/naming.rb +33 -0
  35. data/lib/drillbit/resource/processors/filtering.rb +66 -0
  36. data/lib/drillbit/resource/processors/indexing.rb +40 -0
  37. data/lib/drillbit/resource/processors/paging.rb +46 -0
  38. data/lib/drillbit/resource/processors/sorting.rb +42 -0
  39. data/lib/drillbit/responses/invalid_api_request.rb +18 -0
  40. data/lib/drillbit/responses/invalid_subdomain.rb +18 -0
  41. data/lib/drillbit/responses/invalid_token.rb +20 -0
  42. data/lib/drillbit/serializers/json_api.rb +10 -0
  43. data/lib/drillbit/tokens/base64.rb +45 -0
  44. data/lib/drillbit/tokens/base64s/invalid.rb +14 -0
  45. data/lib/drillbit/tokens/base64s/null.rb +14 -0
  46. data/lib/drillbit/tokens/invalid.rb +26 -0
  47. data/lib/drillbit/tokens/json_web_token.rb +112 -0
  48. data/lib/drillbit/tokens/json_web_tokens/invalid.rb +14 -0
  49. data/lib/drillbit/tokens/json_web_tokens/null.rb +14 -0
  50. data/lib/drillbit/tokens/null.rb +26 -0
  51. data/lib/drillbit/version.rb +4 -0
  52. data/spec/drillbit/accept_header_spec.rb +112 -0
  53. data/spec/drillbit/authorizers/parameters/filtering_spec.rb +71 -0
  54. data/spec/drillbit/authorizers/parameters/resource_spec.rb +12 -0
  55. data/spec/drillbit/authorizers/parameters_spec.rb +17 -0
  56. data/spec/drillbit/authorizers/query_spec.rb +21 -0
  57. data/spec/drillbit/authorizers/scope_spec.rb +20 -0
  58. data/spec/drillbit/errors/invalid_api_request_spec.rb +31 -0
  59. data/spec/drillbit/errors/invalid_subdomain_spec.rb +31 -0
  60. data/spec/drillbit/errors/invalid_token_spec.rb +24 -0
  61. data/spec/drillbit/invalid_subdomain_spec.rb +46 -0
  62. data/spec/drillbit/invalid_token_spec.rb +44 -0
  63. data/spec/drillbit/matchers/accept_header_spec.rb +114 -0
  64. data/spec/drillbit/matchers/subdomain_spec.rb +78 -0
  65. data/spec/drillbit/matchers/version_spec.rb +86 -0
  66. data/spec/drillbit/middleware/api_request_spec.rb +220 -0
  67. data/spec/drillbit/parameters_spec.rb +49 -0
  68. data/spec/drillbit/requests/base_spec.rb +37 -0
  69. data/spec/drillbit/requests/rack_spec.rb +253 -0
  70. data/spec/drillbit/requests/rails_spec.rb +264 -0
  71. data/spec/drillbit/resource/model_spec.rb +64 -0
  72. data/spec/drillbit/resource/processors/filtering_spec.rb +106 -0
  73. data/spec/drillbit/resource/processors/indexing_spec.rb +46 -0
  74. data/spec/drillbit/resource/processors/paging_spec.rb +74 -0
  75. data/spec/drillbit/resource/processors/sorting_spec.rb +66 -0
  76. data/spec/drillbit/tokens/base64_spec.rb +44 -0
  77. data/spec/drillbit/tokens/json_web_token_spec.rb +135 -0
  78. data/spec/fixtures/test_rsa_key +27 -0
  79. data/spec/fixtures/test_rsa_key.pub +9 -0
  80. data/spec/spec_helper.rb +4 -0
  81. data/spec/support/private_keys.rb +42 -0
  82. metadata +244 -0
  83. 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