drillbit 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/drillbit.rb +3 -1
  5. data/lib/drillbit/authorizable_resource.rb +6 -0
  6. data/lib/drillbit/authorizers/parameters/filtering.rb +1 -0
  7. data/lib/drillbit/authorizers/parameters/resource.rb +59 -0
  8. data/lib/drillbit/errors/invalid_request_body.rb +29 -0
  9. data/lib/drillbit/middleware/api_request_validator.rb +40 -0
  10. data/lib/drillbit/middleware/parameter_parser.rb +61 -0
  11. data/lib/drillbit/middleware/token_processor.rb +26 -0
  12. data/lib/drillbit/requests/base.rb +8 -5
  13. data/lib/drillbit/responses/invalid_request_body.rb +18 -0
  14. data/lib/drillbit/tokens/json_web_token.rb +9 -1
  15. data/lib/drillbit/version.rb +1 -1
  16. data/spec/drillbit/accept_header_spec.rb +2 -2
  17. data/spec/drillbit/authorizers/parameters/filtering_spec.rb +4 -4
  18. data/spec/drillbit/authorizers/parameters/resource_spec.rb +4 -4
  19. data/spec/drillbit/authorizers/parameters_spec.rb +3 -3
  20. data/spec/drillbit/authorizers/query_spec.rb +3 -3
  21. data/spec/drillbit/authorizers/scope_spec.rb +3 -3
  22. data/spec/drillbit/errors/invalid_api_request_spec.rb +3 -3
  23. data/spec/drillbit/errors/invalid_request_body_spec.rb +25 -0
  24. data/spec/drillbit/errors/invalid_subdomain_spec.rb +3 -3
  25. data/spec/drillbit/errors/invalid_token_spec.rb +3 -3
  26. data/spec/drillbit/invalid_subdomain_spec.rb +3 -3
  27. data/spec/drillbit/invalid_token_spec.rb +3 -3
  28. data/spec/drillbit/matchers/accept_header_spec.rb +3 -3
  29. data/spec/drillbit/matchers/subdomain_spec.rb +3 -3
  30. data/spec/drillbit/matchers/version_spec.rb +3 -3
  31. data/spec/drillbit/middleware/{api_request_spec.rb → api_request_validator_spec.rb} +11 -46
  32. data/spec/drillbit/middleware/parameter_parser_spec.rb +184 -0
  33. data/spec/drillbit/middleware/token_processor_spec.rb +27 -0
  34. data/spec/drillbit/requests/base_spec.rb +3 -3
  35. data/spec/drillbit/requests/rack_spec.rb +3 -3
  36. data/spec/drillbit/requests/rails_spec.rb +3 -3
  37. data/spec/drillbit/resource/model_spec.rb +3 -3
  38. data/spec/drillbit/resource/processors/filtering_spec.rb +4 -4
  39. data/spec/drillbit/resource/processors/indexing_spec.rb +4 -4
  40. data/spec/drillbit/resource/processors/paging_spec.rb +4 -4
  41. data/spec/drillbit/resource/processors/sorting_spec.rb +4 -4
  42. data/spec/drillbit/tokens/base64_spec.rb +3 -3
  43. data/spec/drillbit/tokens/json_web_token_spec.rb +11 -3
  44. data/spec/drillbit/tokens/json_web_tokens/password_reset_spec.rb +4 -4
  45. metadata +15 -8
  46. metadata.gz.sig +0 -0
  47. data/lib/drillbit/middleware/api_request.rb +0 -49
  48. data/lib/drillbit/parameters.rb +0 -22
  49. data/spec/drillbit/parameters_spec.rb +0 -49
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 477a99cfe9c988e804d76c42b67dd503d71716ca
4
- data.tar.gz: 2cbc77f50321b0d52b0209df5228c564a68f7993
3
+ metadata.gz: 362e3bda774e3c8a662b8dc640281e0ae389c953
4
+ data.tar.gz: ffa6d8b3eb8c83f52496a103378b994302d01fc2
5
5
  SHA512:
6
- metadata.gz: 0a2cf2c6301d8d29a8936e3c73334f074ab0c27087a326ee8228c627dad73c55d8eb39b84bf741ddb3b458d595ff287cc8fe4fd95d6d570decf357c0fe04a140
7
- data.tar.gz: cd9c47f410eeda20b0ff09b0ede87c8febb5cc9a971853cb59b71e2f567fc370887022eda8519a87688ed40a5f78b2055e061f6f4700deacac6f976421ce8ea7
6
+ metadata.gz: b6d144f2c1b07894b0ed5ebf08e308720b882ed3892dea1e55f4a4a77a3dd348d933b56be2f9357b19bc651759380d9b52834274745e3651b76d3f32648ad2da
7
+ data.tar.gz: e0be2925d007e7771501b62fc28d2561445edd900ea584ccc6685c90de6b19cca7df02340e5342fbd7efd41bc0791d5bcc87c7d939e11b827ec0f339b6ff0405
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -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/api_request'
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,6 +1,7 @@
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
@@ -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
- authorization_token_from_params.blank? ||
42
- !authorization_token_from_params.valid?
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
@@ -88,7 +88,15 @@ class JsonWebToken
88
88
  end
89
89
 
90
90
  def blank?
91
- false
91
+ data.empty?
92
+ end
93
+
94
+ def present?
95
+ data.any?
96
+ end
97
+
98
+ def empty?
99
+ data.empty?
92
100
  end
93
101
 
94
102
  def to_h
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Drillbit
3
- VERSION = '1.1.0'
3
+ VERSION = '2.0.0'
4
4
  end
@@ -3,8 +3,8 @@ require 'spec_helper'
3
3
  require 'drillbit/accept_header'
4
4
 
5
5
  # rubocop:disable Metrics/LineLength
6
- module Drillbit
7
- RSpec.describe AcceptHeader do
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 Drillbit
6
- module Authorizers
7
- class Parameters
8
- RSpec.describe Filtering do
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 Drillbit
6
- module Authorizers
7
- class Parameters
8
- RSpec.describe Resource do
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 Drillbit
6
- module Authorizers
7
- RSpec.describe Parameters do
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 Drillbit
6
- module Authorizers
7
- RSpec.describe Query do
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 Drillbit
7
- module Authorizers
8
- RSpec.describe Scope do
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 Drillbit
6
- module Errors
7
- RSpec.describe InvalidApiRequest do
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