token_master 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1166 -0
  3. data/README.md +58 -99
  4. data/dummy/.gitignore +18 -0
  5. data/dummy/Gemfile +30 -0
  6. data/dummy/Rakefile +6 -0
  7. data/dummy/app/assets/config/manifest.js +3 -0
  8. data/dummy/app/assets/images/.keep +0 -0
  9. data/dummy/app/assets/javascripts/application.js +16 -0
  10. data/dummy/app/assets/javascripts/cable.js +13 -0
  11. data/dummy/app/assets/javascripts/channels/.keep +0 -0
  12. data/dummy/app/assets/stylesheets/application.css +15 -0
  13. data/dummy/app/channels/application_cable/channel.rb +4 -0
  14. data/dummy/app/channels/application_cable/connection.rb +4 -0
  15. data/dummy/app/controllers/application_controller.rb +3 -0
  16. data/dummy/app/controllers/concerns/.keep +0 -0
  17. data/dummy/app/helpers/application_helper.rb +2 -0
  18. data/dummy/app/jobs/application_job.rb +2 -0
  19. data/dummy/app/mailers/application_mailer.rb +4 -0
  20. data/dummy/app/models/application_record.rb +3 -0
  21. data/dummy/app/models/concerns/.keep +0 -0
  22. data/dummy/app/models/user.rb +10 -0
  23. data/dummy/app/views/layouts/application.html.erb +14 -0
  24. data/dummy/app/views/layouts/mailer.html.erb +13 -0
  25. data/dummy/app/views/layouts/mailer.text.erb +1 -0
  26. data/dummy/bin/bundle +3 -0
  27. data/dummy/bin/rails +9 -0
  28. data/dummy/bin/rake +9 -0
  29. data/dummy/bin/setup +34 -0
  30. data/dummy/bin/spring +17 -0
  31. data/dummy/bin/update +29 -0
  32. data/dummy/config.ru +5 -0
  33. data/dummy/config/application.rb +15 -0
  34. data/dummy/config/boot.rb +3 -0
  35. data/dummy/config/cable.yml +9 -0
  36. data/dummy/config/database.yml +14 -0
  37. data/dummy/config/environment.rb +6 -0
  38. data/dummy/config/environments/development.rb +54 -0
  39. data/dummy/config/environments/test.rb +42 -0
  40. data/dummy/config/initializers/assets.rb +11 -0
  41. data/dummy/config/initializers/cookies_serializer.rb +5 -0
  42. data/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  43. data/dummy/config/initializers/new_framework_defaults.rb +24 -0
  44. data/dummy/config/initializers/session_store.rb +3 -0
  45. data/dummy/config/initializers/token_master.rb +29 -0
  46. data/dummy/config/initializers/wrap_parameters.rb +14 -0
  47. data/dummy/config/locales/en.yml +23 -0
  48. data/dummy/config/puma.rb +47 -0
  49. data/dummy/config/routes.rb +3 -0
  50. data/dummy/config/secrets.yml +9 -0
  51. data/dummy/config/spring.rb +6 -0
  52. data/dummy/db/migrate/20170505170857_create_users.rb +11 -0
  53. data/dummy/db/migrate/20170505171217_add_confirm_tokenable_to_users.rb +26 -0
  54. data/dummy/db/schema.rb +41 -0
  55. data/dummy/db/seeds.rb +14 -0
  56. data/dummy/spec/factories/users.rb +8 -0
  57. data/dummy/spec/models/user_spec.rb +12 -0
  58. data/dummy/spec/rails_helper.rb +54 -0
  59. data/dummy/spec/spec_helper.rb +85 -0
  60. data/dummy/spec/support/factory_bot.rb +3 -0
  61. data/dummy/spec/support/shoulda_matchers.rb +6 -0
  62. data/lib/token_master/core.rb +14 -0
  63. data/lib/token_master/model.rb +6 -1
  64. data/lib/token_master/version.rb +1 -1
  65. metadata +62 -3
