apiphobic-tokens 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 065e265c8b0c9ab81b420ba7d8d39c7483300d65634915d4b3c863b816033917
4
+ data.tar.gz: 611ffbb6939685eae93124f327505a963f09a7d4f703cb1824e6eb2dcad4ba5f
5
+ SHA512:
6
+ metadata.gz: 7a34c1d2b67a73efc7a5c4bf345083e9684cffa7accbc951d263b06d18aea57fd1f4af4108e458127b7699f44bdb3337949064fbdfab703831e212cf01c3a44b
7
+ data.tar.gz: 5e16c64ef6ed70afd1d50cbd9ac2c0785ddd7a8bcce11ade799d23d6951e63bb2990029a9a1a0ba2e6548e7c2e1c7763aa7e39c05bb46f8614de393908112637
Binary file
Binary file
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010-2016 The Kompanee, Ltd
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,35 @@
1
+ # Tokens
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/tokens`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'tokens'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install tokens
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/jfelchner/tokens.
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apiphobic/tokens/version'
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erratum/errors/authentication/invalid_token'
4
+
5
+ module Apiphobic
6
+ module Errors
7
+ class Expired < ::Erratum::Errors::InvalidToken
8
+ attr_accessor :expired_at
9
+
10
+ def title
11
+ 'Token Is Expired'
12
+ end
13
+
14
+ def detail
15
+ <<~HEREDOC.chomp.tr("\n", ' ')
16
+ This token has already expired. It expired on '#{expired_at.iso8601}'.
17
+ HEREDOC
18
+ end
19
+
20
+ def source
21
+ {
22
+ 'expired_at' => expired_at.iso8601,
23
+ }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erratum/errors/authentication/invalid_token'
4
+
5
+ module Apiphobic
6
+ module Errors
7
+ class FutureIssuance < ::Erratum::Errors::InvalidToken
8
+ attr_accessor :issued_at
9
+
10
+ def title
11
+ 'Invalid Issuance'
12
+ end
13
+
14
+ def detail
15
+ <<~HEREDOC.chomp.tr("\n", ' ')
16
+ The date at which this token was issued (#{issued_at.iso8601}) is
17
+ invalid.
18
+ HEREDOC
19
+ end
20
+
21
+ def source
22
+ {
23
+ 'issued_at' => issued_at,
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erratum/errors/authentication/invalid_token'
4
+
5
+ module Apiphobic
6
+ module Errors
7
+ class InvalidAttribute < ::Erratum::Errors::InvalidToken
8
+ attr_accessor :attribute_name,
9
+ :attribute_value,
10
+ :additional_message
11
+
12
+ def title
13
+ 'Invalid Issuance'
14
+ end
15
+
16
+ def detail
17
+ <<~HEREDOC.chomp.tr("\n", ' ')
18
+ The #{attribute_name} listed in your token (#{attribute_value}) is not
19
+ valid for this application. #{additional_message}
20
+ HEREDOC
21
+ end
22
+
23
+ def source
24
+ {
25
+ attribute_name => attribute_value,
26
+ }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erratum/errors/authentication/invalid_token'
4
+
5
+ module Apiphobic
6
+ module Errors
7
+ class Unavailable < ::Erratum::Errors::InvalidToken
8
+ attr_accessor :available_at
9
+
10
+ def title
11
+ 'Token Is Unavailable'
12
+ end
13
+
14
+ def detail
15
+ <<~HEREDOC.chomp.tr("\n", ' ')
16
+ The token you passed is not yet usable. Please wait until
17
+ '#{available_at.iso8601}' to try again.
18
+ HEREDOC
19
+ end
20
+
21
+ def source
22
+ {
23
+ 'available_at' => available_at,
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'erratum/errors/authentication/invalid_token'
5
+ require 'apiphobic/tokens/base64s/null'
6
+
7
+ module Apiphobic
8
+ module Tokens
9
+ class Base64
10
+ attr_accessor :token
11
+
12
+ def initialize(token:)
13
+ self.token = token
14
+ end
15
+
16
+ def valid?
17
+ true
18
+ end
19
+
20
+ def validate!
21
+ true
22
+ end
23
+
24
+ def blank?
25
+ false
26
+ end
27
+
28
+ def to_h
29
+ [
30
+ {
31
+ 'token' => token,
32
+ },
33
+ {
34
+ 'typ' => 'base64',
35
+ },
36
+ ]
37
+ end
38
+
39
+ def self.convert(raw_token:)
40
+ return Base64s::Null.instance if raw_token.to_s == ''
41
+
42
+ ::Base64.strict_decode64(raw_token)
43
+
44
+ new(token: raw_token)
45
+ rescue ArgumentError
46
+ raise Erratum::Errors::InvalidToken
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apiphobic/tokens/invalid'
4
+
5
+ module Apiphobic
6
+ module Tokens
7
+ module Base64s
8
+ class Invalid < Tokens::Invalid
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apiphobic/tokens/null'
4
+
5
+ module Apiphobic
6
+ module Tokens
7
+ module Base64s
8
+ class Null < Tokens::Null
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apiphobic/tokens/configuration'
4
+
5
+ module Apiphobic
6
+ module Tokens
7
+ module Configurable
8
+ module ClassMethods
9
+ def configure
10
+ yield configuration
11
+ end
12
+
13
+ def configuration
14
+ ::Apiphobic::Tokens::Configuration.instance
15
+ end
16
+ end
17
+
18
+ def self.included(base)
19
+ base.extend(ClassMethods)
20
+ end
21
+
22
+ def configuration
23
+ ::Apiphobic::Tokens::Configuration.instance
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require 'openssl'
5
+
6
+ module Apiphobic
7
+ module Tokens
8
+ class Configuration
9
+ include Singleton
10
+
11
+ attr_writer :available_audiences,
12
+ :available_issuers,
13
+ :available_roles,
14
+ :available_subjects,
15
+ :default_audience,
16
+ :default_availability_leeway_in_seconds,
17
+ :default_expiration_in_minutes,
18
+ :default_expiration_leeway_in_seconds,
19
+ :default_issuer,
20
+ :default_roles,
21
+ :default_subject,
22
+ :private_key,
23
+ :type
24
+
25
+ def available_audiences
26
+ @available_audiences || []
27
+ end
28
+
29
+ def available_issuers
30
+ @available_issuers || []
31
+ end
32
+
33
+ def available_roles
34
+ @available_roles || []
35
+ end
36
+
37
+ def available_subjects
38
+ @available_subjects || []
39
+ end
40
+
41
+ def default_audience
42
+ @default_audience || 'public'
43
+ end
44
+
45
+ def default_availability_leeway_in_seconds
46
+ @default_availability_leeway_in_seconds || 5
47
+ end
48
+
49
+ def default_expiration_in_minutes
50
+ @default_expiration_in_minutes || (7 * 24 * 60)
51
+ end
52
+
53
+ def default_expiration_leeway_in_seconds
54
+ @default_expiration_leeway_in_seconds || 5
55
+ end
56
+
57
+ def default_issuer
58
+ @default_issuer || 'apiphobic'
59
+ end
60
+
61
+ def default_roles
62
+ @default_roles || %w{standard}
63
+ end
64
+
65
+ def default_subject
66
+ @default_subject || 'User'
67
+ end
68
+
69
+ def private_key
70
+ return unless @private_key
71
+ return @private_key if @private_key.respond_to?(:sign)
72
+
73
+ OpenSSL::PKey::RSA.new(@private_key)
74
+ end
75
+
76
+ def to_h
77
+ {
78
+ available_audiences: available_audiences,
79
+ available_issuers: available_issuers,
80
+ available_roles: available_roles,
81
+ available_subjects: available_subjects,
82
+ default_audience: default_audience,
83
+ default_availability_leeway_in_seconds: default_availability_leeway_in_seconds,
84
+ default_expiration_in_minutes: default_expiration_in_minutes,
85
+ default_expiration_leeway_in_seconds: default_expiration_leeway_in_seconds,
86
+ default_issuer: default_issuer,
87
+ default_roles: default_roles,
88
+ default_subject: default_subject,
89
+ private_key: private_key,
90
+ type: type,
91
+ }
92
+ end
93
+
94
+ def type
95
+ @type || 'JWE'
96
+ end
97
+ end
98
+
99
+ def self.configure
100
+ yield configuration
101
+ end
102
+
103
+ def self.configuration
104
+ Configuration.instance
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require 'erratum/errors/authentication/invalid_token'
5
+
6
+ module Apiphobic
7
+ module Tokens
8
+ class Invalid
9
+ include Singleton
10
+
11
+ def valid?
12
+ false
13
+ end
14
+
15
+ def blank?
16
+ false
17
+ end
18
+
19
+ def validate!
20
+ fail Erratum::Errors::InvalidToken
21
+ end
22
+
23
+ def to_h
24
+ [{}, {}]
25
+ end
26
+
27
+ def to_s
28
+ ''
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,328 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erratum'
4
+ require 'json/jwt'
5
+ require 'apiphobic/errors/expired'
6
+ require 'apiphobic/errors/future_issuance'
7
+ require 'apiphobic/errors/invalid_attribute'
8
+ require 'apiphobic/errors/unavailable'
9
+ require 'apiphobic/tokens/configurable'
10
+ require 'apiphobic/tokens/json_web_tokens/null'
11
+ require 'apiphobic/tokens/role_predicable'
12
+
13
+ # rubocop:disable Metrics/ClassLength
14
+ module Apiphobic
15
+ module Tokens
16
+ class JsonWebToken
17
+ include Configurable
18
+ include RolePredicable
19
+
20
+ TRANSFORMATION_EXCEPTIONS = [
21
+ JSON::JWT::Exception,
22
+ JSON::JWT::InvalidFormat,
23
+ JSON::JWT::VerificationFailed,
24
+ JSON::JWT::UnexpectedAlgorithm,
25
+ OpenSSL::PKey::RSAError,
26
+ OpenSSL::Cipher::CipherError,
27
+ ].freeze
28
+
29
+ attr_accessor :data,
30
+ :headers,
31
+ :private_key
32
+
33
+ def initialize(data:,
34
+ headers: {},
35
+ private_key: configuration.private_key)
36
+
37
+ self.data = data
38
+ self.headers = headers
39
+ self.private_key = private_key
40
+ end
41
+
42
+ def self.build_from_request(request_token)
43
+ return Tokens::JsonWebTokens::Null.instance unless request_token
44
+
45
+ data, headers = *request_token
46
+
47
+ new(data: data, headers: headers)
48
+ end
49
+
50
+ # rubocop:disable Metrics/ParameterLists, Metrics/LineLength
51
+ def self.build(id: SecureRandom.uuid,
52
+ audience: configuration.default_audience,
53
+ expiration: Time.now.utc.to_i + (60 * configuration.default_expiration_in_minutes),
54
+ issuer: configuration.default_issuer,
55
+ issued_at: Time.now.utc,
56
+ not_before: Time.now.utc,
57
+ owner: nil,
58
+ roles: configuration.default_roles,
59
+ subject: configuration.default_subject,
60
+ subject_id:,
61
+ token_private_key: configuration.private_key)
62
+
63
+ owner ||= subject_id
64
+
65
+ new(
66
+ private_key: token_private_key,
67
+ data: {
68
+ 'aud' => audience,
69
+ 'exp' => expiration.to_i,
70
+ 'iat' => issued_at.to_i,
71
+ 'iss' => issuer,
72
+ 'jti' => id,
73
+ 'nbf' => not_before.to_i,
74
+ 'own' => owner,
75
+ 'rol' => roles.join(','),
76
+ 'sid' => subject_id,
77
+ 'sub' => subject,
78
+ },
79
+ )
80
+ end
81
+ # rubocop:enable Metrics/ParameterLists, Metrics/LineLength
82
+
83
+ def available?
84
+ not_before <= (Time.now.to_i + configuration.default_availability_leeway_in_seconds)
85
+ end
86
+
87
+ def blank?
88
+ data.empty?
89
+ end
90
+
91
+ def present?
92
+ data.any?
93
+ end
94
+
95
+ def empty?
96
+ data.empty?
97
+ end
98
+
99
+ def expired?
100
+ expiration <= (Time.now.to_i - configuration.default_expiration_leeway_in_seconds)
101
+ end
102
+
103
+ def future_issuance?
104
+ issued_at > Time.now.to_f
105
+ end
106
+
107
+ def valid?
108
+ validate!
109
+ rescue Erratum::Errors::InvalidToken
110
+ false
111
+ end
112
+
113
+ def valid_audience?
114
+ configuration.available_audiences.empty? ||
115
+ configuration.available_audiences.include?(audience)
116
+ end
117
+
118
+ def valid_id?
119
+ id =~ /[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}/i
120
+ end
121
+
122
+ def valid_issuer?
123
+ configuration.available_issuers.empty? ||
124
+ configuration.available_issuers.include?(issuer)
125
+ end
126
+
127
+ def valid_role?
128
+ roles.empty? ||
129
+ configuration.available_roles.empty? ||
130
+ (configuration.available_roles & roles).any?
131
+ end
132
+
133
+ def valid_subject?
134
+ configuration.available_subjects.empty? ||
135
+ configuration.available_subjects.include?(subject)
136
+ end
137
+
138
+ def validate!
139
+ validate_id!
140
+ validate_availability!
141
+ validate_expiration!
142
+ validate_issuance!
143
+ validate_issuer!
144
+ validate_role!
145
+ validate_subject!
146
+
147
+ true
148
+ end
149
+
150
+ def audience
151
+ data['aud']
152
+ end
153
+
154
+ def issued_at
155
+ data['iat']
156
+ end
157
+
158
+ def issuer
159
+ data['iss']
160
+ end
161
+
162
+ def expiration
163
+ data['exp']
164
+ end
165
+
166
+ def expiration_date
167
+ Time.at(expiration)
168
+ end
169
+
170
+ def id
171
+ data['jti']
172
+ end
173
+
174
+ def not_before
175
+ data['nbf']
176
+ end
177
+
178
+ def not_before_date
179
+ Time.at(not_before)
180
+ end
181
+
182
+ def owner_id
183
+ data['own']
184
+ end
185
+
186
+ def subject_id
187
+ data['sid']
188
+ end
189
+
190
+ def subject
191
+ data['sub']
192
+ end
193
+
194
+ def roles
195
+ @roles ||= data.fetch('rol', '').split(',')
196
+ end
197
+
198
+ def to_h
199
+ [data, headers]
200
+ end
201
+
202
+ def to_jwt
203
+ @to_jwt ||= JSON::JWT.new(data)
204
+ end
205
+
206
+ def to_jwt_s
207
+ @to_jwt_s ||= to_jwt.to_s
208
+ end
209
+
210
+ def to_jws
211
+ @to_jws ||= to_jwt.sign(private_key, 'RS256')
212
+ end
213
+
214
+ def to_jws_s
215
+ @to_jws_s ||= to_jws.to_s
216
+ end
217
+
218
+ def to_jwe
219
+ @to_jwe ||= to_jws.encrypt(private_key, 'RSA-OAEP', 'A256GCM')
220
+ end
221
+
222
+ def to_jwe_s
223
+ @to_jwe_s ||= to_jwe.to_s
224
+ end
225
+
226
+ def self.from_jwe(encrypted_token,
227
+ private_key: configuration.private_key)
228
+
229
+ return JsonWebTokens::Null.instance if encrypted_token.to_s == ''
230
+
231
+ decrypted_token = JSON::JWT
232
+ .decode(encrypted_token, private_key)
233
+ .plain_text
234
+
235
+ from_jws(decrypted_token, private_key: private_key)
236
+ rescue *TRANSFORMATION_EXCEPTIONS => error
237
+ raise ::Erratum::Errors::InvalidToken.wrap(error)
238
+ end
239
+
240
+ def self.from_jws(signed_token,
241
+ private_key: configuration.private_key)
242
+
243
+ return JsonWebTokens::Null.instance if signed_token.to_s == ''
244
+
245
+ decoded = JSON::JWT.decode(signed_token, private_key)
246
+
247
+ new(data: decoded.to_h,
248
+ headers: decoded.header,
249
+ private_key: private_key)
250
+ rescue *TRANSFORMATION_EXCEPTIONS => error
251
+ raise ::Erratum::Errors::InvalidToken.wrap(error)
252
+ end
253
+
254
+ private
255
+
256
+ def validate_audience!
257
+ return true unless audience
258
+ return true if valid_audience?
259
+
260
+ Erratum.fail Errors::InvalidAttribute,
261
+ attribute_name: 'audience',
262
+ attribute_value: audience
263
+ end
264
+
265
+ def validate_availability!
266
+ return true unless not_before
267
+ return true if available?
268
+
269
+ Erratum.fail Errors::Unavailable,
270
+ available_at: not_before_date
271
+ end
272
+
273
+ def validate_expiration!
274
+ return true unless expiration
275
+ return true unless expired?
276
+
277
+ Erratum.fail Errors::Expired,
278
+ expired_at: expiration_date
279
+ end
280
+
281
+ def validate_id!
282
+ return true unless id
283
+ return true if valid_id?
284
+
285
+ Erratum.fail Errors::InvalidAttribute,
286
+ attribute_name: 'id',
287
+ attribute_value: id,
288
+ additional_message: 'It must be in the form of a UUID.'
289
+ end
290
+
291
+ def validate_issuance!
292
+ return true unless issued_at
293
+ return true unless future_issuance?
294
+
295
+ Erratum.fail Errors::FutureIssuance,
296
+ issued_at: issued_at_date
297
+ end
298
+
299
+ def validate_issuer!
300
+ return true unless issuer
301
+ return true if valid_issuer?
302
+
303
+ Erratum.fail Errors::InvalidAttribute,
304
+ attribute_name: 'issuer',
305
+ attribute_value: issuer
306
+ end
307
+
308
+ def validate_role!
309
+ return true unless roles
310
+ return true if valid_role?
311
+
312
+ Erratum.fail Errors::InvalidAttribute,
313
+ attribute_name: 'roles',
314
+ attribute_value: roles
315
+ end
316
+
317
+ def validate_subject!
318
+ return true unless subject
319
+ return true if valid_subject?
320
+
321
+ Erratum.fail Errors::InvalidAttribute,
322
+ attribute_name: 'subject',
323
+ attribute_value: subject
324
+ end
325
+ end
326
+ end
327
+ end
328
+ # rubocop:enable Metrics/ClassLength
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apiphobic/tokens/invalid'
4
+
5
+ module Apiphobic
6
+ module Tokens
7
+ module JsonWebTokens
8
+ class Invalid < Tokens::Invalid
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apiphobic/tokens/configurable'
4
+ require 'apiphobic/tokens/role_predicable'
5
+ require 'apiphobic/tokens/null'
6
+
7
+ module Apiphobic
8
+ module Tokens
9
+ module JsonWebTokens
10
+ class Null < Tokens::Null
11
+ include Configurable
12
+ include RolePredicable
13
+
14
+ def audience
15
+ nil
16
+ end
17
+
18
+ def issued_at
19
+ nil
20
+ end
21
+
22
+ def issuer
23
+ nil
24
+ end
25
+
26
+ def expiration
27
+ nil
28
+ end
29
+
30
+ def id
31
+ nil
32
+ end
33
+
34
+ def not_before
35
+ nil
36
+ end
37
+
38
+ def owner_id
39
+ nil
40
+ end
41
+
42
+ def subject_id
43
+ nil
44
+ end
45
+
46
+ def subject
47
+ nil
48
+ end
49
+
50
+ def roles
51
+ []
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apiphobic/tokens/json_web_token'
4
+
5
+ module Apiphobic
6
+ module Tokens
7
+ module JsonWebTokens
8
+ class PasswordReset < JsonWebToken
9
+ def self.build(expiration: Time.now.utc.to_i + (30 * 60),
10
+ roles: %w{password_reset},
11
+ **attrs)
12
+
13
+ super(expiration: expiration, roles: roles, **attrs)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module Apiphobic
6
+ module Tokens
7
+ class Null
8
+ include Singleton
9
+
10
+ def valid?
11
+ true
12
+ end
13
+
14
+ def validate!
15
+ true
16
+ end
17
+
18
+ def blank?
19
+ true
20
+ end
21
+
22
+ def to_h
23
+ [{}, {}]
24
+ end
25
+
26
+ def to_s
27
+ ''
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiphobic
4
+ module Tokens
5
+ module RolePredicable
6
+ def method_missing(name, *args)
7
+ if role_predicates.include?(name)
8
+ roles.map(&:to_s).include?(name.to_s.gsub(/\?\z/, ''))
9
+ else
10
+ super
11
+ end
12
+ end
13
+
14
+ def respond_to_missing?(name, include_all)
15
+ role_predicates.include?(name) || super
16
+ end
17
+
18
+ private
19
+
20
+ def role_predicates
21
+ configuration.available_roles.map { |r| "#{r}?".to_sym }
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiphobic
4
+ module Tokens
5
+ VERSION = '1.0.0'
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apiphobic-tokens
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - thegranddesign
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDqjCCApKgAwIBAgIBATANBgkqhkiG9w0BAQUFADBNMREwDwYDVQQDDAhydWJ5
14
+ Z2VtczEjMCEGCgmSJomT8ixkARkWE2xpdmluZ2hpZ2hvbnRoZWJsb2cxEzARBgoJ
15
+ kiaJk/IsZAEZFgNjb20wHhcNMTcwODAyMjI1OTM1WhcNMTgwODAyMjI1OTM1WjBN
16
+ MREwDwYDVQQDDAhydWJ5Z2VtczEjMCEGCgmSJomT8ixkARkWE2xpdmluZ2hpZ2hv
17
+ bnRoZWJsb2cxEzARBgoJkiaJk/IsZAEZFgNjb20wggEiMA0GCSqGSIb3DQEBAQUA
18
+ A4IBDwAwggEKAoIBAQDtLa7+7p49gW15OgOyRZad/F92iZcMdDjZ2kAxZlviXgVe
19
+ PCtjfdURobH+YMdt++6eRkE25utIFqHyN51Shxfdc21T3fPQe/ZEoMyiJK4tYzbh
20
+ 7VjNJG4ldvKKpS1p7iVz9imnyTxNwb0JaIOsOFCA04T0u6aCQi2acNvAPLviXk0q
21
+ xJ/CKjI4QUTZKVrBt8Q1Egrp2yzmEnSNftDuTbBb8m4vDR+w325CwbKCgycHJ1/g
22
+ YZ3FO76TzJuRVbsYS/bU5XKHVEpkeFmWBqEXsk4DuUIWLa6WZEJcoZf+YP+1pycG
23
+ 7YqSbydpINtEdopD+EEI+g+zNJ4nSI8/eQcQyEjBAgMBAAGjgZQwgZEwCQYDVR0T
24
+ BAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFDWuVrg4ve0vLu71kqiGdyBnzJGV
25
+ MCsGA1UdEQQkMCKBIHJ1YnlnZW1zQGxpdmluZ2hpZ2hvbnRoZWJsb2cuY29tMCsG
26
+ A1UdEgQkMCKBIHJ1YnlnZW1zQGxpdmluZ2hpZ2hvbnRoZWJsb2cuY29tMA0GCSqG
27
+ SIb3DQEBBQUAA4IBAQDJIpHjbBPGiaY4wOHcXlltQ+BMmhWQNh+1fZtyajQd+7Ay
28
+ fv23mO7Mf25Q38gopQlpaODkfxq54Jt8FvQbr5RYRS4j+JEKb75NgrAtehd8USUd
29
+ CiJJGH+yvGNWug9IGZCGX91HIbTsLQ5IUUWQasC5jGP8nxXufUr9xgAJZZenewny
30
+ B2qKu8q1A/kj6cw62RCY7yBmUXxlcJBj8g+JKYAFbYYKUdQSzf50k9IiWLWunJM+
31
+ Y2GAoHKstmfIVhc4XHOPpmTd2o/C29O9oaRgjrkfQEhF/KvJ/PhoV5hvokzsCyI5
32
+ iUeXPfvrGD/itYIBCgk+fnzyQQ4QtE5hTQaWQ3o2
33
+ -----END CERTIFICATE-----
34
+ date: 2018-05-01 00:00:00.000000000 Z
35
+ dependencies:
36
+ - !ruby/object:Gem::Dependency
37
+ name: json-jwt
38
+ requirement: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '1.9'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.9'
50
+ - !ruby/object:Gem::Dependency
51
+ name: erratum
52
+ requirement: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '3.1'
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '3.1'
64
+ - !ruby/object:Gem::Dependency
65
+ name: rspec
66
+ requirement: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '3.7'
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '3.7'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspeckled
80
+ requirement: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '0.0'
85
+ type: :development
86
+ prerelease: false
87
+ version_requirements: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '0.0'
92
+ - !ruby/object:Gem::Dependency
93
+ name: timecop
94
+ requirement: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: 0.9.0
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: 0.9.0
106
+ description: ''
107
+ email:
108
+ - rubygems@livinghighontheblog.com
109
+ executables: []
110
+ extensions: []
111
+ extra_rdoc_files: []
112
+ files:
113
+ - LICENSE.txt
114
+ - README.md
115
+ - lib/apiphobic-tokens.rb
116
+ - lib/apiphobic/errors/expired.rb
117
+ - lib/apiphobic/errors/future_issuance.rb
118
+ - lib/apiphobic/errors/invalid_attribute.rb
119
+ - lib/apiphobic/errors/unavailable.rb
120
+ - lib/apiphobic/tokens/base64.rb
121
+ - lib/apiphobic/tokens/base64s/invalid.rb
122
+ - lib/apiphobic/tokens/base64s/null.rb
123
+ - lib/apiphobic/tokens/configurable.rb
124
+ - lib/apiphobic/tokens/configuration.rb
125
+ - lib/apiphobic/tokens/invalid.rb
126
+ - lib/apiphobic/tokens/json_web_token.rb
127
+ - lib/apiphobic/tokens/json_web_tokens/invalid.rb
128
+ - lib/apiphobic/tokens/json_web_tokens/null.rb
129
+ - lib/apiphobic/tokens/json_web_tokens/password_reset.rb
130
+ - lib/apiphobic/tokens/null.rb
131
+ - lib/apiphobic/tokens/role_predicable.rb
132
+ - lib/apiphobic/tokens/version.rb
133
+ homepage: ''
134
+ licenses:
135
+ - MIT
136
+ metadata:
137
+ allowed_push_host: https://rubygems.org
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 2.7.6
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: API Tokens
158
+ test_files: []
Binary file