drillbit 0.0.1 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7da2a5f4ccd53545dbaed3a47514e4412a03c8d5
4
- data.tar.gz: fd209949f22667edd9463773350c5e10653c02ef
3
+ metadata.gz: 8653add9748daa722199cda827ade767201495db
4
+ data.tar.gz: bad41a3d97ac655776263d7fc5770c71e09ab062
5
5
  SHA512:
6
- metadata.gz: fe248f3809403c62785c45701359ae0646d8ae3ba70230ed02c7a00e126ea2c826c238c7150544faf4bacd918eddd86a88078b368dbf3aceaffccd9ffc5c1720
7
- data.tar.gz: 16178d7d3d1a04f7917d5e980734deff2d05513e39d95138aa67c0a654be4e2ff09963ee0f54bab50a0f775952e7367c50d09903f321dfe7548d7152b15d7111
6
+ metadata.gz: d8bbd110a4c40d5beecd18b3c4b72e91b239c0b7824f7c142006d6d29caf2722ec438b5acdf5fafc573165e9edbffc49aa960c51d30b43c202eaea032cc4f7b7
7
+ data.tar.gz: 4e6b1c5897fa747f3497cdb17a10bed6d355fe093d7e0f7e2a65810ab8aec65b3166cdc204e0f1927f0f96cbf5d628f46b534eb8f2095ba129d0309188932e87
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -75,6 +75,7 @@ module AuthorizableResource
75
75
  authorizer_class.
76
76
  new(token: token,
77
77
  user: authorized_user,
78
+ params: authorized_params,
78
79
  resource: authorized_resource)
79
80
  end
80
81
 
@@ -4,11 +4,13 @@ module Authorizers
4
4
  class Query
5
5
  attr_accessor :token,
6
6
  :user,
7
+ :params,
7
8
  :resource
8
9
 
9
- def initialize(token:, user:, resource:, **other)
10
+ def initialize(token:, user:, params:, resource:, **other)
10
11
  self.token = token
11
12
  self.user = user
13
+ self.params = params
12
14
  self.resource = resource
13
15
 
14
16
  other.each do |name, value|
@@ -17,7 +19,7 @@ class Query
17
19
  end
18
20
 
19
21
  def able_to_index?
20
- true
22
+ false
21
23
  end
22
24
 
23
25
  def able_to_show?
@@ -1,19 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
  module Drillbit
3
3
  class Configuration
4
- attr_accessor \
5
- :allowed_subdomains,
4
+ attr_writer \
6
5
  :allowed_api_subdomains,
6
+ :allowed_subdomains,
7
7
  :application_name,
8
+ :available_token_roles,
8
9
  :default_api_version,
10
+ :default_token_audience,
11
+ :default_token_roles,
12
+ :default_token_expiration_in_minutes,
13
+ :default_token_issuer,
14
+ :default_token_subject,
9
15
  :token_private_key
10
16
 
17
+ attr_accessor \
18
+ :application_name
19
+
11
20
  def to_h
12
21
  {
13
- allowed_subdomains: allowed_subdomains,
14
- allowed_api_subdomains: allowed_api_subdomains,
15
- application_name: application_name,
16
- default_api_version: default_api_version,
22
+ allowed_api_subdomains: allowed_api_subdomains,
23
+ allowed_subdomains: allowed_subdomains,
24
+ application_name: application_name,
25
+ default_api_version: default_api_version,
26
+ default_token_issuer: default_token_issuer,
27
+ default_token_subject: default_token_subject,
28
+ default_token_expiration_in_minutes: token_expiration_in_minutes,
17
29
  }
18
30
  end
19
31
 
@@ -24,6 +36,41 @@ module Drillbit
24
36
  def allowed_api_subdomains
25
37
  @allowed_api_subdomains || ['api']
26
38
  end
