drillbit 1.1.0 → 2.0.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/drillbit.rb +3 -1
- data/lib/drillbit/authorizable_resource.rb +6 -0
- data/lib/drillbit/authorizers/parameters/filtering.rb +1 -0
- data/lib/drillbit/authorizers/parameters/resource.rb +59 -0
- data/lib/drillbit/errors/invalid_request_body.rb +29 -0
- data/lib/drillbit/middleware/api_request_validator.rb +40 -0
- data/lib/drillbit/middleware/parameter_parser.rb +61 -0
- data/lib/drillbit/middleware/token_processor.rb +26 -0
- data/lib/drillbit/requests/base.rb +8 -5
- data/lib/drillbit/responses/invalid_request_body.rb +18 -0
- data/lib/drillbit/tokens/json_web_token.rb +9 -1
- data/lib/drillbit/version.rb +1 -1
- data/spec/drillbit/accept_header_spec.rb +2 -2
- data/spec/drillbit/authorizers/parameters/filtering_spec.rb +4 -4
- data/spec/drillbit/authorizers/parameters/resource_spec.rb +4 -4
- data/spec/drillbit/authorizers/parameters_spec.rb +3 -3
- data/spec/drillbit/authorizers/query_spec.rb +3 -3
- data/spec/drillbit/authorizers/scope_spec.rb +3 -3
- data/spec/drillbit/errors/invalid_api_request_spec.rb +3 -3
- data/spec/drillbit/errors/invalid_request_body_spec.rb +25 -0
- data/spec/drillbit/errors/invalid_subdomain_spec.rb +3 -3
- data/spec/drillbit/errors/invalid_token_spec.rb +3 -3
- data/spec/drillbit/invalid_subdomain_spec.rb +3 -3
- data/spec/drillbit/invalid_token_spec.rb +3 -3
- data/spec/drillbit/matchers/accept_header_spec.rb +3 -3
- data/spec/drillbit/matchers/subdomain_spec.rb +3 -3
- data/spec/drillbit/matchers/version_spec.rb +3 -3
- data/spec/drillbit/middleware/{api_request_spec.rb → api_request_validator_spec.rb} +11 -46
- data/spec/drillbit/middleware/parameter_parser_spec.rb +184 -0
- data/spec/drillbit/middleware/token_processor_spec.rb +27 -0
- data/spec/drillbit/requests/base_spec.rb +3 -3
- data/spec/drillbit/requests/rack_spec.rb +3 -3
- data/spec/drillbit/requests/rails_spec.rb +3 -3
- data/spec/drillbit/resource/model_spec.rb +3 -3
- data/spec/drillbit/resource/processors/filtering_spec.rb +4 -4
- data/spec/drillbit/resource/processors/indexing_spec.rb +4 -4
- data/spec/drillbit/resource/processors/paging_spec.rb +4 -4
- data/spec/drillbit/resource/processors/sorting_spec.rb +4 -4
- data/spec/drillbit/tokens/base64_spec.rb +3 -3
- data/spec/drillbit/tokens/json_web_token_spec.rb +11 -3
- data/spec/drillbit/tokens/json_web_tokens/password_reset_spec.rb +4 -4
- metadata +15 -8
- metadata.gz.sig +0 -0
- data/lib/drillbit/middleware/api_request.rb +0 -49
- data/lib/drillbit/parameters.rb +0 -22
- data/spec/drillbit/parameters_spec.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 362e3bda774e3c8a662b8dc640281e0ae389c953
|
4
|
+
data.tar.gz: ffa6d8b3eb8c83f52496a103378b994302d01fc2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b6d144f2c1b07894b0ed5ebf08e308720b882ed3892dea1e55f4a4a77a3dd348d933b56be2f9357b19bc651759380d9b52834274745e3651b76d3f32648ad2da
|
7
|
+
data.tar.gz: e0be2925d007e7771501b62fc28d2561445edd900ea584ccc6685c90de6b19cca7df02340e5342fbd7efd41bc0791d5bcc87c7d939e11b827ec0f339b6ff0405
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/lib/drillbit.rb
CHANGED
@@ -13,7 +13,9 @@ require 'drillbit/matchers/version'
|
|
13
13
|
require 'drillbit/resource'
|
14
14
|
require 'drillbit/serializers/json_api'
|
15
15
|
|
16
|
-
require 'drillbit/middleware/
|
16
|
+
require 'drillbit/middleware/api_request_validator'
|
17
|
+
require 'drillbit/middleware/token_processor'
|
18
|
+
require 'drillbit/middleware/parameter_parser'
|
17
19
|
|
18
20
|
require 'drillbit/responses/invalid_api_request'
|
19
21
|
require 'drillbit/responses/invalid_subdomain'
|
@@ -99,6 +99,12 @@ module AuthorizableResource
|
|
99
99
|
call
|
100
100
|
end
|
101
101
|
|
102
|
+
def authorized_attributes
|
103
|
+
@authorized_attributes ||= authorized_params.
|
104
|
+
fetch(:data, {}).
|
105
|
+
fetch(:attributes, {})
|
106
|
+
end
|
107
|
+
|
102
108
|
def authorized_resource
|
103
109
|
return nil if RESOURCE_COLLECTION_ACTIONS.include?(action_name)
|
104
110
|
|
@@ -1,10 +1,69 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'drillbit/authorizers/parameters'
|
3
3
|
|
4
|
+
# :reek:UnusedPrivateMethod
|
4
5
|
module Drillbit
|
5
6
|
module Authorizers
|
6
7
|
class Parameters
|
7
8
|
class Resource < Authorizers::Parameters
|
9
|
+
def call
|
10
|
+
params.permit(*authorized_params)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def authorized_params
|
16
|
+
@authorized_params ||= [
|
17
|
+
data: [
|
18
|
+
:type,
|
19
|
+
:id,
|
20
|
+
{
|
21
|
+
attributes: [
|
22
|
+
{},
|
23
|
+
],
|
24
|
+
relationships: [
|
25
|
+
{},
|
26
|
+
],
|
27
|
+
},
|
28
|
+
],
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_authorized_attribute(name)
|
33
|
+
param = params.fetch(:data, {}).
|
34
|
+
fetch(:attributes, {}).
|
35
|
+
fetch(name, nil)
|
36
|
+
|
37
|
+
if param.class == Array
|
38
|
+
authorized_params[0][:data][:attributes][0][name] = []
|
39
|
+
else
|
40
|
+
authorized_params[0][:data][2][:attributes] << name
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_authorized_attributes(*names)
|
45
|
+
names.each do |name|
|
46
|
+
add_authorized_attribute(name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_authorized_relationship(name)
|
51
|
+
param = params.fetch(:data, {}).
|
52
|
+
fetch(:relationships, {}).
|
53
|
+
fetch(name, nil)
|
54
|
+
|
55
|
+
if param.class == Array
|
56
|
+
authorized_params[0][:data][:relationships][0][name] = []
|
57
|
+
else
|
58
|
+
authorized_params[0][:data][2][:relationships] << name
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_authorized_relationships(*names)
|
63
|
+
names.each do |name|
|
64
|
+
add_authorized_relationship(name)
|
65
|
+
end
|
66
|
+
end
|
8
67
|
end
|
9
68
|
end
|
10
69
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'erratum'
|
3
|
+
|
4
|
+
module Drillbit
|
5
|
+
module Errors
|
6
|
+
class InvalidRequestBody < RuntimeError
|
7
|
+
include Erratum::Error
|
8
|
+
|
9
|
+
attr_accessor :request_body
|
10
|
+
|
11
|
+
def http_status
|
12
|
+
400
|
13
|
+
end
|
14
|
+
|
15
|
+
def title
|
16
|
+
'Invalid Request Body'
|
17
|
+
end
|
18
|
+
|
19
|
+
def detail
|
20
|
+
'The information you attempted to send in the request cannot be parsed as ' \
|
21
|
+
'a valid JSON document.'
|
22
|
+
end
|
23
|
+
|
24
|
+
def source
|
25
|
+
{ request_body: request_body }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'drillbit/configuration'
|
3
|
+
require 'drillbit/matchers/subdomain'
|
4
|
+
require 'drillbit/matchers/accept_header'
|
5
|
+
require 'drillbit/requests/base'
|
6
|
+
require 'drillbit/responses/invalid_api_request'
|
7
|
+
require 'drillbit/responses/invalid_subdomain'
|
8
|
+
|
9
|
+
module Drillbit
|
10
|
+
module Middleware
|
11
|
+
class ApiRequestValidator
|
12
|
+
JSON_API_MIME_TYPE_PATTERN = %r{application/vnd\.api\+json(?=\z|;)}
|
13
|
+
|
14
|
+
def initialize(app)
|
15
|
+
@app = app
|
16
|
+
end
|
17
|
+
|
18
|
+
# rubocop:disable Metrics/LineLength
|
19
|
+
# :reek:FeatureEnvy
|
20
|
+
def call(env)
|
21
|
+
env['HTTP_X_APPLICATION_NAME'] = Drillbit.configuration.application_name
|
22
|
+
|
23
|
+
request = Requests::Base.resolve(env)
|
24
|
+
subdomain_matcher = Matchers::Subdomain.new
|
25
|
+
accept_header_matcher = Matchers::AcceptHeader.new
|
26
|
+
|
27
|
+
return Responses::InvalidSubdomain.call(env) unless subdomain_matcher.matches?(request)
|
28
|
+
return Responses::InvalidApiRequest.call(env) if subdomain_matcher.matches_api_subdomain?(request) &&
|
29
|
+
!accept_header_matcher.matches?(request)
|
30
|
+
|
31
|
+
env['CONTENT_TYPE'] = env['CONTENT_TYPE'].
|
32
|
+
to_s.
|
33
|
+
gsub(JSON_API_MIME_TYPE_PATTERN, 'application/json')
|
34
|
+
|
35
|
+
@app.call(env)
|
36
|
+
end
|
37
|
+
# rubocop:enable Metrics/LineLength
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'json'
|
3
|
+
require 'stringio'
|
4
|
+
require 'drillbit/responses/invalid_request_body'
|
5
|
+
|
6
|
+
module Drillbit
|
7
|
+
module Middleware
|
8
|
+
class ParameterParser
|
9
|
+
def initialize(app)
|
10
|
+
@app = app
|
11
|
+
end
|
12
|
+
|
13
|
+
# :reek:FeatureEnvy
|
14
|
+
def call(env)
|
15
|
+
env['QUERY_STRING'] = underscore_query_string(env['QUERY_STRING'])
|
16
|
+
|
17
|
+
if env['CONTENT_LENGTH'].to_i > 0 && env['CONTENT_TYPE'] =~ /json/
|
18
|
+
if env['rack.input']
|
19
|
+
env['rack.input'] = StringIO.new(underscore_request_parameters(env['rack.input']))
|
20
|
+
elsif env['RACK_INPUT']
|
21
|
+
env['RACK_INPUT'] = underscore_request_parameters(env['RACK_INPUT'])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
@app.call(env)
|
26
|
+
rescue JSON::ParserError => _error
|
27
|
+
return Responses::InvalidRequestBody.call(env)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def underscore_request_parameters(request_parameters)
|
33
|
+
data = JSON.load(request_parameters)
|
34
|
+
data = underscore_parameters(data)
|
35
|
+
|
36
|
+
JSON.dump(data)
|
37
|
+
end
|
38
|
+
|
39
|
+
# :reek:UtilityFunction
|
40
|
+
def underscore_query_string(query_string)
|
41
|
+
return query_string unless query_string.respond_to? :gsub
|
42
|
+
|
43
|
+
query_string.gsub(/(?<=\A|&|\?)[^=&]+/) do |match|
|
44
|
+
match.tr('-', '_')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# :reek:UtilityFunction
|
49
|
+
# :reek:FeatureEnvy
|
50
|
+
def underscore_parameters(parameters)
|
51
|
+
return parameters unless parameters.is_a? Hash
|
52
|
+
|
53
|
+
parameters.each_with_object({}) do |(key, value), hash|
|
54
|
+
value = underscore_parameters(value) if value.is_a? Hash
|
55
|
+
|
56
|
+
hash[key.tr('-', '_')] = value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'drillbit/requests/base'
|
3
|
+
require 'drillbit/responses/invalid_token'
|
4
|
+
|
5
|
+
module Drillbit
|
6
|
+
module Middleware
|
7
|
+
class TokenProcessor
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
# :reek:FeatureEnvy
|
13
|
+
def call(env)
|
14
|
+
request = Requests::Base.resolve(env)
|
15
|
+
token = request.authorization_token
|
16
|
+
|
17
|
+
return Responses::InvalidToken.call(env, application_name: request.application_name) \
|
18
|
+
unless token.valid?
|
19
|
+
|
20
|
+
env['X_JSON_WEB_TOKEN'] = token.to_h
|
21
|
+
|
22
|
+
@app.call(env)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -32,21 +32,24 @@ class Base
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
# rubocop:disable Style/ClosingParenthesisIndentation
|
35
36
|
def authorization_token
|
36
37
|
if (
|
37
38
|
!authorization_token_from_header.blank? &&
|
38
39
|
authorization_token_from_header.valid?
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
) \
|
41
|
+
||
|
42
|
+
(
|
43
|
+
authorization_token_from_params.blank? ||
|
44
|
+
!authorization_token_from_params.valid?
|
45
|
+
)
|
44
46
|
|
45
47
|
authorization_token_from_header
|
46
48
|
else
|
47
49
|
authorization_token_from_params
|
48
50
|
end
|
49
51
|
end
|
52
|
+
# rubocop:enable Style/ClosingParenthesisIndentation
|
50
53
|
|
51
54
|
def application_name
|
52
55
|
raw_request_application_name || Drillbit.configuration.application_name
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'drillbit/errors/invalid_request_body'
|
3
|
+
|
4
|
+
module Drillbit
|
5
|
+
module Responses
|
6
|
+
class InvalidRequestBody
|
7
|
+
def self.call(env)
|
8
|
+
error = Drillbit::Errors::InvalidRequestBody.new(request_body: env['RACK_INPUT'])
|
9
|
+
|
10
|
+
[
|
11
|
+
error.http_status, # HTTP Status Code
|
12
|
+
{}, # Response Headers
|
13
|
+
[error.to_json], # Message
|
14
|
+
]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/drillbit/version.rb
CHANGED
@@ -3,8 +3,8 @@ require 'spec_helper'
|
|
3
3
|
require 'drillbit/accept_header'
|
4
4
|
|
5
5
|
# rubocop:disable Metrics/LineLength
|
6
|
-
module
|
7
|
-
|
6
|
+
module Drillbit
|
7
|
+
describe AcceptHeader do
|
8
8
|
it 'can validate an accept header with all the pieces of information' do
|
9
9
|
header = AcceptHeader.new(application: 'westeros',
|
10
10
|
header: 'application/vnd.westeros+redkeep;version=1.0.0')
|
@@ -2,10 +2,10 @@
|
|
2
2
|
require 'rspeckled'
|
3
3
|
require 'drillbit/authorizers/parameters/filtering'
|
4
4
|
|
5
|
-
module
|
6
|
-
module
|
7
|
-
class
|
8
|
-
|
5
|
+
module Drillbit
|
6
|
+
module Authorizers
|
7
|
+
class Parameters
|
8
|
+
describe Filtering do
|
9
9
|
let(:params) { { filter: { name: 'Bill', age: 26 } } }
|
10
10
|
|
11
11
|
it 'can authorize new filter parameters', verify: false do
|
@@ -2,10 +2,10 @@
|
|
2
2
|
require 'rspeckled'
|
3
3
|
require 'drillbit/authorizers/parameters/resource'
|
4
4
|
|
5
|
-
module
|
6
|
-
module
|
7
|
-
class
|
8
|
-
|
5
|
+
module Drillbit
|
6
|
+
module Authorizers
|
7
|
+
class Parameters
|
8
|
+
describe Resource do
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -2,9 +2,9 @@
|
|
2
2
|
require 'rspeckled'
|
3
3
|
require 'drillbit/authorizers/parameters'
|
4
4
|
|
5
|
-
module
|
6
|
-
module
|
7
|
-
|
5
|
+
module Drillbit
|
6
|
+
module Authorizers
|
7
|
+
describe Parameters do
|
8
8
|
it 'defaults to nothing' do
|
9
9
|
parameters = Parameters.new(token: '123',
|
10
10
|
user: 'my_user',
|
@@ -2,9 +2,9 @@
|
|
2
2
|
require 'rspeckled'
|
3
3
|
require 'drillbit/authorizers/query'
|
4
4
|
|
5
|
-
module
|
6
|
-
module
|
7
|
-
|
5
|
+
module Drillbit
|
6
|
+
module Authorizers
|
7
|
+
describe Query do
|
8
8
|
it 'does not authorize the resource by default' do
|
9
9
|
authorizer = Query.new(token: '123',
|
10
10
|
user: 'my_user',
|
@@ -3,9 +3,9 @@ require 'rspeckled'
|
|
3
3
|
require 'ostruct'
|
4
4
|
require 'drillbit/authorizers/scope'
|
5
5
|
|
6
|
-
module
|
7
|
-
module
|
8
|
-
|
6
|
+
module Drillbit
|
7
|
+
module Authorizers
|
8
|
+
describe Scope do
|
9
9
|
it 'defaults to nothing' do
|
10
10
|
scope = Scope.new(token: '123',
|
11
11
|
user: 'my_user',
|
@@ -2,9 +2,9 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
require 'drillbit/errors/invalid_api_request'
|
4
4
|
|
5
|
-
module
|
6
|
-
module
|
7
|
-
|
5
|
+
module Drillbit
|
6
|
+
module Errors
|
7
|
+
describe InvalidApiRequest do
|
8
8
|
let(:error) { InvalidApiRequest.new }
|
9
9
|
|
10
10
|
it 'has a status of 400' do
|