data/README.md CHANGED
@@ -9,48 +9,17 @@
9
9
  [![Test Coverage](https://codeclimate.com/github/LaunchPadLab/token-master/badges/coverage.svg)](https://codeclimate.com/github/LaunchPadLab/token-master/coverage)
10
10
  [![License](http://img.shields.io/badge/license-MIT-yellowgreen.svg)](#license)
11
11
 
12
- **Minimal** and **Simple** user management for Ruby and Rails applications.
13
-
14
- * [Motivation](#motivation)
15
- * [Token Master](#enter-the-token-master)
16
- * [Quick Start](#quick-start)
17
- * [Details](#details)
18
- * [FAQ](#faq)
19
- * [Comparisons](#comparisons)
20
-
21
- ## Motivation
22
- Whenever your application manages users, you will inevitably need to handle email confirmation, password reset, user invitations, and other authentication flows. While not too complicated, they are sort of annoying to implement and some great libraries have our backs. [Devise][devise] and [Sorcery][sorcery] are great options that we have used in the past, but we found ourselves wanting both a little less and a little more. See our more detailed thoughts on these options [below](#comparisons).
23
-
24
- ### User Authentication Flows
25
- Email confirmation, password reset, user invitations are all variations of the same process:
26
-
27
- 1. Create a unique token that allows the user temporary and limited access to your application
28
- 2. Notify the user with a link to redeem the token
29
- 3. Redeem or reject the token based on certain conditions (ex. validity, expiration, etc)
30
- 4. Update the user with any new information
31
- 5. Revoke the token
32
-
33
- They are all *tokenable* activities, and all you need to do them is a **Token Master**!
34
-
35
- ## Enter the Token Master
36
-
37
- ### Front-end agnostic
12
+ Simple token logic for providing (temporary) restricted access.
38
13
  No routing, views, controllers, or mailers, just logic that you can use wherever and whenever you want.
39
14
 
40
- ### Authentication strategy agnostic
41
- Token Master does not handle user authentication, it assumes you have this covered with `has_secure_password`, Devise, Sorcery, or other solutions
42
-
43
- ### Unobtrusive
44
- Does not take over your app, minimal magic, and only if you want it. Token Master works with your existing authentication solution.
45
-
46
- ### Flexible
47
- Works for APIs, ERB apps and everything in between.
15
+ Tokens can be used for any action that needs the access, such as inviting, confirming, or resetting passwords. These actions can be considered *tokenable actions*.
48
16
 
49
- ### Simple
50
- Only 5 methods and you may not even use them all!
17
+ Tokenable actions can be attributed to any model, not just users. These models then become *tokenable models*.
51
18
 
52
- ### Helpful errors
53
- We take the approach of raising an error whenever anything unexpected happens and provide a specific error with a helpful message to aid your debugging and testing experience.
19
+ * [Quick Start](#quick-start)
20
+ * [Details](#details)
21
+ * [FAQ](#faq)
22
+ * [Motivation](#motivation)
54
23
 
55
24
  ## Quick Start
56
25
 
@@ -67,19 +36,19 @@ Or install it yourself as:
67
36
 
68
37
  `$ gem install token_master`
69
38
 
70
- ### Add a *tokenable*
39
+ ### Usage
71
40
 
72
- ##### These examples assume Rails 5, but anything >= 4 will work.
41
+ ##### These examples assume Rails 5, but anything >= 4 will work
73
42
 
74
- Let's say you want to add email confirmation flow to your User.
43
+ Let's say you want to add email confirmation flow to your User. Your **tokenable model** then is the **User** model, and the **tokenable action** might be something like *confirm* (although you can name it anything, as long as you are consistent).
75
44
 
76
- 1. Create and run a migration to add the necessary columns to the `users` table
45
+ 1. Create and run a migration to add the necessary columns to the `users` table like so:
77
46
  ```
78
47
  bundle exec rails generate token_master User confirm
79
48
  bundle exec rails db:migrate
80
49
  ```
81
50
 
82
- 2. Add Token Master to the User class
51
+ 2. Add the Token Master `token_master` hook to the User class, and pass in the symbol for your *tokenable action*:
83
52
 
84
53
  ```
85
54
  class User < ApplicationRecord
@@ -87,13 +56,11 @@ class User < ApplicationRecord
87
56
  end
88
57
  ```
89
58
 
90
- 3. Somewhere during the signup flow, generate and send the token
59
+ 3. Somewhere during the signup flow, generate and send the token:
91
60
 
92
61
  ```
93
62
  class UsersController < ApplicationController
94
63
 
95
- ...
96
-
97
64
  def create
98
65
 
99
66
  # Creating the user is up to you, here is an example
@@ -103,39 +70,40 @@ class UsersController < ApplicationController
103
70
  password_confirmation: params[:password_confirmation]
104
71
  )
105
72
 
106
- # Generate and save a unique token
73
+ # Generate and save a unique token on the new user
107
74
  token = user.set_confirm_token!
108
75
 
109
76
  # Mark the token as sent
110
77
  user.send_confirm_instructions! do
111
- # Sending the email is up to you
78
+ # Sending the email is up to you, by passing a block here:
112
79
  UserMailer.send_confirm(user) # or some other logic
113
80
  end
114
81
  end
115
82
 
116
- ...
83
+ def resend_confirmation_instructions
84
+
85
+ # if you have a 'resend instructions?' flow you can generate a new token and send instructions again in one step
86
+ user.resend_confirm_instructions! do
87
+ # Sending the email is up to you, by passing a block here:
88
+ UserMailer.send_confirm(user) # or some other logic
89
+ end
90
+ end
117
91
 
118
92
  end
119
93
  ```
120
94
 
121
- 4. Somewhere during the confirmation flow, find and confirm the User
95
+ 4. Somewhere during the confirmation flow, find and confirm the user:
122
96
 
123
97
  ```
124
98
  class UsersController < ApplicationController
125
-
126
- ...
127
-
128
99
  def confirm
129
100
 
130
- # find the user by the token and mark the token as completed
101
+ # finds the user by the token, and mark the token as completed
131
102
  user = User.confirm_by_token!(params[:token])
132
103
 
133
104
  ...
134
105
 
135
106
  end
136
-
137
- ...
138
-
139
107
  end
140
108
  ```
141
109
 
@@ -148,16 +116,16 @@ When you ran the generator
148
116
  ```
149
117
  bundle exec rails generate token_master User confirm
150
118
  ```
151
- you provided 2 variables:
152
- * `User` - The class name of the model to which you are adding the *tokenable*
153
- * `confirm` - The name of the *tokenable*
119
+ you provided two arguments:
120
+ * `User` - The class name of the model to which you are adding the *tokenable action*
121
+ * `confirm` - The name of the *tokenable action*
154
122
 
155
123
  Both of these could be anything, as long as you use the same class and name later on. If you like, you can create multiple *tokenables* at the same time, just add more space-separated *tokenable* names when calling the generator:
156
124
  ```
157
- bundle exec rails generate token_master User confirm invite reset ...
125
+ bundle exec rails generate token_master User confirm invite reset
158
126
  ```
159
127
 
160
- Running the generator does 2 things:
128
+ Running the generator does two things:
161
129
  1. Creates a migration file in `#{Rails.root}/db/migrate` that looks like:
162
130
 
163
131
  ```
@@ -182,22 +150,21 @@ TokenMaster.config do |config|
182
150
  # Set up your configurations for each *tokenable* using the methods at the bottom of this file.
183
151
  # Example: For `confirm` logic:
184
152
  #
185
- # config.add_tokenable_options :confirm,
186
- # token_lifetime: 15, # days
187
- # required_params: [:email],
188
- # token_length: 30 # characters
189
- #
190
153
  # Default values:
191
154
  # token_lifetime = 15 # days
192
155
  # required_params = []
193
156
  # token_length = 20 # characters
194
- #
157
+
158
+ config.add_tokenable_options :confirm,
159
+ token_lifetime: 15, # days
160
+ required_params: [:email],
161
+ token_length: 30 # characters
195
162
  end
196
163
  ```
197
- The default values will be used unless you configure them otherwise. These options can be set for each *tokenable*.
164
+ The default values will be used unless you configure them otherwise. These options can be set for each *tokenable action*.
198
165
 
199
166
  ### The Model
200
- When you added the *tokenable* to your model
167
+ When you added the Token Master hook and *tokenable action* to your model
201
168
  ```
202
169
  class User < ApplicationRecord
203
170
  token_master :confirm
@@ -207,57 +174,58 @@ just make sure the class `User` and *tokenable(s)* `:confirm` (this can be multi
207
174
 
208
175
  Ex.
209
176
  ```
210
- token_master :confirm, :invite, :reset, ...
177
+ token_master :confirm, :invite, :reset
211
178
  ```
212
179
 
213
- There are 2 tiny bits of magic here:
180
+ 1. The `token_master` hook is included automatically by Token Master in your `ApplicationRecord` base class.
214
181
 
215
- 1. In Rails apps by default, the Token Master module is included in your `ApplicationRecord` base class. However, if necessary, you can add this yourself by including the following in your class:
182
+ However, if necessary, you can add this yourself by including the following in your class:
216
183
  ```
217
184
  include TokenMaster::Model
218
185
  ```
219
- This adds the `token_master` class method we used above.
186
+ This adds the `token_master` class method we used above, and you can make the same calls we described in the `confirm` example above.
187
+
188
+ 2. When you call the `token_master` class method, for each *tokenable action* you provide, a handful of methods are added to the class for each *tokenable action*, and named accordingly.
220
189
 
221
- 2. When you call the `token_master` class method, for each *tokenable* you provide, 5 methods are added to the class (assuming the *tokenable* below is `confirm`):
190
+ Assuming the *tokenable action* below is `confirm`, the methods would look like this:
222
191
 
223
192
  Instance methods
224
193
  * `set_confirm_token!`
225
194
  * `send_confirm_instructions!`
195
+ * `resend_confirm_instructions!`
226
196
  * `confirm_status`
227
197
  * `force_confirm!`
228
198
 
229
199
  Class methods
230
200
  * `confirm_by_token!`
231
201
 
232
- In addition to the 3 you have already seen in action, there is also:
202
+ In addition to the three you have already seen in action, there is also:
233
203
 
234
- `confirm_status` - returns the current status of the *tokenable*. This is one of:
204
+ `confirm_status` - returns the current status of the *tokenable action*. This is one of:
235
205
  * 'no token'
236
206
  * 'created'
237
207
  * 'sent'
238
208
  * 'completed'
239
209
  * 'expired'
240
210
 
241
- `force_confirm!` - forcibly completes the given *tokenable*
211
+ `force_confirm!` - forcibly completes the given *tokenable action*
242
212
 
243
213
  See the [Api Docs][docs] for more details.
244
214
 
245
215
  ## Advanced
246
- Sometimes in order to redeem a token, we want to make sure some additional information is present and possibly save that to our model. For example, when implementing a password reset flow, we want to update the User with the new password and make sure that its valid.
216
+ Sometimes in order to redeem a token, we want to make sure some additional information is present and possibly save that to our model.
217
+ For example, when implementing a password reset flow, we want to update the User with the new password and make sure it's valid.
247
218
 
248
219
  Assuming we are using `has_secure_password` or something similar all we need to do is:
249
- 1. Configure the *tokenable* to require these fields when redeeming the token
250
- ```
251
- # in ../initializers/token_master.rb
220
+ 1. Configure the *tokenable action* to require these fields when redeeming the token
252
221
 
222
+ **../initializers/token_master.rb**
223
+ ```
253
224
  TokenMaster.config do |config|
254
-
255
- ...
256
-
257
- config.add_tokenable_options :reset_password, required_params: [:password, :password_confirmation]
258
-
259
- ...
260
-
225
+ config.add_tokenable_options :reset_password,
226
+ token_lifetime: 1
227
+ required_params: [:password, :password_confirmation]
228
+ token_length: 30
261
229
  end
262
230
  ```
263
231
 
@@ -280,7 +248,7 @@ Yes! However, there is a small dependency on ActiveRecord, see below.
280
248
  ### Can I use this without ActiveRecord?
281
249
  Almost! There is only a slight dependence on a few ActiveRecord methods and its on our radar to refactor this a bit. In the meantime, a workaround is to make sure the class you are using implements `update`, `update!`, `save`, and `find_by`. In addition, you have to either add Token Master to your class with `include TokenMaster::Model` or use the Token Master core module explicitly:
282
250
 
283
- `user.set_confirm_token!(token)` == `TokenMaster::Core.set_token!(User, :confirm)`
251
+ `TokenMaster::Core.set_token!(User, :confirm)` (which is equivalent to `user.set_confirm_token!(token)`)
284
252
 
285
253
  See the [Api Docs][docs] for more details.
286
254
 
@@ -296,15 +264,6 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/Launch
296
264
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
297
265
 
298
266
  ---------------------------
299
-
300
- ## Comparisons
301
-
302
- ### Devise
303
- [Devise][devise] is an amazing gem! It is perfect when you want an all-in-one solution that handles user authentication and associated flows for your Rails/ERB app. Everything is in the box, including the routes, controllers, views, and even mailers to handle user auth. But we often use Rails as an API and/or wanted more control over all those pieces and it became difficult to peel back all the layers to just to confirm a user's email.
304
-
305
- ### Sorcery
306
- [Sorcery][sorcery] is great and we highly recommend it. It is closer to what we wanted but still was a bit more than we needed and even the < 20 methods seemed like more than necessary.
307
-
308
267
  <!-- Links -->
309
268
  [devise]: https://github.com/plataformatec/devise
310
269
  [sorcery]: https://github.com/Sorcery/sorcery
@@ -0,0 +1,18 @@
1
+ # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile '~/.gitignore_global'
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+
10
+ # Ignore all logfiles and tempfiles.
11
+ /log/*
12
+ /tmp/*
13
+ !/log/.keep
14
+ !/tmp/.keep
15
+
16
+ # Ignore Byebug command history file.
17
+ .byebug_history
18
+ .DS_Store
@@ -0,0 +1,30 @@
1
+ source 'https://rubygems.org'
2
+
3
+ git_source(:github) do |repo_name|
4
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
5
+ "https://github.com/#{repo_name}.git"
6
+ end
7
+
8
+ gem 'bcrypt', '~> 3.1.7'
9
+ gem 'factory_bot_rails'
10
+ gem 'faker'
11
+ gem 'pg', '~> 0.18'
12
+ # gem 'puma', '~> 3.0'
13
+ gem 'rails', '~> 5.0.2'
14
+ gem 'token_master', path: './../..'
15
+
16
+ group :development, :test do
17
+ gem 'byebug', platform: :mri
18
+ gem 'pry'
19
+ gem 'rspec-rails'
20
+ end
21
+
22
+ group :development do
23
+ gem 'web-console', '>= 3.3.0'
24
+ gem 'listen'
25
+ end
26
+
27
+ group :test do
28
+ gem 'shoulda-matchers'
29
+ gem 'rspec-collection_matchers'
30
+ end
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative 'config/application'
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../javascripts .js
3
+ //= link_directory ../stylesheets .css
File without changes
@@ -0,0 +1,16 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require turbolinks
16
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ // Action Cable provides the framework to deal with WebSockets in Rails.
2
+ // You can generate new channels where WebSocket features live using the rails generate channel command.
3
+ //
4
+ //= require action_cable
5
+ //= require_self
6
+ //= require_tree ./channels
7
+
8
+ (function() {
9
+ this.App || (this.App = {});
10
+
11
+ App.cable = ActionCable.createConsumer();
12
+
13
+ }).call(this);
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Channel < ActionCable::Channel::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Connection < ActionCable::Connection::Base
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery with: :exception
3
+ end
File without changes
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ class ApplicationJob < ActiveJob::Base
2
+ end
@@ -0,0 +1,4 @@
1
+ class ApplicationMailer < ActionMailer::Base
2
+ default from: 'from@example.com'
3
+ layout 'mailer'
4
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end