39
+
40
+ def default_token_audience
41
+ @default_token_audience || 'public'
42
+ end
43
+
44
+ def available_token_roles
45
+ @available_token_roles || %w{standard admin password_reset email_verification}
46
+ end
47
+
48
+ def default_api_version
49
+ @default_api_version || '1'
50
+ end
51
+
52
+ def default_token_roles
53
+ @default_token_roles || %w{standard}
54
+ end
55
+
56
+ def default_token_issuer
57
+ @default_token_issuer || 'drillbit'
58
+ end
59
+
60
+ def default_token_subject
61
+ @default_token_subject || 'User'
62
+ end
63
+
64
+ def default_token_expiration_in_minutes
65
+ @default_token_expiration_in_minutes || (7 * 24 * 60)
66
+ end
67
+
68
+ def token_private_key
69
+ return unless @token_private_key
70
+ return @token_private_key if @token_private_key.respond_to?(:sign)
71
+
72
+ OpenSSL::PKey::RSA.new(@token_private_key)
73
+ end
27
74
  end
28
75
 
29
76
  def self.configure
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
+ # rubocop:disable Metrics/LineLength
2
3
  module Drillbit
3
4
  module Resource
4
5
  module Naming
5
- CONTROLLER_RESOURCE_NAME_PATTERN = /\A((.*?::)?.*?)(\w+)Controller\z/
6
+ CONTROLLER_RESOURCE_NAME_PATTERN = /\A((.*?::)?.*?)(\w+?)(?:Index|Indicies)?Controller\z/
6
7
 
7
8
  module ClassMethods
8
9
  def plural_resource_name
@@ -31,3 +32,4 @@ module Naming
31
32
  end
32
33
  end
33
34
  end
35
+ # rubocop:enable Metrics/LineLength
@@ -3,7 +3,17 @@ module Drillbit
3
3
  module Serializers
4
4
  module JsonApi
5
5
  def json_api_type
6
- object.class.name.demodulize.tableize.dasherize
6
+ object.
7
+ class.
8
+ name.
9
+ gsub(/\A[^:]+::/, '').
10
+ split('::').
11
+ reverse.
12
+ join('::').
13
+ tableize.
14
+ dasherize.
15
+ tr('/', '-').
16
+ pluralize
7
17
  end
8
18
  end
9
19
  end
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
  require 'jwt'
3
3
  require 'json/jwt'
4
+ require 'drillbit/configuration'
4
5
  require 'drillbit/tokens/json_web_tokens/invalid'
5
6
  require 'drillbit/tokens/json_web_tokens/null'
6
7
 
8
+ # :reek:TooManyMethods
7
9
  module Drillbit
8
10
  module Tokens
9
11
  class JsonWebToken
@@ -27,15 +29,60 @@ class JsonWebToken
27
29
  ].freeze
28
30
 
29
31
  attr_accessor :data,
32
+ :headers,
30
33
  :private_key
31
34
 
32
35
  def initialize(data:,
36
+ headers: {},
33
37
  private_key: Drillbit.configuration.token_private_key)
34
38
 
35
39
  self.data = data
40
+ self.headers = headers
36
41
  self.private_key = private_key
37
42
  end
38
43
 
44
+ def self.build_from_request(request_token)
45
+ return Tokens::JsonWebTokens::Null.instance unless request_token
46
+
47
+ data, headers = *request_token
48
+
49
+ new(data: data, headers: headers)
50
+ end
51
+
52
+ # rubocop:disable Metrics/ParameterLists, Metrics/AbcSize, Metrics/LineLength
53
+ # :reek:DuplicateMethodCall
54
+ def self.build(id: SecureRandom.uuid,
55
+ audience: Drillbit.configuration.default_token_audience,
56
+ expiration: Time.now.utc.to_i + (60 * Drillbit.configuration.default_token_expiration_in_minutes),
57
+ issuer: Drillbit.configuration.default_token_issuer || 'Drillbit',
58
+ issued_at: Time.now.utc,
59
+ not_before: Time.now.utc,
60
+ owner: nil,
61
+ roles: Drillbit.configuration.default_token_roles,
62
+ subject: Drillbit.configuration.default_token_subject,
63
+ subject_id:,
64
+ token_private_key: Drillbit.configuration.token_private_key)
65
+
66
+ owner ||= subject_id
67
+
68
+ new(
69
+ private_key: token_private_key,
70
+ data: {
71
+ 'aud' => audience,
72
+ 'exp' => expiration.to_i,
73
+ 'iat' => issued_at.to_i,
74
+ 'iss' => issuer,
75
+ 'jti' => id,
76
+ 'nbf' => not_before.to_i,
77
+ 'own' => owner,
78
+ 'rol' => roles.join(','),
79
+ 'sid' => subject_id,
80
+ 'sub' => subject,
81
+ },
82
+ )
83
+ end
84
+ # rubocop:enable Metrics/ParameterLists, Metrics/AbcSize, Metrics/LineLength
85
+
39
86
  def valid?
