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.

Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -0
  3. data/.rubocop_todo.yml +435 -0
  4. data/.travis.yml +15 -24
  5. data/CHANGELOG.md +19 -0
  6. data/Gemfile +1 -1
  7. data/README.md +25 -5
  8. data/lib/generators/sorcery/templates/initializer.rb +66 -3
  9. data/lib/generators/sorcery/templates/migration/magic_login.rb +9 -0
  10. data/lib/generators/sorcery/templates/migration/reset_password.rb +1 -0
  11. data/lib/sorcery.rb +2 -0
  12. data/lib/sorcery/adapters/active_record_adapter.rb +2 -1
  13. data/lib/sorcery/controller.rb +2 -0
  14. data/lib/sorcery/controller/submodules/external.rb +9 -0
  15. data/lib/sorcery/controller/submodules/session_timeout.rb +1 -1
  16. data/lib/sorcery/model/config.rb +4 -1
  17. data/lib/sorcery/model/submodules/external.rb +15 -0
  18. data/lib/sorcery/model/submodules/magic_login.rb +134 -0
  19. data/lib/sorcery/model/submodules/reset_password.rb +18 -0
  20. data/lib/sorcery/model/temporary_token.rb +3 -1
  21. data/lib/sorcery/providers/vk.rb +3 -2
  22. data/lib/sorcery/test_helpers/rails/request.rb +20 -0
  23. data/lib/sorcery/version.rb +1 -1
  24. data/sorcery.gemspec +6 -5
  25. data/spec/active_record/user_magic_login_spec.rb +15 -0
  26. data/spec/providers/vk_spec.rb +41 -0
  27. data/spec/rails_app/app/mailers/sorcery_mailer.rb +7 -0
  28. data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.html.erb +13 -0
  29. data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.text.erb +6 -0
  30. data/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb +17 -0
  31. data/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +2 -0
  32. data/spec/shared_examples/user_magic_login_shared_examples.rb +150 -0
  33. data/spec/shared_examples/user_reset_password_shared_examples.rb +16 -0
  34. data/spec/sorcery_temporary_token_spec.rb +27 -0
  35. metadata +44 -13
@@ -1,11 +1,10 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - jruby
4
- - 2.0.0
5
- - 2.1.10
6
- - 2.2.6
7
- - 2.3.3
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.0.0
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
- - rvm: 2.4.0
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
@@ -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
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'rails', '~> 5.0.0'
3
+ gem 'rails', '~> 5.1.0'
4
4
  gem 'rails-controller-testing'
5
5
  gem 'sqlite3'
6
6
  gem 'pry'
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
- If you feel sorcery has made your life easier, and you would like to express
213
- your thanks via a donation, my PayPal email is in the contact details.
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
- - Noam Ben-Ari: [nbenari@gmail.com](mailto:nbenari@gmail.com) (also PayPal), [Twitter](https://twitter.com/nbenari)
220
- - Kir Shatrov: [shatrov@me.com](mailto:shatrov@me.com), [Twitter](https://twitter.com/Kiiiir)
221
- - Grzegorz Witek: [arnvald.to@gmail.com](mailto:arnvald.to@gmail.com), [Twitter](https://twitter.com/arnvald)
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.user_info_mapping = {:email => "name"}
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
- # user.stretches =
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
@@ -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 = {})
@@ -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
- @current_user = nil
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
@@ -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