token_master 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +5 -1
- data/.travis.yml +7 -0
- data/Gemfile +5 -0
- data/README.md +9 -0
- data/Rakefile +0 -5
- data/lib/token_master.rb +1 -1
- data/lib/token_master/config.rb +30 -0
- data/lib/token_master/core.rb +56 -26
- data/lib/token_master/error.rb +23 -27
- data/lib/token_master/model.rb +16 -9
- data/lib/token_master/version.rb +2 -1
- metadata +2 -3
- data/Gemfile.lock +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd04ff6bcd482acefe3cb6f02eef4cd186ef8722
|
4
|
+
data.tar.gz: 741a3a5d77632dd6736aaa7546d1aac10eafc69b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f5937cb91561db88615e0dcb47e772fa0f9751ba70914cf2e7fcd7dc5e5b10a3226a95241c1fd666f9de0597ec860244c7d069914e09236d95260de28283ff2
|
7
|
+
data.tar.gz: 53ae9e0eb252be8acf703999eccba66fa40ac3d647137fa4e21db444b14e9b13fa3bad293dd89fdd76dd16e4c5b9fad1cc1a0173702658ef52a21813462fa5cb
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
+
[![GitHub](http://img.shields.io/badge/github-launchpadlab/token_master-blue.svg)](http://github.com/launchpadlab/token_master)
|
2
|
+
[![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/token_master)
|
3
|
+
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/token_master.svg)](https://badge.fury.io/rb/token_master)
|
1
5
|
[![Build Status](https://travis-ci.org/LaunchPadLab/token-master.svg?branch=master)](https://travis-ci.org/LaunchPadLab/token-master)
|
6
|
+
[![Test Coverage](https://codeclimate.com/github/LaunchPadLab/token-master/badges/coverage.svg)](https://codeclimate.com/github/LaunchPadLab/token-master/coverage)
|
7
|
+
[![License](http://img.shields.io/badge/license-MIT-yellowgreen.svg)](#license)
|
2
8
|
|
3
9
|
# token-master
|
4
10
|
User management logic using tokens
|
@@ -68,3 +74,6 @@ config.add_tokenable_options :confirm, TokenMaster::Config::DEFAULT_VALUES
|
|
68
74
|
|
69
75
|
config.add_tokenable_options :reset, token_lifetime: 1, required_params: [:password, :password_confirmation], token_length: 15
|
70
76
|
```
|
77
|
+
|
78
|
+
## Api Documentation
|
79
|
+
[Rdoc](http://www.rubydoc.info/gems/token_master)
|
data/Rakefile
CHANGED
data/lib/token_master.rb
CHANGED
data/lib/token_master/config.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
module TokenMaster
|
2
|
+
# `TokenMaster::Config` manages the configuration options for tokenable actions. These can be set with the initializer provided with the generator. Default values will be used if options are not specified
|
2
3
|
class Config
|
4
|
+
|
5
|
+
# Provides default values for a tokenable action:
|
6
|
+
# * `token_lifetime` is an integer representing the number of days before the token expires
|
7
|
+
# * `required_params` is an array of symbols, e.g., `[:password, :password_confirmation]`
|
8
|
+
# * `token_length` is an integer representing the number of characters in the token
|
3
9
|
DEFAULT_VALUES = {
|
4
10
|
token_lifetime: 14,
|
5
11
|
required_params: [],
|
@@ -8,26 +14,50 @@ module TokenMaster
|
|
8
14
|
|
9
15
|
attr_accessor :options
|
10
16
|
|
17
|
+
# Creates a new instance of `TokenMaster::Config`
|
11
18
|
def initialize
|
12
19
|
@options = {}
|
13
20
|
end
|
14
21
|
|
22
|
+
# Sets the key-value pairs needed to complete a tokenable action<br /> Key-value pairs used to complete a tokenable action are the `token_lifetime`, `required_params` (can be blank), and `token_length`
|
23
|
+
# @example Set a Tokenable Option
|
24
|
+
# config.add_tokenable_options(:invite, { token_lifetime: 10, required_params: [:password, :password_confirmation], token_length: 12 }) #=>
|
25
|
+
# { invite: {
|
26
|
+
# token_lifetime: 10,
|
27
|
+
# required_params: [:password, :password_confirmation],
|
28
|
+
# token_length: 12
|
29
|
+
# }
|
30
|
+
# }
|
31
|
+
# @param [Symbol] key the tokenable action
|
32
|
+
# @param [Symbol=>[Integer, String, Array]] params the key-value pairs
|
15
33
|
def add_tokenable_options(key, **params)
|
16
34
|
@options[key] = params
|
17
35
|
end
|
18
36
|
|
37
|
+
# Retrieves the `required_params` for a tokenable_action, either as set by the application, or by the default<br /> Used to update model attributes as needed for a given tokenable action
|
38
|
+
# @param [Symbol] key the tokenable action
|
39
|
+
# @return [Array] the `required_params` for a tokenable action
|
19
40
|
def get_required_params(key)
|
20
41
|
get_option(key, :required_params)
|
21
42
|
end
|
22
43
|
|
44
|
+
# Retrieves the `token_lifetime` for a tokenable action, either as set by the application, or by the default
|
45
|
+
# @param [Symbol] key the tokenable action
|
46
|
+
# @return [Integer] the `token_lifetime` for a tokenable action
|
23
47
|
def get_token_lifetime(key)
|
24
48
|
get_option(key, :token_lifetime)
|
25
49
|
end
|
26
50
|
|
51
|
+
# Retrieves the `token_length` for a tokenable action, either as set by the application, or by the default
|
52
|
+
# @param [Symbol] key the tokenable action
|
53
|
+
# @return [Integer] the `token_length` for a tokenable action
|
27
54
|
def get_token_length(key)
|
28
55
|
get_option(key, :token_length)
|
29
56
|
end
|
30
57
|
|
58
|
+
# Determines whether options are provided for a tokenable action
|
59
|
+
# @param [Symbol] key the tokenable action
|
60
|
+
# @return [Boolean] `true` => options are set; `false` => options are not set
|
31
61
|
def options_set?(key)
|
32
62
|
@options.key? key
|
33
63
|
end
|
data/lib/token_master/core.rb
CHANGED
@@ -2,10 +2,21 @@ require 'token_master/error'
|
|
2
2
|
require 'securerandom'
|
3
3
|
|
4
4
|
module TokenMaster
|
5
|
-
#
|
5
|
+
# `TokenMaster::Core` provides the core functionality of the TokenMaster gem. The `Core` module performs all of the logic of completing tokenable actions, and provides descriptive messages of the status or abilities of calls made.
|
6
6
|
module Core
|
7
7
|
class << self
|
8
|
-
|
8
|
+
|
9
|
+
# Completes the tokenable action for a tokenable model instance using a token, setting `tokenable_completed_at` to the time at completion
|
10
|
+
# @param [Object] klass the tokenable Class
|
11
|
+
# @param [String, Symbol] key the tokenable action
|
12
|
+
# @param token [String] the tokenable's token used to complete the action
|
13
|
+
# @param params [Symbol=>String] keyword arguments required to complete the tokenable action
|
14
|
+
# @raise [NotTokenableError] if the provided Class does not have the correct tokenable column
|
15
|
+
# @raise [TokenNotFoundError] if a tokenable instance cannot be found by the given token
|
16
|
+
# @raise [TokenCompletedError] if the tokenable action has already been completed, i.e., the tokenable instance has a timestamp in `tokenable_completed_at`
|
17
|
+
# @raise [TokenExpiredError] if the token is expired, i.e., the date is beyond the token's `created_at` plus `token_lifetime`
|
18
|
+
# @raise [MissingRequiredParamsError] if the params required by a tokenable are not provided
|
19
|
+
# @return [Object] tokenable Class instance
|
9
20
|
def do_by_token!(klass, key, token, **params)
|
10
21
|
check_manageable! klass, key
|
11
22
|
token_column = { token_col(key) => token }
|
@@ -19,7 +30,16 @@ module TokenMaster
|
|
19
30
|
model
|
20
31
|
end
|
21
32
|
|
22
|
-
#
|
33
|
+
# Completes the token action for a tokenable instance _without_ a token, setting the `tokenable_completed_at` to the time at completion.<br /> Usually implemented when you want to complete multiple tokenable actions at once, e.g., a user completes the invite action by setting up passwords, by default also completes the confirm action
|
34
|
+
# @example Force a Tokenable Action (Confirm)
|
35
|
+
# user.force_confirm! =>
|
36
|
+
# <User id: 205, name: "John Smith", email: "jsmith@example.com", confirm_token: nil, confirm_created_at: nil, confirm_sent_at: nil, confirm_completed_at: "2017-04-25 14:17:13">
|
37
|
+
# @param [Object] model the tokenable model instance
|
38
|
+
# @param [String, Symbol] key the tokenable action
|
39
|
+
# @param [Symbol=>String] params keyword arguments required to complete the tokenable action
|
40
|
+
# @raise [NotTokenableError] if the provided Class does not have the correct tokenable column
|
41
|
+
# @raise [MissingRequiredParamsError] if the params required by a tokenable are not provided
|
42
|
+
# @return [Object] tokenable Class instance
|
23
43
|
def force_tokenable!(model, key, **params)
|
24
44
|
check_manageable! model.class, key
|
25
45
|
check_params! key, params
|
@@ -30,10 +50,14 @@ module TokenMaster
|
|
30
50
|
model
|
31
51
|
end
|
32
52
|
|
33
|
-
#
|
53
|
+
# Generates a tokenable action token, sets the token and the time of creation on the tokenable model instance
|
54
|
+
# @param [Object] model the tokenable model instance
|
55
|
+
# @param [String, Symbol] key the tokenable action
|
56
|
+
# @param [Integer] token_length the length of the generated token, method will use configuration token_length if not provided otherwise
|
57
|
+
# @raise [NotTokenableError] if the provided Class does not have the correct tokenable column
|
58
|
+
# @return [String] token
|
34
59
|
def set_token!(model, key, token_length = nil)
|
35
60
|
check_manageable! model.class, key
|
36
|
-
check_configs_set! key
|
37
61
|
token_length ||= TokenMaster.config.get_token_length(key.to_sym)
|
38
62
|
token = generate_token token_length
|
39
63
|
|
@@ -47,7 +71,16 @@ module TokenMaster
|
|
47
71
|
token
|
48
72
|
end
|
49
73
|
|
50
|
-
#
|
74
|
+
# Accepts a block to pass on a generated token through a block, such as a mailer method, and sets `tokenable_sent_at` to the time the method is called
|
75
|
+
# @example Send Reset Instructions
|
76
|
+
# user.send_reset_instruction! { user.send_email } =>
|
77
|
+
# <User id: 205, name: "John Smith", email: "jsmith@example.com", reset_token: "3YcHkTJ7kXwV5wM", reset_created_at: 2017-04-25 14:20:54", reset_sent_at: "2017-04-25 14:22:42", reset_completed_at: nil>
|
78
|
+
# @param [Object] model the tokenable model instance
|
79
|
+
# @param [String, Symbol] key the tokenable action
|
80
|
+
# @raise [NotTokenableError] if the provided Class does not have the correct tokenable column
|
81
|
+
# @raise [TokenNotSetError] if the tokenable model instance does not have a token for the tokenable action
|
82
|
+
# @raise [TokenSentError] if this has already been called for the instance and tokenable action, i.e., `tokenable_sent_at` is not `nil`
|
83
|
+
# @return [Object] tokenable model instance
|
51
84
|
def send_instructions!(model, key)
|
52
85
|
check_manageable! model.class, key
|
53
86
|
check_token_set! model, key
|
@@ -59,7 +92,16 @@ module TokenMaster
|
|
59
92
|
model.save(validate: false)
|
60
93
|
end
|
61
94
|
|
62
|
-
#
|
95
|
+
# Provides the status of the tokenable action, whether the action has been completed, the token has been sent, the token is expired, or the token has only been created
|
96
|
+
# @param [Object] model the tokenable model instance
|
97
|
+
# @param [String, Symbol] key the tokenable action
|
98
|
+
# @raise [NotTokenableError] if the provided Class does not have the correct tokenable column
|
99
|
+
# @return [String] status of the tokenable action:
|
100
|
+
# * completed
|
101
|
+
# * sent
|
102
|
+
# * expired
|
103
|
+
# * created
|
104
|
+
# * no token
|
63
105
|
def status(model, key)
|
64
106
|
check_manageable! model.class, key
|
65
107
|
return 'completed' if completed?(model, key)
|
@@ -93,12 +135,8 @@ module TokenMaster
|
|
93
135
|
TokenMaster.config.get_token_lifetime(key.to_sym)
|
94
136
|
end
|
95
137
|
|
96
|
-
def required_params(key)
|
97
|
-
TokenMaster.config.required_params(key.to_sym)
|
98
|
-
end
|
99
|
-
|
100
138
|
def check_manageable!(klass, key)
|
101
|
-
raise NotTokenable, "#{klass} not #{key}able" unless manageable?(klass, key)
|
139
|
+
raise Errors::NotTokenable, "#{klass} not #{key}able" unless manageable?(klass, key)
|
102
140
|
end
|
103
141
|
|
104
142
|
def manageable?(klass, key)
|
@@ -112,21 +150,17 @@ module TokenMaster
|
|
112
150
|
).all? { |attr| column_names.include? attr }
|
113
151
|
end
|
114
152
|
|
115
|
-
def check_configs_set!(key)
|
116
|
-
raise NotConfigured, 'You have not set the configurations for this tokenable.' unless TokenMaster.config.options_set?(key.to_sym)
|
117
|
-
end
|
118
|
-
|
119
153
|
def check_params!(key, params)
|
120
154
|
required_params = TokenMaster.config.get_required_params(key.to_sym)
|
121
|
-
raise MissingRequiredParams, 'You did not pass in the required params for this tokenable' unless required_params.all? do |k|
|
155
|
+
raise Errors::MissingRequiredParams, 'You did not pass in the required params for this tokenable' unless required_params.all? do |k|
|
122
156
|
params.keys.include? k
|
123
157
|
end
|
124
158
|
end
|
125
159
|
|
126
160
|
def check_token_active!(model, key)
|
127
|
-
raise TokenNotFound, "#{key} token not found" unless model
|
128
|
-
raise TokenCompleted, "#{key} already completed" if completed?(model, key)
|
129
|
-
raise TokenExpired, "#{key} token expired" unless token_active?(model, key)
|
161
|
+
raise Errors::TokenNotFound, "#{key} token not found" unless model
|
162
|
+
raise Errors::TokenCompleted, "#{key} already completed" if completed?(model, key)
|
163
|
+
raise Errors::TokenExpired, "#{key} token expired" unless token_active?(model, key)
|
130
164
|
end
|
131
165
|
|
132
166
|
def token_active?(model, key)
|
@@ -136,7 +170,7 @@ module TokenMaster
|
|
136
170
|
end
|
137
171
|
|
138
172
|
def check_instructions_sent!(model, key)
|
139
|
-
raise TokenSent, "#{key} already sent" if instructions_sent?(model, key)
|
173
|
+
raise Errors::TokenSent, "#{key} already sent" if instructions_sent?(model, key)
|
140
174
|
end
|
141
175
|
|
142
176
|
def instructions_sent?(model, key)
|
@@ -148,17 +182,13 @@ module TokenMaster
|
|
148
182
|
end
|
149
183
|
|
150
184
|
def check_token_set!(model, key)
|
151
|
-
raise TokenNotSet, "#{key}_token not set" unless token_set?(model, key)
|
185
|
+
raise Errors::TokenNotSet, "#{key}_token not set" unless token_set?(model, key)
|
152
186
|
end
|
153
187
|
|
154
188
|
def completed?(model, key)
|
155
189
|
model.send(completed_at_col(key)).present?
|
156
190
|
end
|
157
191
|
|
158
|
-
def check_completed!(model, key)
|
159
|
-
raise TokenNotCompleted, "#{key} not completed" unless completed?(model, key)
|
160
|
-
end
|
161
|
-
|
162
192
|
def generate_token(length)
|
163
193
|
rlength = (length * 3) / 4
|
164
194
|
SecureRandom.urlsafe_base64(rlength).tr('lIO0', 'sxyz')
|
data/lib/token_master/error.rb
CHANGED
@@ -1,36 +1,32 @@
|
|
1
1
|
module TokenMaster
|
2
|
+
# `TokenMaster::Errors` holds all of the error classes used in applying TokenMaster
|
3
|
+
module Errors
|
4
|
+
# The base class for all errors raised by TokenMaster
|
5
|
+
class Error < StandardError; end
|
2
6
|
|
3
|
-
|
4
|
-
|
7
|
+
# Raised when the attributes for a tokenable do not exist.
|
8
|
+
# This could result from a migration not being run or a spelling error
|
9
|
+
class NotTokenable < Error; end
|
5
10
|
|
6
|
-
|
7
|
-
|
11
|
+
# Raised when the required parameters for a tokenable are not provided.
|
12
|
+
# This typically happens with reset and invite tokenables, that might require both `password` and `password_confirmation` fields,
|
13
|
+
# but only one is provided to the method
|
14
|
+
class MissingRequiredParams < Error; end
|
8
15
|
|
9
|
-
|
10
|
-
|
11
|
-
class NotTokenable < Error; end
|
16
|
+
# Raised when the tokenable instance is not found
|
17
|
+
class TokenNotFound < Error; end
|
12
18
|
|
13
|
-
|
14
|
-
|
19
|
+
# Raised when the status of the token is reviewed, but the tokenable action has already been completed
|
20
|
+
class TokenCompleted < Error; end
|
15
21
|
|
16
|
-
|
17
|
-
|
22
|
+
# Raised when the token has expired based on the tokenable's `token_lifetime`
|
23
|
+
class TokenExpired < Error; end
|
18
24
|
|
19
|
-
|
20
|
-
|
25
|
+
# Raised when the tokenable instructions have already been sent when calling `send_tokenable_instructions!`
|
26
|
+
class TokenSent < Error; end
|
21
27
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
# TODO
|
26
|
-
class TokenNotCompleted < Error; end
|
27
|
-
|
28
|
-
# TODO
|
29
|
-
class TokenExpired < Error; end
|
30
|
-
|
31
|
-
# TODO
|
32
|
-
class TokenSent < Error; end
|
33
|
-
|
34
|
-
# TODO
|
35
|
-
class TokenNotSet < Error; end
|
28
|
+
# Raised when the tokenable model instance does not have a token set for a tokenable action
|
29
|
+
class TokenNotSet < Error; end
|
30
|
+
end
|
36
31
|
end
|
32
|
+
|
data/lib/token_master/model.rb
CHANGED
@@ -1,34 +1,41 @@
|
|
1
1
|
module TokenMaster
|
2
2
|
|
3
|
-
#
|
3
|
+
# `TokenMaster::Model` provides the interface to the app it is used in, providing access to its public methods by invoking `TokenMaster::ClassMethods` and definiing the appropriate methods on the app model(s).
|
4
4
|
module Model
|
5
|
+
# Includes `TokenMaster::Model` and extends `TokenMaster::ClassMethods` to the class it's used with (automatically included via Railties)
|
5
6
|
def self.included(base)
|
6
7
|
base.extend(ClassMethods)
|
7
8
|
end
|
8
9
|
|
10
|
+
# `TokenMaster::ClassMethods` defines methods on the tokenable Class to be used in applying TokenMaster
|
9
11
|
module ClassMethods
|
10
|
-
#
|
12
|
+
# Iterates over each of the tokenables provided in the generator arguments to define the appropriate TokenMaster methods on the tokenable model
|
11
13
|
def token_master(*tokenables)
|
12
14
|
tokenables.each do |tokenable|
|
13
15
|
# instance methods
|
14
|
-
|
16
|
+
|
17
|
+
# Defines a method on the tokenable model instance to generate a tokenable action token, e.g., `user.set_confim_token!`
|
15
18
|
define_method("set_#{tokenable}_token!") do
|
16
19
|
TokenMaster::Core.set_token!(self, tokenable)
|
17
20
|
end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
+
|
22
|
+
# Defines a method on the tokenable model instance to send tokenable action instructions, e.g., `user.send_confim_instructions!`. Accepts a block with app logic to send instructions.
|
23
|
+
define_method("send_#{tokenable}_instructions!") do |&email|
|
24
|
+
TokenMaster::Core.send_instructions!(self, tokenable, &email)
|
21
25
|
end
|
22
|
-
|
26
|
+
|
27
|
+
# Defines a method on the tokenable model instance to retrieve the status of a tokenable action, e.g., `user.confim_status`
|
23
28
|
define_method("#{tokenable}_status") do
|
24
29
|
TokenMaster::Core.status(self, tokenable)
|
25
30
|
end
|
26
|
-
|
31
|
+
|
32
|
+
# Defines a method on the tokenable model instance to force the completion of a tokenable action, e.g., `user.force_confim!`. Accepts keyword arguments for `required_params`.
|
27
33
|
define_method("force_#{tokenable}!") do |**params|
|
28
34
|
TokenMaster::Core.force_tokenable!(self, tokenable, **params)
|
29
35
|
end
|
30
36
|
# class methods
|
31
|
-
|
37
|
+
|
38
|
+
# Defines a method on the tokenable model class to completed a tokenable action given a token, e.g., `User.confim_by_token!`. Takes the token and accepts any keyword arguments for `required_params`.
|
32
39
|
define_singleton_method("#{tokenable}_by_token!") do |token, **params|
|
33
40
|
TokenMaster::Core.do_by_token!(self, tokenable, token, **params)
|
34
41
|
end
|
data/lib/token_master/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: token_master
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dave Corwin
|
@@ -61,7 +61,6 @@ files:
|
|
61
61
|
- ".ruby-version"
|
62
62
|
- ".travis.yml"
|
63
63
|
- Gemfile
|
64
|
-
- Gemfile.lock
|
65
64
|
- LICENSE.txt
|
66
65
|
- README.md
|
67
66
|
- Rakefile
|
@@ -98,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
97
|
version: '0'
|
99
98
|
requirements: []
|
100
99
|
rubyforge_project:
|
101
|
-
rubygems_version: 2.
|
100
|
+
rubygems_version: 2.5.1
|
102
101
|
signing_key:
|
103
102
|
specification_version: 4
|
104
103
|
summary: Token Master!
|
data/Gemfile.lock
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
token_master (0.0.1)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: https://rubygems.org/
|
8
|
-
specs:
|
9
|
-
minitest (5.10.1)
|
10
|
-
rake (10.4.2)
|
11
|
-
yard (0.9.8)
|
12
|
-
|
13
|
-
PLATFORMS
|
14
|
-
ruby
|
15
|
-
|
16
|
-
DEPENDENCIES
|
17
|
-
minitest (~> 5.10.1)
|
18
|
-
rake (~> 10.4.2)
|
19
|
-
token_master!
|
20
|
-
yard
|
21
|
-
|
22
|
-
BUNDLED WITH
|
23
|
-
1.14.5
|