sorcery 0.11.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sorcery might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +435 -0
- data/.travis.yml +15 -24
- data/CHANGELOG.md +19 -0
- data/Gemfile +1 -1
- data/README.md +25 -5
- data/lib/generators/sorcery/templates/initializer.rb +66 -3
- data/lib/generators/sorcery/templates/migration/magic_login.rb +9 -0
- data/lib/generators/sorcery/templates/migration/reset_password.rb +1 -0
- data/lib/sorcery.rb +2 -0
- data/lib/sorcery/adapters/active_record_adapter.rb +2 -1
- data/lib/sorcery/controller.rb +2 -0
- data/lib/sorcery/controller/submodules/external.rb +9 -0
- data/lib/sorcery/controller/submodules/session_timeout.rb +1 -1
- data/lib/sorcery/model/config.rb +4 -1
- data/lib/sorcery/model/submodules/external.rb +15 -0
- data/lib/sorcery/model/submodules/magic_login.rb +134 -0
- data/lib/sorcery/model/submodules/reset_password.rb +18 -0
- data/lib/sorcery/model/temporary_token.rb +3 -1
- data/lib/sorcery/providers/vk.rb +3 -2
- data/lib/sorcery/test_helpers/rails/request.rb +20 -0
- data/lib/sorcery/version.rb +1 -1
- data/sorcery.gemspec +6 -5
- data/spec/active_record/user_magic_login_spec.rb +15 -0
- data/spec/providers/vk_spec.rb +41 -0
- data/spec/rails_app/app/mailers/sorcery_mailer.rb +7 -0
- data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.html.erb +13 -0
- data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.text.erb +6 -0
- data/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb +17 -0
- data/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +2 -0
- data/spec/shared_examples/user_magic_login_shared_examples.rb +150 -0
- data/spec/shared_examples/user_reset_password_shared_examples.rb +16 -0
- data/spec/sorcery_temporary_token_spec.rb +27 -0
- metadata +44 -13
data/.travis.yml
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
3
|
- jruby
|
4
|
-
- 2.
|
5
|
-
- 2.
|
6
|
-
- 2.
|
7
|
-
- 2.
|
8
|
-
- 2.4.0
|
4
|
+
- 2.2.9
|
5
|
+
- 2.3.6
|
6
|
+
- 2.4.3
|
7
|
+
- 2.5.0
|
9
8
|
|
10
9
|
env:
|
11
10
|
global:
|
@@ -29,29 +28,21 @@ matrix:
|
|
29
28
|
- rvm: jruby
|
30
29
|
|
31
30
|
exclude:
|
32
|
-
- rvm: 2.
|
33
|
-
gemfile: gemfiles/active_record-rails42.gemfile
|
34
|
-
|
35
|
-
- rvm: 2.0.0
|
36
|
-
gemfile: Gemfile
|
37
|
-
|
38
|
-
- rvm: 2.1.10
|
39
|
-
gemfile: Gemfile
|
40
|
-
|
41
|
-
- rvm: 2.2.6
|
31
|
+
- rvm: 2.2.9
|
42
32
|
gemfile: gemfiles/active_record-rails40.gemfile
|
43
|
-
|
44
|
-
- rvm: 2.3.3
|
33
|
+
- rvm: 2.3.6
|
45
34
|
gemfile: gemfiles/active_record-rails40.gemfile
|
46
|
-
|
47
|
-
- rvm: 2.4.0
|
35
|
+
- rvm: 2.4.3
|
48
36
|
gemfile: gemfiles/active_record-rails40.gemfile
|
49
|
-
|
50
|
-
- rvm: 2.4.0
|
37
|
+
- rvm: 2.4.3
|
51
38
|
gemfile: gemfiles/active_record-rails41.gemfile
|
52
|
-
|
53
|
-
|
39
|
+
- rvm: 2.4.3
|
40
|
+
gemfile: gemfiles/active_record-rails42.gemfile
|
41
|
+
- rvm: 2.5.0
|
42
|
+
gemfile: gemfiles/active_record-rails40.gemfile
|
43
|
+
- rvm: 2.5.0
|
44
|
+
gemfile: gemfiles/active_record-rails41.gemfile
|
45
|
+
- rvm: 2.5.0
|
54
46
|
gemfile: gemfiles/active_record-rails42.gemfile
|
55
|
-
|
56
47
|
- rvm: jruby
|
57
48
|
gemfile: Gemfile
|
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,30 @@
|
|
1
1
|
# Changelog
|
2
2
|
## HEAD
|
3
3
|
|
4
|
+
## 0.12.0
|
5
|
+
|
6
|
+
* Fix magic_login not inheriting from migration_class_name [#99](https://github.com/Sorcery/sorcery/pull/99)
|
7
|
+
* Update YARD dependency [#100](https://github.com/Sorcery/sorcery/pull/100)
|
8
|
+
* Make `#update_attributes` behave like `#update` [#98](https://github.com/Sorcery/sorcery/pull/98)
|
9
|
+
* Add tests to the magic login submodule [#95](https://github.com/Sorcery/sorcery/pull/95)
|
10
|
+
* Set user.stretches to 1 in test env by default [#81](https://github.com/Sorcery/sorcery/pull/81)
|
11
|
+
* Allow user to be loaded from other source when session expires. fix #89 [#94](https://github.com/Sorcery/sorcery/pull/94)
|
12
|
+
* Added a new ArgumentError for not defined user_class in config [#82](https://github.com/Sorcery/sorcery/pull/82)
|
13
|
+
* Updated Required Ruby version to 2.2 [#85](https://github.com/Sorcery/sorcery/pull/85)
|
14
|
+
* Add configuration for token randomness [#67](https://github.com/Sorcery/sorcery/pull/67)
|
15
|
+
* Add facebook user_info_path option to initializer.rb [#63](https://github.com/Sorcery/sorcery/pull/63)
|
16
|
+
* Add new function: `build_from` (allows building a user instance from OAuth without saving) [#54](https://github.com/Sorcery/sorcery/pull/54)
|
17
|
+
* Add rubocop configuration and TODO list [#107](https://github.com/Sorcery/sorcery/pull/107)
|
18
|
+
* Add support for VK OAuth (thanks to @Hirurg103) [#109](https://github.com/Sorcery/sorcery/pull/109)
|
19
|
+
* Fix token leak via referrer header [#56](https://github.com/Sorcery/sorcery/pull/56)
|
20
|
+
* Add `login_user` helper for request specs [#57](https://github.com/Sorcery/sorcery/pull/57)
|
21
|
+
|
4
22
|
## 0.11.0
|
5
23
|
|
6
24
|
* Refer to User before calling remove_const to avoid NameError [#58](https://github.com/Sorcery/sorcery/pull/58)
|
7
25
|
* Resurrect block authentication, showing auth failure reason. [#41](https://github.com/Sorcery/sorcery/pull/41)
|
8
26
|
* Add github scope option to initializer.rb [#50](https://github.com/Sorcery/sorcery/pull/50)
|
27
|
+
* Fix Facebook being broken due to API deprecation [#53](https://github.com/Sorcery/sorcery/pull/53)
|
9
28
|
|
10
29
|
## 0.10.3
|
11
30
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -22,6 +22,18 @@ Sorcery is a stripped-down, bare-bones authentication library, with which you ca
|
|
22
22
|
- Configuration over Confusion - Centralized (1 file), Simple & short configuration as possible, not drowning in syntactic sugar.
|
23
23
|
- Keep MVC cleanly separated - DB is for models, sessions are for controllers. Models stay unaware of sessions.
|
24
24
|
|
25
|
+
## Table of Contents
|
26
|
+
|
27
|
+
1. [Useful Links](#useful-links)
|
28
|
+
2. [API Summary](#api-summary)
|
29
|
+
3. [Installation](#installation)
|
30
|
+
4. [Configuration](#configuration)
|
31
|
+
5. [Full Features List by Module](#full-features-list-by-module)
|
32
|
+
6. [Planned Features](#planned-features)
|
33
|
+
7. [Contributing](#contributing)
|
34
|
+
8. [Contact](#contact)
|
35
|
+
9. [License](#license)
|
36
|
+
|
25
37
|
## Useful Links
|
26
38
|
|
27
39
|
- [Documentation](http://rubydoc.info/gems/sorcery)
|
@@ -70,6 +82,7 @@ require_login_from_http_basic # This is a before action
|
|
70
82
|
login_at(provider) # Sends the user to an external service (Facebook, Twitter, etc.) to authenticate
|
71
83
|
login_from(provider) # Tries to login from the external provider's callback
|
72
84
|
create_from(provider) # Create the user in the local app database
|
85
|
+
build_from(provider) # Build user instance using user_info_mappings
|
73
86
|
```
|
74
87
|
|
75
88
|
### Remember Me
|
@@ -209,16 +222,23 @@ Have an idea? Let us know, and it might get into the gem!
|
|
209
222
|
|
210
223
|
Bug reports and pull requests are welcome on GitHub at https://github.com/Sorcery/sorcery.
|
211
224
|
|
212
|
-
|
213
|
-
|
225
|
+
- [Git Workflow](https://github.com/Sorcery/sorcery/wiki/Git-Workflow)
|
226
|
+
- [Running the specs](https://github.com/Sorcery/sorcery/wiki/Running-the-specs)
|
214
227
|
|
215
228
|
## Contact
|
216
229
|
|
217
230
|
Feel free to ask questions using these contact details:
|
218
231
|
|
219
|
-
|
220
|
-
|
221
|
-
-
|
232
|
+
**Current Maintainers:**
|
233
|
+
|
234
|
+
- Chase Gilliam ([@Ch4s3](https://github.com/Ch4s3)) | [Email](mailto:chase.gilliam@gmail.com)
|
235
|
+
- Josh Buker ([@athix](https://github.com/athix)) | [Email](mailto:jbuker@aeonsplice.com)
|
236
|
+
|
237
|
+
**Past Maintainers:**
|
238
|
+
|
239
|
+
- Noam Ben-Ari ([@NoamB](https://github.com/NoamB)) | [Email](mailto:nbenari@gmail.com) | [Twitter](https://twitter.com/nbenari)
|
240
|
+
- Kir Shatrov ([@kirs](https://github.com/kirs)) | [Email](mailto:shatrov@me.com) | [Twitter](https://twitter.com/Kiiiir)
|
241
|
+
- Grzegorz Witek ([@arnvald](https://github.com/arnvald)) | [Email](mailto:arnvald.to@gmail.com) | [Twitter](https://twitter.com/arnvald)
|
222
242
|
|
223
243
|
## License
|
224
244
|
|
@@ -29,6 +29,12 @@ Rails.application.config.sorcery.configure do |config|
|
|
29
29
|
#
|
30
30
|
# config.remember_me_httponly =
|
31
31
|
|
32
|
+
# Set token randomness. (e.g. user activation tokens)
|
33
|
+
# The length of the result string is about 4/3 of `token_randomness`.
|
34
|
+
# Default: `15`
|
35
|
+
#
|
36
|
+
# config.token_randomness =
|
37
|
+
|
32
38
|
# -- session timeout --
|
33
39
|
# How long in seconds to keep the session alive.
|
34
40
|
# Default: `3600`
|
@@ -106,7 +112,8 @@ Rails.application.config.sorcery.configure do |config|
|
|
106
112
|
# config.facebook.key = ""
|
107
113
|
# config.facebook.secret = ""
|
108
114
|
# config.facebook.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=facebook"
|
109
|
-
# config.facebook.
|
115
|
+
# config.facebook.user_info_path = "me?fields=email"
|
116
|
+
# config.facebook.user_info_mapping = {:email => "email"}
|
110
117
|
# config.facebook.access_permissions = ["email", "publish_actions"]
|
111
118
|
# config.facebook.display = "page"
|
112
119
|
# config.facebook.api_version = "v2.3"
|
@@ -147,6 +154,7 @@ Rails.application.config.sorcery.configure do |config|
|
|
147
154
|
# config.vk.secret = ""
|
148
155
|
# config.vk.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=vk"
|
149
156
|
# config.vk.user_info_mapping = {:login => "domain", :name => "full_name"}
|
157
|
+
# config.vk.api_version = "5.71"
|
150
158
|
#
|
151
159
|
# config.slack.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=slack"
|
152
160
|
# config.slack.key = ''
|
@@ -225,9 +233,9 @@ Rails.application.config.sorcery.configure do |config|
|
|
225
233
|
# user.salt_attribute_name =
|
226
234
|
|
227
235
|
# how many times to apply encryption to the password.
|
228
|
-
# Default: `nil`
|
236
|
+
# Default: 1 in test env, `nil` otherwise
|
229
237
|
#
|
230
|
-
|
238
|
+
user.stretches = 1 if Rails.env.test?
|
231
239
|
|
232
240
|
# encryption key used to encrypt reversible encryptions such as AES256.
|
233
241
|
# WARNING: If used for users' passwords, changing this key will leave passwords undecryptable!
|
@@ -358,6 +366,61 @@ Rails.application.config.sorcery.configure do |config|
|
|
358
366
|
# Default: `5 * 60`
|
359
367
|
#
|
360
368
|
# user.reset_password_time_between_emails =
|
369
|
+
|
370
|
+
# access counter to a reset password page attribute name
|
371
|
+
# Default: `:access_count_to_reset_password_page`
|
372
|
+
#
|
373
|
+
# user.reset_password_page_access_count_attribute_name =
|
374
|
+
|
375
|
+
# -- magic_login --
|
376
|
+
# magic login code attribute name.
|
377
|
+
# Default: `:magic_login_token`
|
378
|
+
#
|
379
|
+
# user.magic_login_token_attribute_name =
|
380
|
+
|
381
|
+
|
382
|
+
# expires at attribute name.
|
383
|
+
# Default: `:magic_login_token_expires_at`
|
384
|
+
#
|
385
|
+
# user.magic_login_token_expires_at_attribute_name =
|
386
|
+
|
387
|
+
|
388
|
+
# when was email sent, used for hammering protection.
|
389
|
+
# Default: `:magic_login_email_sent_at`
|
390
|
+
#
|
391
|
+
# user.magic_login_email_sent_at_attribute_name =
|
392
|
+
|
393
|
+
|
394
|
+
# mailer class. Needed.
|
395
|
+
# Default: `nil`
|
396
|
+
#
|
397
|
+
# user.magic_login_mailer_class =
|
398
|
+
|
399
|
+
|
400
|
+
# magic login email method on your mailer class.
|
401
|
+
# Default: `:magic_login_email`
|
402
|
+
#
|
403
|
+
# user.magic_login_email_method_name =
|
404
|
+
|
405
|
+
|
406
|
+
# when true sorcery will not automatically
|
407
|
+
# email magic login details and allow you to
|
408
|
+
# manually handle how and when email is sent
|
409
|
+
# Default: `true`
|
410
|
+
#
|
411
|
+
# user.magic_login_mailer_disabled =
|
412
|
+
|
413
|
+
|
414
|
+
# how many seconds before the request expires. nil for never expires.
|
415
|
+
# Default: `nil`
|
416
|
+
#
|
417
|
+
# user.magic_login_expiration_period =
|
418
|
+
|
419
|
+
|
420
|
+
# hammering protection, how long in seconds to wait before allowing another email to be sent.
|
421
|
+
# Default: `5 * 60`
|
422
|
+
#
|
423
|
+
# user.magic_login_time_between_emails =
|
361
424
|
|
362
425
|
# -- brute_force_protection --
|
363
426
|
# Failed logins attribute name.
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class SorceryMagicLogin < <%= migration_class_name %>
|
2
|
+
def change
|
3
|
+
add_column :<%= model_class_name.tableize %>, :magic_login_token, :string, :default => nil
|
4
|
+
add_column :<%= model_class_name.tableize %>, :magic_login_token_expires_at, :datetime, :default => nil
|
5
|
+
add_column :<%= model_class_name.tableize %>, :magic_login_email_sent_at, :datetime, :default => nil
|
6
|
+
|
7
|
+
add_index :<%= model_class_name.tableize %>, :magic_login_token
|
8
|
+
end
|
9
|
+
end
|
@@ -3,6 +3,7 @@ class SorceryResetPassword < <%= migration_class_name %>
|
|
3
3
|
add_column :<%= model_class_name.tableize %>, :reset_password_token, :string, :default => nil
|
4
4
|
add_column :<%= model_class_name.tableize %>, :reset_password_token_expires_at, :datetime, :default => nil
|
5
5
|
add_column :<%= model_class_name.tableize %>, :reset_password_email_sent_at, :datetime, :default => nil
|
6
|
+
add_column :<%= model_class_name.tableize %>, :access_count_to_reset_password_page, :integer, :default => 0
|
6
7
|
|
7
8
|
add_index :<%= model_class_name.tableize %>, :reset_password_token
|
8
9
|
end
|
data/lib/sorcery.rb
CHANGED
@@ -18,6 +18,7 @@ module Sorcery
|
|
18
18
|
require 'sorcery/model/submodules/activity_logging'
|
19
19
|
require 'sorcery/model/submodules/brute_force_protection'
|
20
20
|
require 'sorcery/model/submodules/external'
|
21
|
+
require 'sorcery/model/submodules/magic_login'
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
@@ -56,6 +57,7 @@ module Sorcery
|
|
56
57
|
module Rails
|
57
58
|
require 'sorcery/test_helpers/rails/controller'
|
58
59
|
require 'sorcery/test_helpers/rails/integration'
|
60
|
+
require 'sorcery/test_helpers/rails/request'
|
59
61
|
end
|
60
62
|
|
61
63
|
module Internal
|
@@ -6,7 +6,8 @@ module Sorcery
|
|
6
6
|
@model.send(:"#{name}=", value)
|
7
7
|
end
|
8
8
|
primary_key = @model.class.primary_key
|
9
|
-
@model.class.where(:"#{primary_key}" => @model.send(:"#{primary_key}")).update_all(attrs)
|
9
|
+
updated_count = @model.class.where(:"#{primary_key}" => @model.send(:"#{primary_key}")).update_all(attrs)
|
10
|
+
updated_count == 1
|
10
11
|
end
|
11
12
|
|
12
13
|
def save(options = {})
|
data/lib/sorcery/controller.rb
CHANGED
@@ -155,6 +155,8 @@ module Sorcery
|
|
155
155
|
|
156
156
|
def user_class
|
157
157
|
@user_class ||= Config.user_class.to_s.constantize
|
158
|
+
rescue NameError
|
159
|
+
raise ArgumentError, 'You have incorrectly defined user_class or have forgotten to define it in intitializer file (config.user_class = \'User\').'
|
158
160
|
end
|
159
161
|
end
|
160
162
|
end
|
@@ -187,6 +187,15 @@ module Sorcery
|
|
187
187
|
@user = user_class.create_from_provider(provider_name, @user_hash[:uid], attrs, &block)
|
188
188
|
end
|
189
189
|
|
190
|
+
# follows the same patterns as create_from, but builds the user instead of creating
|
191
|
+
def build_from(provider_name, &block)
|
192
|
+
sorcery_fetch_user_hash provider_name
|
193
|
+
config = user_class.sorcery_config
|
194
|
+
|
195
|
+
attrs = user_attrs(@provider.user_info_mapping, @user_hash)
|
196
|
+
@user = user_class.build_from_provider(attrs, &block)
|
197
|
+
end
|
198
|
+
|
190
199
|
def user_attrs(user_info_mapping, user_hash)
|
191
200
|
attrs = {}
|
192
201
|
user_info_mapping.each do |k, v|
|
@@ -39,7 +39,7 @@ module Sorcery
|
|
39
39
|
session_to_use = Config.session_timeout_from_last_action ? session[:last_action_time] : session[:login_time]
|
40
40
|
if session_to_use && sorcery_session_expired?(session_to_use.to_time)
|
41
41
|
reset_sorcery_session
|
42
|
-
|
42
|
+
remove_instance_variable :@current_user if defined? @current_user
|
43
43
|
else
|
44
44
|
session[:last_action_time] = Time.now.in_time_zone
|
45
45
|
end
|
data/lib/sorcery/model/config.rb
CHANGED
@@ -35,6 +35,8 @@ module Sorcery
|
|
35
35
|
attr_accessor :email_delivery_method
|
36
36
|
# an array of method names to call after configuration by user. used internally.
|
37
37
|
attr_accessor :after_config
|
38
|
+
# Set token randomness
|
39
|
+
attr_accessor :token_randomness
|
38
40
|
|
39
41
|
# change default encryption_provider.
|
40
42
|
attr_reader :encryption_provider
|
@@ -61,7 +63,8 @@ module Sorcery
|
|
61
63
|
:@subclasses_inherit_config => false,
|
62
64
|
:@before_authenticate => [],
|
63
65
|
:@after_config => [],
|
64
|
-
:@email_delivery_method => default_email_delivery_method
|
66
|
+
:@email_delivery_method => default_email_delivery_method,
|
67
|
+
:@token_randomness => 15
|
65
68
|
}
|
66
69
|
reset!
|
67
70
|
end
|
@@ -73,6 +73,21 @@ module Sorcery
|
|
73
73
|
end
|
74
74
|
user
|
75
75
|
end
|
76
|
+
|
77
|
+
# NOTE: Should this build the authentication as well and return [user, auth]?
|
78
|
+
# Currently, users call this function for the user and call add_provider_to_user after saving
|
79
|
+
def build_from_provider(attrs)
|
80
|
+
user = new
|
81
|
+
attrs.each do |k, v|
|
82
|
+
user.send(:"#{k}=", v)
|
83
|
+
end
|
84
|
+
|
85
|
+
if block_given?
|
86
|
+
return false unless yield user
|
87
|
+
end
|
88
|
+
|
89
|
+
user
|
90
|
+
end
|
76
91
|
end
|
77
92
|
|
78
93
|
module InstanceMethods
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module Sorcery
|
2
|
+
module Model
|
3
|
+
module Submodules
|
4
|
+
# This submodule adds the ability to login via email without password.
|
5
|
+
# When the user requests an email is sent to him with a url.
|
6
|
+
# The url includes a token, which is also saved with the user's record in the db.
|
7
|
+
# The token has configurable expiration.
|
8
|
+
# When the user clicks the url in the email, providing the token has not yet expired,
|
9
|
+
# he will be able to login.
|
10
|
+
#
|
11
|
+
# When using this submodule, supplying a mailer is mandatory.
|
12
|
+
module MagicLogin
|
13
|
+
def self.included(base)
|
14
|
+
base.sorcery_config.class_eval do
|
15
|
+
attr_accessor :magic_login_token_attribute_name, # magic login code attribute name.
|
16
|
+
:magic_login_token_expires_at_attribute_name, # expires at attribute name.
|
17
|
+
:magic_login_email_sent_at_attribute_name, # when was email sent, used for hammering
|
18
|
+
# protection.
|
19
|
+
|
20
|
+
:magic_login_mailer_class, # mailer class. Needed.
|
21
|
+
|
22
|
+
:magic_login_mailer_disabled, # when true sorcery will not automatically
|
23
|
+
# email magic login details and allow you to
|
24
|
+
# manually handle how and when email is sent
|
25
|
+
|
26
|
+
:magic_login_email_method_name, # magic login email method on your
|
27
|
+
# mailer class.
|
28
|
+
|
29
|
+
:magic_login_expiration_period, # how many seconds before the request
|
30
|
+
# expires. nil for never expires.
|
31
|
+
|
32
|
+
:magic_login_time_between_emails # hammering protection, how long to wait
|
33
|
+
# before allowing another email to be sent.
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
base.sorcery_config.instance_eval do
|
38
|
+
@defaults.merge!(:@magic_login_token_attribute_name => :magic_login_token,
|
39
|
+
:@magic_login_token_expires_at_attribute_name => :magic_login_token_expires_at,
|
40
|
+
:@magic_login_email_sent_at_attribute_name => :magic_login_email_sent_at,
|
41
|
+
:@magic_login_mailer_class => nil,
|
42
|
+
:@magic_login_mailer_disabled => true,
|
43
|
+
:@magic_login_email_method_name => :magic_login_email,
|
44
|
+
:@magic_login_expiration_period => 15 * 60,
|
45
|
+
:@magic_login_time_between_emails => 5 * 60)
|
46
|
+
|
47
|
+
reset!
|
48
|
+
end
|
49
|
+
|
50
|
+
base.extend(ClassMethods)
|
51
|
+
|
52
|
+
base.sorcery_config.after_config << :validate_mailer_defined
|
53
|
+
base.sorcery_config.after_config << :define_magic_login_fields
|
54
|
+
|
55
|
+
base.send(:include, InstanceMethods)
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
module ClassMethods
|
60
|
+
# Find user by token, also checks for expiration.
|
61
|
+
# Returns the user if token found and is valid.
|
62
|
+
def load_from_magic_login_token(token)
|
63
|
+
token_attr_name = @sorcery_config.magic_login_token_attribute_name
|
64
|
+
token_expiration_date_attr = @sorcery_config.magic_login_token_expires_at_attribute_name
|
65
|
+
load_from_token(token, token_attr_name, token_expiration_date_attr)
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
|
70
|
+
# This submodule requires the developer to define his own mailer class to be used by it
|
71
|
+
# when magic_login_mailer_disabled is false
|
72
|
+
def validate_mailer_defined
|
73
|
+
msg = "To use magic_login submodule, you must define a mailer (config.magic_login_mailer_class = YourMailerClass)."
|
74
|
+
raise ArgumentError, msg if @sorcery_config.magic_login_mailer_class.nil? and @sorcery_config.magic_login_mailer_disabled == false
|
75
|
+
end
|
76
|
+
|
77
|
+
def define_magic_login_fields
|
78
|
+
sorcery_adapter.define_field sorcery_config.magic_login_token_attribute_name, String
|
79
|
+
sorcery_adapter.define_field sorcery_config.magic_login_token_expires_at_attribute_name, Time
|
80
|
+
sorcery_adapter.define_field sorcery_config.magic_login_email_sent_at_attribute_name, Time
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
module InstanceMethods
|
86
|
+
# generates a reset code with expiration
|
87
|
+
def generate_magic_login_token!
|
88
|
+
config = sorcery_config
|
89
|
+
attributes = {config.magic_login_token_attribute_name => TemporaryToken.generate_random_token,
|
90
|
+
config.magic_login_email_sent_at_attribute_name => Time.now.in_time_zone}
|
91
|
+
attributes[config.magic_login_token_expires_at_attribute_name] = Time.now.in_time_zone + config.magic_login_expiration_period if config.magic_login_expiration_period
|
92
|
+
|
93
|
+
self.sorcery_adapter.update_attributes(attributes)
|
94
|
+
end
|
95
|
+
|
96
|
+
# generates a magic login code with expiration and sends an email to the user.
|
97
|
+
def deliver_magic_login_instructions!
|
98
|
+
mail = false
|
99
|
+
config = sorcery_config
|
100
|
+
# hammering protection
|
101
|
+
return false if !config.magic_login_time_between_emails.nil? &&
|
102
|
+
self.send(config.magic_login_email_sent_at_attribute_name) &&
|
103
|
+
self.send(config.magic_login_email_sent_at_attribute_name) > config.magic_login_time_between_emails.seconds.ago
|
104
|
+
|
105
|
+
self.class.sorcery_adapter.transaction do
|
106
|
+
generate_magic_login_token!
|
107
|
+
unless config.magic_login_mailer_disabled
|
108
|
+
send_magic_login_email!
|
109
|
+
mail = true
|
110
|
+
end
|
111
|
+
end
|
112
|
+
mail
|
113
|
+
end
|
114
|
+
|
115
|
+
# Clears the token.
|
116
|
+
def clear_magic_login_token!
|
117
|
+
config = sorcery_config
|
118
|
+
self.sorcery_adapter.update_attributes({
|
119
|
+
config.magic_login_token_attribute_name => nil,
|
120
|
+
config.magic_login_token_expires_at_attribute_name => nil
|
121
|
+
})
|
122
|
+
end
|
123
|
+
|
124
|
+
protected
|
125
|
+
|
126
|
+
def send_magic_login_email!
|
127
|
+
generic_send_email(:magic_login_email_method_name, :magic_login_mailer_class)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|