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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7da2a5f4ccd53545dbaed3a47514e4412a03c8d5
4
+ data.tar.gz: fd209949f22667edd9463773350c5e10653c02ef
5
+ SHA512:
6
+ metadata.gz: fe248f3809403c62785c45701359ae0646d8ae3ba70230ed02c7a00e126ea2c826c238c7150544faf4bacd918eddd86a88078b368dbf3aceaffccd9ffc5c1720
7
+ data.tar.gz: 16178d7d3d1a04f7917d5e980734deff2d05513e39d95138aa67c0a654be4e2ff09963ee0f54bab50a0f775952e7367c50d09903f321dfe7548d7152b15d7111
Binary file
Binary file
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2016 The Grand Design
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,2 @@
1
+ Drillbit
2
+ ================================================================================
@@ -0,0 +1,2 @@
1
+ # frozen_string_literal: true
2
+ require 'bundler/gem_tasks'
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ require 'drillbit/version'
3
+
4
+ require 'drillbit/authorizers/parameters'
5
+ require 'drillbit/authorizers/parameters/filtering'
6
+ require 'drillbit/authorizers/parameters/resource'
7
+ require 'drillbit/authorizers/query'
8
+ require 'drillbit/authorizers/scope'
9
+ require 'drillbit/configuration'
10
+ require 'drillbit/matchers/accept_header'
11
+ require 'drillbit/matchers/subdomain'
12
+ require 'drillbit/matchers/version'
13
+ require 'drillbit/resource'
14
+ require 'drillbit/serializers/json_api'
15
+
16
+ require 'drillbit/middleware/api_request'
17
+
18
+ require 'drillbit/responses/invalid_api_request'
19
+ require 'drillbit/responses/invalid_subdomain'
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+ module Drillbit
3
+ class AcceptHeader
4
+ attr_accessor :application,
5
+ :raw_accept_header
6
+
7
+ def initialize(application:, header:)
8
+ self.application = application
9
+ self.raw_accept_header = header || ''
10
+ end
11
+
12
+ def version
13
+ accept_header_data[2]
14
+ end
15
+
16
+ def content_type
17
+ accept_header_data[1]
18
+ end
19
+
20
+ def valid?
21
+ !invalid?
22
+ end
23
+
24
+ def invalid?
25
+ accept_header_data.nil?
26
+ end
27
+
28
+ def to_s
29
+ raw_accept_header
30
+ end
31
+
32
+ private
33
+
34
+ def accept_header_data
35
+ raw_accept_header.match(accept_header_format)
36
+ end
37
+
38
+ def accept_header_format
39
+ %r{\Aapplication/#{application_vendor}(?:\+(\w+))?(?:;version=(#{version_format}))?\z}
40
+ end
41
+
42
+ def application_vendor
43
+ "vnd\\.#{application}"
44
+ end
45
+
46
+ def version_format
47
+ '\\d+(?:\\.\\d+){0,2}(?:beta(?:\\d*))?'
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+ require 'drillbit/resource/naming'
3
+ require 'drillbit/resource/model'
4
+
5
+ module Drillbit
6
+ module AuthorizableResource
7
+ RESOURCE_COLLECTION_ACTIONS = %w{index}.freeze
8
+
9
+ module ClassMethods
10
+ def authorizer_prefix
11
+ @authorizer_prefix ||= name[Resource::Naming::CONTROLLER_RESOURCE_NAME_PATTERN, 2]
12
+ end
13
+
14
+ def authorizer_class
15
+ @authorizer_class ||= "#{authorizer_prefix}" \
16
+ "Authorizers::" \
17
+ "#{resource_class_name}".
18
+ constantize
19
+ rescue NameError
20
+ 'Drillbit::Authorizers::Query'.constantize
21
+ end
22
+
23
+ def authorizer_scope_class
24
+ @authorizer_scope_class ||= "#{authorizer_prefix}" \
25
+ "Authorizers::" \
26
+ "#{resource_class_name}" \
27
+ "::Scope".
28
+ constantize
29
+ rescue NameError
30
+ 'Drillbit::Authorizers::Scope'.constantize
31
+ end
32
+
33
+ def authorizer_resource_params_class
34
+ @authorizer_resource_params_class ||= "#{authorizer_prefix}" \
35
+ "Authorizers::" \
36
+ "#{resource_class_name}" \
37
+ "::ResourceParameters".
38
+ constantize
39
+ rescue NameError
40
+ 'Drillbit::Authorizers::Parameters::Resource'.constantize
41
+ end
42
+
43
+ def authorizer_filtering_params_class
44
+ @authorizer_filtering_params_class ||= "#{authorizer_prefix}" \
45
+ "Authorizers::" \
46
+ "#{resource_class_name}::" \
47
+ "FilteringParameters".
48
+ constantize
49
+ rescue NameError
50
+ 'Drillbit::Authorizers::Parameters::Filtering'.constantize
51
+ end
52
+ end
53
+
54
+ def self.included(base)
55
+ base.include Resource::Naming
56
+ base.extend ClassMethods
57
+
58
+ base.before_action :authorize
59
+ end
60
+
61
+ private
62
+
63
+ def authorize
64
+ Erratum.raise(
65
+ 'ForbiddenError',
66
+ resource_name: self.class.singular_resource_name,
67
+ resource_id: [params[:id]],
68
+ action: action_name,
69
+ ) unless authorizer.public_send(authorization_query)
70
+ end
71
+
72
+ def authorizer
73
+ @authorizer ||= self.
74
+ class.
75
+ authorizer_class.
76
+ new(token: token,
77
+ user: authorized_user,
78
+ resource: authorized_resource)
79
+ end
80
+
81
+ def authorized_scope
82
+ @authorized_scope ||= self.
83
+ class.
84
+ authorizer_scope_class.
85
+ new(token: token,
86
+ user: authorized_user,
87
+ scoped_user_id: scoped_user_id,
88
+ params: authorized_params,
89
+ scope_root: authorized_scope_root).
90
+ call
91
+ end
92
+
93
+ def authorized_params
94
+ @authorized_params ||= authorizer_params_class.
95
+ new(token: token,
96
+ user: authorized_user,
97
+ params: params).
98
+ call
99
+ end
100
+
101
+ def authorized_resource
102
+ return nil if RESOURCE_COLLECTION_ACTIONS.include?(action_name)
103
+
104
+ @authorized_resource ||= public_send(self.class.singular_resource_name)
105
+ end
106
+
107
+ def authorized_collection
108
+ return nil unless RESOURCE_COLLECTION_ACTIONS.include?(action_name)
109
+
110
+ @authorized_collection ||= Resource::Model.
111
+ new(resource: public_send(self.class.plural_resource_name),
112
+ parameters: authorized_params)
113
+ end
114
+
115
+ def authorizer_params_class
116
+ @authorizer_params_class ||= if RESOURCE_COLLECTION_ACTIONS.include?(action_name)
117
+ self.class.authorizer_filtering_params_class
118
+ else
119
+ self.class.authorizer_resource_params_class
120
+ end
121
+ end
122
+
123
+ def authorized_scope_root
124
+ @authorized_scope_root ||= "#{self.class.authorizer_prefix}" \
125
+ "#{self.class.resource_class_name}".
126
+ constantize
127
+ end
128
+
129
+ def scoped_user_id
130
+ @scoped_user_id ||= if requested_user_id.blank?
131
+ nil
132
+ else
133
+ requested_user_id
134
+ end
135
+ end
136
+
137
+ def requested_user_id
138
+ @requested_user_id ||= params.
139
+ fetch(:filter, {}).
140
+ fetch(authorized_user_underscored_class_name,
141
+ authorized_user.id)
142
+ end
143
+
144
+ def authorized_user
145
+ current_user
146
+ end
147
+
148
+ def authorized_user_underscored_class_name
149
+ @authorized_user_underscored_class_name ||= authorized_user.
150
+ class.
151
+ name[/([^:]+)\z/, 1].
152
+ underscore.
153
+ downcase
154
+ end
155
+
156
+ def authorization_query
157
+ @authorization_query ||= "able_to_#{action_name}?"
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module Drillbit
3
+ module Authorizers
4
+ class Parameters
5
+ attr_accessor :token,
6
+ :user,
7
+ :params
8
+
9
+ def initialize(token:, user:, params:, **other)
10
+ self.token = token
11
+ self.user = user
12
+ self.params = params
13
+
14
+ other.each do |name, value|
15
+ public_send("#{name}=", value)
16
+ end
17
+ end
18
+
19
+ def call
20
+ params
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+ require 'drillbit/authorizers/parameters'
3
+
4
+ module Drillbit
5
+ module Authorizers
6
+ class Parameters
7
+ class Filtering < Authorizers::Parameters
8
+ def call
9
+ params.permit(*authorized_params)
10
+ end
11
+
12
+ private
13
+
14
+ def authorized_params
15
+ @authorized_params ||= [
16
+ :sort,
17
+ page: %i{
18
+ number
19
+ size
20
+ offset
21
+ limit
22
+ cursor
23
+ },
24
+ filter: [
25
+ :query,
26
+ {},
27
+ ],
28
+ ]
29
+ end
30
+
31
+ def add_filterable_parameter(name)
32
+ param = params.fetch(:filter, {}).
33
+ fetch(name, nil)
34
+
35
+ if param.class == Array
36
+ authorized_params[1][:filter][1][name] = []
37
+ else
38
+ authorized_params[1][:filter] << name
39
+ end
40
+ end
41
+
42
+ def add_filterable_parameters(*names)
43
+ names.each do |name|
44
+ add_filterable_parameter(name)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ require 'drillbit/authorizers/parameters'
3
+
4
+ module Drillbit
5
+ module Authorizers
6
+ class Parameters
7
+ class Resource < Authorizers::Parameters
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ module Drillbit
3
+ module Authorizers
4
+ class Query
5
+ attr_accessor :token,
6
+ :user,
7
+ :resource
8
+
9
+ def initialize(token:, user:, resource:, **other)
10
+ self.token = token
11
+ self.user = user
12
+ self.resource = resource
13
+
14
+ other.each do |name, value|
15
+ public_send("#{name}=", value)
16
+ end
17
+ end
18
+
19
+ def able_to_index?
20
+ true
21
+ end
22
+
23
+ def able_to_show?
24
+ false
25
+ end
26
+
27
+ def able_to_create?
28
+ false
29
+ end
30
+
31
+ def able_to_update?
32
+ false
33
+ end
34
+
35
+ def able_to_destroy?
36
+ false
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ module Drillbit
3
+ module Authorizers
4
+ class Scope
5
+ attr_accessor :token,
6
+ :user,
7
+ :scoped_user_id,
8
+ :params,
9
+ :scope_root
10
+
11
+ # rubocop:disable Metrics/ParameterLists
12
+ def initialize(token:, user:, params:, scoped_user_id:, scope_root:, **other)
13
+ self.token = token
14
+ self.user = user
15
+ self.params = params
16
+ self.scoped_user_id = scoped_user_id
17
+ self.scope_root = scope_root
18
+
19
+ other.each do |name, value|
20
+ public_send("#{name}=", value)
21
+ end
22
+ end
23
+ # rubocop:enable Metrics/ParameterLists
24
+
25
+ def call
26
+ scope_root.none
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ module Drillbit
3
+ class Configuration
4
+ attr_accessor \
5
+ :allowed_subdomains,
6
+ :allowed_api_subdomains,
7
+ :application_name,
8
+ :default_api_version,
9
+ :token_private_key
10
+
11
+ def to_h
12
+ {
13
+ allowed_subdomains: allowed_subdomains,
14
+ allowed_api_subdomains: allowed_api_subdomains,
15
+ application_name: application_name,
16
+ default_api_version: default_api_version,
17
+ }
18
+ end
19
+
20
+ def allowed_subdomains
21
+ @allowed_subdomains || ['api']
22
+ end
23
+
24
+ def allowed_api_subdomains
25
+ @allowed_api_subdomains || ['api']
26
+ end
27
+ end
28
+
29
+ def self.configure
30
+ yield configuration
31
+ end
32
+
33
+ def self.configuration
34
+ @configuration ||= Configuration.new
35
+ end
36
+ end