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