40
87
  true
41
88
  end
@@ -45,7 +92,53 @@ class JsonWebToken
45
92
  end
46
93
 
47
94
  def to_h
48
- data
95
+ [data, headers]
96
+ end
97
+
98
+ def audience
99
+ data['aud']
100
+ end
101
+
102
+ def issued_at
103
+ data['iat']
104
+ end
105
+
106
+ def issuer
107
+ data['iss']
108
+ end
109
+
110
+ def expiration
111
+ data['exp']
112
+ end
113
+
114
+ def id
115
+ data['jti']
116
+ end
117
+
118
+ def not_before
119
+ data['nbf']
120
+ end
121
+
122
+ def owner_id
123
+ data['own']
124
+ end
125
+
126
+ def subject_id
127
+ data['sid']
128
+ end
129
+
130
+ def subject
131
+ data['sub']
132
+ end
133
+
134
+ Drillbit.configuration.available_token_roles.each do |role|
135
+ define_method("#{role}?") do
136
+ roles.include? role
137
+ end
138
+ end
139
+
140
+ def roles
141
+ @roles ||= data['rol'].split(',')
49
142
  end
50
143
 
51
144
  def to_jwt
@@ -91,18 +184,21 @@ class JsonWebToken
91
184
 
92
185
  return JsonWebTokens::Null.instance if signed_token.to_s == ''
93
186
 
94
- data = JWT.decode(
95
- signed_token,
96
- private_key,
97
- true,
98
- algorithm: 'RS256',
99
- verify_expiration: true,
100
- verify_not_before: true,
101
- verify_iat: true,
102
- leeway: 5,
187
+ decoded = JWT.decode(
188
+ signed_token,
189
+ private_key,
190
+ true,
191
+ algorithm: 'RS256',
192
+ verify_expiration: true,
193
+ verify_not_before: true,
194
+ verify_iat: true,
195
+ leeway: 5,
103
196
  )
104
197
 
198
+ data, headers = *decoded
199
+
105
200
  new(data: data,
201
+ headers: headers,
106
202
  private_key: private_key)
107
203
  rescue *TRANSFORMATION_EXCEPTIONS
108
204
  JsonWebTokens::Invalid.instance
@@ -1,12 +1,55 @@
1
1
  # frozen_string_literal: true
2
+ require 'drillbit/configuration'
2
3
  require 'drillbit/tokens/null'
3
4
 
4
5
  module Drillbit
5
6
  module Tokens
6
7
  module JsonWebTokens
7
8
  class Null < Tokens::Null
8
- def to_h
9
- [{}, {}]
9
+ def audience
10
+ nil
11
+ end
12
+
13
+ def issued_at
14
+ nil
15
+ end
16
+
17
+ def issuer
18
+ nil
19
+ end
20
+
21
+ def expiration
22
+ nil
23
+ end
24
+
25
+ def id
26
+ nil
27
+ end
28
+
29
+ def not_before
30
+ nil
31
+ end
32
+
33
+ def owner_id
34
+ nil
35
+ end
36
+
37
+ def subject_id
38
+ nil
39
+ end
40
+
41
+ def subject
42
+ nil
43
+ end
44
+
45
+ Drillbit.configuration.available_token_roles.each do |role|
46
+ define_method("#{role}?") do
47
+ false
48
+ end
49
+ end
50
+
51
+ def roles
52
+ []
10
53
  end
11
54
  end
12
55
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ require 'drillbit/tokens/json_web_token'
3
+
4
+ module Drillbit
5
+ module Tokens
6
+ module JsonWebTokens
7
+ class PasswordReset < JsonWebToken
8
+ def self.build(expiration: Time.now.utc.to_i + (30 * 60), **attrs)
9
+ super(expiration: expiration, **attrs)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Drillbit
3
- VERSION = '0.0.1'
3
+ VERSION = '1.0.0'
4
4
  end
@@ -8,9 +8,10 @@ RSpec.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',
11
- resource: 'my_resource')
11
+ resource: 'my_resource',
12
+ params: 'my_params')
12
13
 
