drillbit 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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