drillbit 0.0.1 → 1.0.0

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