13
- expect(authorizer).to be_able_to_index
14
+ expect(authorizer).not_to be_able_to_index
14
15
  expect(authorizer).not_to be_able_to_show
15
16
  expect(authorizer).not_to be_able_to_create
16
17
  expect(authorizer).not_to be_able_to_update
@@ -41,7 +41,17 @@ RSpec.describe JsonWebToken do
41
41
  private_key: test_private_key)
42
42
 
43
43
  expect(token).to be_a JsonWebToken
44
- expect(token.to_h).to eql([{ 'bar' => 'baz' }, { 'typ' => 'JWT', 'alg' => 'RS256' }])
44
+ expect(token.to_h).to eql(
45
+ [
46
+ {
47
+ 'bar' => 'baz',
48
+ },
49
+ {
50
+ 'typ' => 'JWT',
51
+ 'alg' => 'RS256',
52
+ },
53
+ ],
54
+ )
45
55
  end
46
56
 
47
57
  it 'can convert an empty signed token' do
@@ -130,6 +140,84 @@ RSpec.describe JsonWebToken do
130
140
  ]
131
141
  end
132
142
  # rubocop:enable Metrics/LineLength
143
+
144
+ it 'can accept roles as a string but convert them to an array' do
145
+ token = JsonWebToken.new(data: { 'rol' => 'bar,baz' },
146
+ private_key: test_private_key)
147
+
148
+ expect(token.roles).to eql %w{bar baz}
149
+
150
+ jwe_s = token.to_jwe_s
151
+
152
+ converted_token = JsonWebToken.from_jwe(jwe_s,
153
+ private_key: test_private_key)
154
+
155
+ expect(converted_token.data['rol']).to eql 'bar,baz'
156
+ expect(converted_token.roles).to eql %w{bar baz}
157
+ end
158
+
159
+ it 'can build a token by explicitly specifying all the data', :time_mock do
160
+ token = JsonWebToken.build(id: 'test_id',
161
+ audience: 'audience',
162
+ expiration: 20.years.from_now,
163
+ issuer: 'issuer',
164
+ issued_at: 5.minutes.ago,
165
+ not_before: 1.month.ago,
166
+ owner: 'subject_id',
167
+ roles: %w{my_role},
168
+ subject: 'my_subject',
169
+ subject_id: 'subject_id',
170
+ token_private_key: test_private_key)
171
+
172
+ jwe_s = token.to_jwe_s
173
+
174
+ converted_token = JsonWebToken.from_jwe(jwe_s,
175
+ private_key: test_private_key)
176
+
177
+ expect(converted_token.to_h).to eql(
178
+ [
179
+ {
180
+ 'aud' => 'audience',
181
+ 'exp' => 1_974_477_600,
182
+ 'iat' => 1_343_325_300,
183
+ 'iss' => 'issuer',
184
+ 'jti' => 'test_id',
185
+ 'nbf' => 1_340_733_600,
186
+ 'own' => 'subject_id',
187
+ 'rol' => 'my_role',
188
+ 'sid' => 'subject_id',
189
+ 'sub' => 'my_subject',
190
+ },
191
+ {
192
+ 'typ' => 'JWT',
193
+ 'alg' => 'RS256',
194
+ },
195
+ ],
196
+ )
197
+ end
198
+
199
+ it 'can build a token by only specifying the subject ID', :time_mock do
200
+ token = JsonWebToken.build(subject_id: 'subject_id',
201
+ token_private_key: test_private_key)
202
+
203
+ jwe_s = token.to_jwe_s
204
+
205
+ converted_token = JsonWebToken.from_jwe(jwe_s,
206
+ private_key: test_private_key)
207
+
208
+ expect(converted_token.to_h).to include(
209
+ 'aud' => 'public',
210
+ 'exp' => 1_343_930_400,
211
+ 'iat' => 1_343_325_600,
212
+ 'iss' => 'drillbit',
213
+ 'jti' => match(uuid_regex),
214
+ 'nbf' => 1_343_325_600,
215
+ 'own' => 'subject_id',
216
+ 'rol' => 'standard',
217
+ 'sid' => 'subject_id',
218
+ 'sub' => 'User',
219
+ )
220
+ end
133
221
  end
134
222
  end
135
223
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+ require 'drillbit/tokens/json_web_tokens/password_reset'
4
+
5
+ module Drillbit
6
+ module Tokens
7
+ module JsonWebTokens
8
+ RSpec.describe PasswordReset do
9
+ it 'can build a token that expires during the password reset timeframe', :time_mock do
10
+ token = JsonWebToken.build(id: 'test_id',
11
+ subject_id: 'subject_id',
12
+ token_private_key: test_private_key)
13
+
14
+ jwe_s = token.to_jwe_s
15
+
16
+ converted_token = JsonWebToken.from_jwe(jwe_s,
17
+ private_key: test_private_key)
18
+
19
+ expect(converted_token.to_h).to eql(
20
+ [
21
+ {
22
+ 'aud' => 'public',
23
+ 'exp' => 1_343_930_400,
24
+ 'iat' => 1_343_325_600,
25
+ 'iss' => 'drillbit',
26
+ 'jti' => 'test_id',
27
+ 'nbf' => 1_343_325_600,
28
+ 'own' => 'subject_id',
29
+ 'rol' => 'standard',
30
+ 'sid' => 'subject_id',
31
+ 'sub' => 'User',
32
+ },
33
+ {
34
+ 'typ' => 'JWT',
35
+ 'alg' => 'RS256',
36
+ },
37
+ ],
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: drillbit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - thegranddesign
@@ -31,7 +31,7 @@ cert_chain:
31
31
  zRIv8lqQM8QFT76rzP5SBCERwN+ltKAFbQ5/FwmZNGWYnmCP3RZMQiRnbh+9H9lh
32
32
  mlbwaYZTjgsXq6cy8N38EecewgBbZYS1IYJraE/M
33
33
  -----END CERTIFICATE-----
34
- date: 2016-05-01 00:00:00.000000000 Z
34
+ date: 2016-05-12 00:00:00.000000000 Z
35
35
  dependencies:
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: erratum
@@ -103,6 +103,20 @@ dependencies:
103
103
  - - "~>"
104
104
  - !ruby/object:Gem::Version
105
105
  version: '0.0'
106
+ - !ruby/object:Gem::Dependency
107
+ name: timecop
108
+ requirement: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: 0.7.0
113
+ type: :development
114
+ prerelease: false
115
+ version_requirements: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: 0.7.0
106
120
  description: ''
107
121
  email: rubygems@livinghighontheblog.com
108
122
  executables: []
@@ -155,6 +169,7 @@ files:
155
169
  - lib/drillbit/tokens/json_web_token.rb
156
170
  - lib/drillbit/tokens/json_web_tokens/invalid.rb
157
171
  - lib/drillbit/tokens/json_web_tokens/null.rb
172
+ - lib/drillbit/tokens/json_web_tokens/password_reset.rb
158
173
  - lib/drillbit/tokens/null.rb
159
174
  - lib/drillbit/version.rb
160
175
  - spec/drillbit/accept_header_spec.rb
@@ -183,6 +198,7 @@ files:
183
198
  - spec/drillbit/resource/processors/sorting_spec.rb
184
199
  - spec/drillbit/tokens/base64_spec.rb
185
200
  - spec/drillbit/tokens/json_web_token_spec.rb
201
+ - spec/drillbit/tokens/json_web_tokens/password_reset_spec.rb
186
202
  - spec/fixtures/test_rsa_key
187
203
  - spec/fixtures/test_rsa_key.pub
188
204
  - spec/spec_helper.rb
@@ -238,6 +254,7 @@ test_files:
238
254
  - spec/drillbit/resource/processors/sorting_spec.rb
239
255
  - spec/drillbit/tokens/base64_spec.rb
240
256
  - spec/drillbit/tokens/json_web_token_spec.rb
257
+ - spec/drillbit/tokens/json_web_tokens/password_reset_spec.rb
241
258
  - spec/fixtures/test_rsa_key
242
259
  - spec/fixtures/test_rsa_key.pub
243
260
  - spec/spec_helper.rb
metadata.gz.sig CHANGED
Binary file