devise-bootstrap 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +31 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +1 -0
  7. data/app/controllers/devise/confirmations_controller.rb +47 -0
  8. data/app/controllers/devise/omniauth_callbacks_controller.rb +30 -0
  9. data/app/controllers/devise/passwords_controller.rb +70 -0
  10. data/app/controllers/devise/registrations_controller.rb +137 -0
  11. data/app/controllers/devise/sessions_controller.rb +53 -0
  12. data/app/controllers/devise/unlocks_controller.rb +46 -0
  13. data/app/controllers/devise_controller.rb +176 -0
  14. data/app/helpers/devise_helper.rb +25 -0
  15. data/app/mailers/devise/mailer.rb +20 -0
  16. data/app/views/devise/confirmations/new.html.erb +12 -0
  17. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  18. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  19. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  20. data/app/views/devise/passwords/edit.html.erb +16 -0
  21. data/app/views/devise/passwords/new.html.erb +12 -0
  22. data/app/views/devise/registrations/edit.html.erb +29 -0
  23. data/app/views/devise/registrations/new.html.erb +18 -0
  24. data/app/views/devise/sessions/new.html.erb +17 -0
  25. data/app/views/devise/shared/_links.erb +25 -0
  26. data/app/views/devise/unlocks/new.html.erb +12 -0
  27. data/config/locales/en.yml +59 -0
  28. data/devise-bootstrap.gemspec +30 -0
  29. data/gemfiles/Gemfile.rails-3.2-stable +29 -0
  30. data/gemfiles/Gemfile.rails-4.0-stable +29 -0
  31. data/gemfiles/Gemfile.rails-head +29 -0
  32. data/lib/devise/bootstrap.rb +7 -0
  33. data/lib/devise/bootstrap/version.rb +5 -0
  34. data/lib/devise/devise.rb +491 -0
  35. data/lib/devise/devise/controllers/helpers.rb +213 -0
  36. data/lib/devise/devise/controllers/rememberable.rb +47 -0
  37. data/lib/devise/devise/controllers/scoped_views.rb +17 -0
  38. data/lib/devise/devise/controllers/sign_in_out.rb +103 -0
  39. data/lib/devise/devise/controllers/store_location.rb +50 -0
  40. data/lib/devise/devise/controllers/url_helpers.rb +67 -0
  41. data/lib/devise/devise/delegator.rb +16 -0
  42. data/lib/devise/devise/failure_app.rb +205 -0
  43. data/lib/devise/devise/hooks/activatable.rb +11 -0
  44. data/lib/devise/devise/hooks/csrf_cleaner.rb +5 -0
  45. data/lib/devise/devise/hooks/forgetable.rb +9 -0
  46. data/lib/devise/devise/hooks/lockable.rb +7 -0
  47. data/lib/devise/devise/hooks/proxy.rb +21 -0
  48. data/lib/devise/devise/hooks/rememberable.rb +7 -0
  49. data/lib/devise/devise/hooks/timeoutable.rb +28 -0
  50. data/lib/devise/devise/hooks/trackable.rb +9 -0
  51. data/lib/devise/devise/mailers/helpers.rb +90 -0
  52. data/lib/devise/devise/mapping.rb +172 -0
  53. data/lib/devise/devise/models.rb +119 -0
  54. data/lib/devise/devise/models/authenticatable.rb +284 -0
  55. data/lib/devise/devise/models/confirmable.rb +295 -0
  56. data/lib/devise/devise/models/database_authenticatable.rb +164 -0
  57. data/lib/devise/devise/models/lockable.rb +196 -0
  58. data/lib/devise/devise/models/omniauthable.rb +27 -0
  59. data/lib/devise/devise/models/recoverable.rb +131 -0
  60. data/lib/devise/devise/models/registerable.rb +25 -0
  61. data/lib/devise/devise/models/rememberable.rb +129 -0
  62. data/lib/devise/devise/models/timeoutable.rb +49 -0
  63. data/lib/devise/devise/models/trackable.rb +35 -0
  64. data/lib/devise/devise/models/validatable.rb +66 -0
  65. data/lib/devise/devise/modules.rb +28 -0
  66. data/lib/devise/devise/omniauth.rb +28 -0
  67. data/lib/devise/devise/omniauth/config.rb +45 -0
  68. data/lib/devise/devise/omniauth/url_helpers.rb +18 -0
  69. data/lib/devise/devise/orm/active_record.rb +3 -0
  70. data/lib/devise/devise/orm/mongoid.rb +3 -0
  71. data/lib/devise/devise/parameter_filter.rb +40 -0
  72. data/lib/devise/devise/parameter_sanitizer.rb +99 -0
  73. data/lib/devise/devise/rails.rb +56 -0
  74. data/lib/devise/devise/rails/routes.rb +496 -0
  75. data/lib/devise/devise/rails/warden_compat.rb +22 -0
  76. data/lib/devise/devise/strategies/authenticatable.rb +167 -0
  77. data/lib/devise/devise/strategies/base.rb +20 -0
  78. data/lib/devise/devise/strategies/database_authenticatable.rb +23 -0
  79. data/lib/devise/devise/strategies/rememberable.rb +55 -0
  80. data/lib/devise/devise/test_helpers.rb +132 -0
  81. data/lib/devise/devise/time_inflector.rb +14 -0
  82. data/lib/devise/devise/token_generator.rb +70 -0
  83. data/lib/devise/devise/version.rb +3 -0
  84. data/lib/devise/generators/active_record/devise_generator.rb +73 -0
  85. data/lib/devise/generators/active_record/templates/migration.rb +18 -0
  86. data/lib/devise/generators/active_record/templates/migration_existing.rb +25 -0
  87. data/lib/devise/generators/devise/devise_generator.rb +26 -0
  88. data/lib/devise/generators/devise/install_generator.rb +29 -0
  89. data/lib/devise/generators/devise/orm_helpers.rb +51 -0
  90. data/lib/devise/generators/devise/views_generator.rb +135 -0
  91. data/lib/devise/generators/mongoid/devise_generator.rb +55 -0
  92. data/lib/devise/generators/templates/README +35 -0
  93. data/lib/devise/generators/templates/devise.rb +260 -0
  94. data/lib/devise/generators/templates/markerb/confirmation_instructions.markerb +5 -0
  95. data/lib/devise/generators/templates/markerb/reset_password_instructions.markerb +8 -0
  96. data/lib/devise/generators/templates/markerb/unlock_instructions.markerb +7 -0
  97. data/lib/devise/generators/templates/simple_form_for/confirmations/new.html.erb +16 -0
  98. data/lib/devise/generators/templates/simple_form_for/passwords/edit.html.erb +19 -0
  99. data/lib/devise/generators/templates/simple_form_for/passwords/new.html.erb +15 -0
  100. data/lib/devise/generators/templates/simple_form_for/registrations/edit.html.erb +27 -0
  101. data/lib/devise/generators/templates/simple_form_for/registrations/new.html.erb +17 -0
  102. data/lib/devise/generators/templates/simple_form_for/sessions/new.html.erb +15 -0
  103. data/lib/devise/generators/templates/simple_form_for/unlocks/new.html.erb +16 -0
  104. metadata +250 -0
@@ -0,0 +1,164 @@
1
+ require 'devise/strategies/database_authenticatable'
2
+ require 'bcrypt'
3
+
4
+ module Devise
5
+ # Digests the password using bcrypt.
6
+ def self.bcrypt(klass, password)
7
+ ::BCrypt::Password.create("#{password}#{klass.pepper}", cost: klass.stretches).to_s
8
+ end
9
+
10
+ module Models
11
+ # Authenticatable Module, responsible for encrypting password and validating
12
+ # authenticity of a user while signing in.
13
+ #
14
+ # == Options
15
+ #
16
+ # DatabaseAuthenticable adds the following options to devise_for:
17
+ #
18
+ # * +pepper+: a random string used to provide a more secure hash. Use
19
+ # `rake secret` to generate new keys.
20
+ #
21
+ # * +stretches+: the cost given to bcrypt.
22
+ #
23
+ # == Examples
24
+ #
25
+ # User.find(1).valid_password?('password123') # returns true/false
26
+ #
27
+ module DatabaseAuthenticatable
28
+ extend ActiveSupport::Concern
29
+
30
+ included do
31
+ attr_reader :password, :current_password
32
+ attr_accessor :password_confirmation
33
+ end
34
+
35
+ def self.required_fields(klass)
36
+ [:encrypted_password] + klass.authentication_keys
37
+ end
38
+
39
+ # Generates password encryption based on the given value.
40
+ def password=(new_password)
41
+ @password = new_password
42
+ self.encrypted_password = password_digest(@password) if @password.present?
43
+ end
44
+
45
+ # Verifies whether an password (ie from sign in) is the user password.
46
+ def valid_password?(password)
47
+ return false if encrypted_password.blank?
48
+ bcrypt = ::BCrypt::Password.new(encrypted_password)
49
+ password = ::BCrypt::Engine.hash_secret("#{password}#{self.class.pepper}", bcrypt.salt)
50
+ Devise.secure_compare(password, encrypted_password)
51
+ end
52
+
53
+ # Set password and password confirmation to nil
54
+ def clean_up_passwords
55
+ self.password = self.password_confirmation = nil
56
+ end
57
+
58
+ # Update record attributes when :current_password matches, otherwise
59
+ # returns error on :current_password.
60
+ #
61
+ # This method also rejects the password field if it is blank (allowing
62
+ # users to change relevant information like the e-mail without changing
63
+ # their password). In case the password field is rejected, the confirmation
64
+ # is also rejected as long as it is also blank.
65
+ def update_with_password(params, *options)
66
+ current_password = params.delete(:current_password)
67
+
68
+ if params[:password].blank?
69
+ params.delete(:password)
70
+ params.delete(:password_confirmation) if params[:password_confirmation].blank?
71
+ end
72
+
73
+ result = if valid_password?(current_password)
74
+ update_attributes(params, *options)
75
+ else
76
+ self.assign_attributes(params, *options)
77
+ self.valid?
78
+ self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
79
+ false
80
+ end
81
+
82
+ clean_up_passwords
83
+ result
84
+ end
85
+
86
+ # Updates record attributes without asking for the current password.
87
+ # Never allows a change to the current password. If you are using this
88
+ # method, you should probably override this method to protect other
89
+ # attributes you would not like to be updated without a password.
90
+ #
91
+ # Example:
92
+ #
93
+ # def update_without_password(params, *options)
94
+ # params.delete(:email)
95
+ # super(params)
96
+ # end
97
+ #
98
+ def update_without_password(params, *options)
99
+ params.delete(:password)
100
+ params.delete(:password_confirmation)
101
+
102
+ result = update_attributes(params, *options)
103
+ clean_up_passwords
104
+ result
105
+ end
106
+
107
+ # Destroy record when :current_password matches, otherwise returns
108
+ # error on :current_password. It also automatically rejects
109
+ # :current_password if it is blank.
110
+ def destroy_with_password(current_password)
111
+ result = if valid_password?(current_password)
112
+ destroy
113
+ else
114
+ self.valid?
115
+ self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
116
+ false
117
+ end
118
+
119
+ result
120
+ end
121
+
122
+ # A callback initiated after successfully authenticating. This can be
123
+ # used to insert your own logic that is only run after the user successfully
124
+ # authenticates.
125
+ #
126
+ # Example:
127
+ #
128
+ # def after_database_authentication
129
+ # self.update_attribute(:invite_code, nil)
130
+ # end
131
+ #
132
+ def after_database_authentication
133
+ end
134
+
135
+ # A reliable way to expose the salt regardless of the implementation.
136
+ def authenticatable_salt
137
+ encrypted_password[0,29] if encrypted_password
138
+ end
139
+
140
+ protected
141
+
142
+ # Digests the password using bcrypt. Custom encryption should override
143
+ # this method to apply their own algorithm.
144
+ #
145
+ # See https://github.com/plataformatec/devise-encryptable for examples
146
+ # of other encryption engines.
147
+ def password_digest(password)
148
+ Devise.bcrypt(self.class, password)
149
+ end
150
+
151
+ module ClassMethods
152
+ Devise::Models.config(self, :pepper, :stretches)
153
+
154
+ # We assume this method already gets the sanitized values from the
155
+ # DatabaseAuthenticatable strategy. If you are using this method on
156
+ # your own, be sure to sanitize the conditions hash to only include
157
+ # the proper fields.
158
+ def find_for_database_authentication(conditions)
159
+ find_for_authentication(conditions)
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,196 @@
1
+ require "devise/hooks/lockable"
2
+
3
+ module Devise
4
+ module Models
5
+ # Handles blocking a user access after a certain number of attempts.
6
+ # Lockable accepts two different strategies to unlock a user after it's
7
+ # blocked: email and time. The former will send an email to the user when
8
+ # the lock happens, containing a link to unlock its account. The second
9
+ # will unlock the user automatically after some configured time (ie 2.hours).
10
+ # It's also possible to setup lockable to use both email and time strategies.
11
+ #
12
+ # == Options
13
+ #
14
+ # Lockable adds the following options to +devise+:
15
+ #
16
+ # * +maximum_attempts+: how many attempts should be accepted before blocking the user.
17
+ # * +lock_strategy+: lock the user account by :failed_attempts or :none.
18
+ # * +unlock_strategy+: unlock the user account by :time, :email, :both or :none.
19
+ # * +unlock_in+: the time you want to lock the user after to lock happens. Only available when unlock_strategy is :time or :both.
20
+ # * +unlock_keys+: the keys you want to use when locking and unlocking an account
21
+ #
22
+ module Lockable
23
+ extend ActiveSupport::Concern
24
+
25
+ delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, to: "self.class"
26
+
27
+ def self.required_fields(klass)
28
+ attributes = []
29
+ attributes << :failed_attempts if klass.lock_strategy_enabled?(:failed_attempts)
30
+ attributes << :locked_at if klass.unlock_strategy_enabled?(:time)
31
+ attributes << :unlock_token if klass.unlock_strategy_enabled?(:email)
32
+
33
+ attributes
34
+ end
35
+
36
+ # Lock a user setting its locked_at to actual time.
37
+ # * +opts+: Hash options if you don't want to send email
38
+ # when you lock access, you could pass the next hash
39
+ # `{ send_instructions: false } as option`.
40
+ def lock_access!(opts = { })
41
+ self.locked_at = Time.now.utc
42
+
43
+ if unlock_strategy_enabled?(:email) && opts.fetch(:send_instructions, true)
44
+ send_unlock_instructions
45
+ else
46
+ save(validate: false)
47
+ end
48
+ end
49
+
50
+ # Unlock a user by cleaning locked_at and failed_attempts.
51
+ def unlock_access!
52
+ self.locked_at = nil
53
+ self.failed_attempts = 0 if respond_to?(:failed_attempts=)
54
+ self.unlock_token = nil if respond_to?(:unlock_token=)
55
+ save(validate: false)
56
+ end
57
+
58
+ # Verifies whether a user is locked or not.
59
+ def access_locked?
60
+ !!locked_at && !lock_expired?
61
+ end
62
+
63
+ # Send unlock instructions by email
64
+ def send_unlock_instructions
65
+ raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
66
+ self.unlock_token = enc
67
+ self.save(validate: false)
68
+ send_devise_notification(:unlock_instructions, raw, {})
69
+ raw
70
+ end
71
+
72
+ # Resend the unlock instructions if the user is locked.
73
+ def resend_unlock_instructions
74
+ if_access_locked { send_unlock_instructions }
75
+ end
76
+
77
+ # Overwrites active_for_authentication? from Devise::Models::Activatable for locking purposes
78
+ # by verifying whether a user is active to sign in or not based on locked?
79
+ def active_for_authentication?
80
+ super && !access_locked?
81
+ end
82
+
83
+ # Overwrites invalid_message from Devise::Models::Authenticatable to define
84
+ # the correct reason for blocking the sign in.
85
+ def inactive_message
86
+ access_locked? ? :locked : super
87
+ end
88
+
89
+ # Overwrites valid_for_authentication? from Devise::Models::Authenticatable
90
+ # for verifying whether a user is allowed to sign in or not. If the user
91
+ # is locked, it should never be allowed.
92
+ def valid_for_authentication?
93
+ return super unless persisted? && lock_strategy_enabled?(:failed_attempts)
94
+
95
+ # Unlock the user if the lock is expired, no matter
96
+ # if the user can login or not (wrong password, etc)
97
+ unlock_access! if lock_expired?
98
+
99
+ if super && !access_locked?
100
+ true
101
+ else
102
+ self.failed_attempts ||= 0
103
+ self.failed_attempts += 1
104
+ if attempts_exceeded?
105
+ lock_access! unless access_locked?
106
+ else
107
+ save(validate: false)
108
+ end
109
+ false
110
+ end
111
+ end
112
+
113
+ def unauthenticated_message
114
+ # If set to paranoid mode, do not show the locked message because it
115
+ # leaks the existence of an account.
116
+ if Devise.paranoid
117
+ super
118
+ elsif lock_strategy_enabled?(:failed_attempts) && last_attempt?
119
+ :last_attempt
120
+ elsif lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?
121
+ :locked
122
+ else
123
+ super
124
+ end
125
+ end
126
+
127
+ protected
128
+
129
+ def attempts_exceeded?
130
+ self.failed_attempts >= self.class.maximum_attempts
131
+ end
132
+
133
+ def last_attempt?
134
+ self.failed_attempts == self.class.maximum_attempts - 1
135
+ end
136
+
137
+ # Tells if the lock is expired if :time unlock strategy is active
138
+ def lock_expired?
139
+ if unlock_strategy_enabled?(:time)
140
+ locked_at && locked_at < self.class.unlock_in.ago
141
+ else
142
+ false
143
+ end
144
+ end
145
+
146
+ # Checks whether the record is locked or not, yielding to the block
147
+ # if it's locked, otherwise adds an error to email.
148
+ def if_access_locked
149
+ if access_locked?
150
+ yield
151
+ else
152
+ self.errors.add(Devise.unlock_keys.first, :not_locked)
153
+ false
154
+ end
155
+ end
156
+
157
+ module ClassMethods
158
+ # Attempt to find a user by its unlock keys. If a record is found, send new
159
+ # unlock instructions to it. If not user is found, returns a new user
160
+ # with an email not found error.
161
+ # Options must contain the user's unlock keys
162
+ def send_unlock_instructions(attributes={})
163
+ lockable = find_or_initialize_with_errors(unlock_keys, attributes, :not_found)
164
+ lockable.resend_unlock_instructions if lockable.persisted?
165
+ lockable
166
+ end
167
+
168
+ # Find a user by its unlock token and try to unlock it.
169
+ # If no user is found, returns a new user with an error.
170
+ # If the user is not locked, creates an error for the user
171
+ # Options must have the unlock_token
172
+ def unlock_access_by_token(unlock_token)
173
+ original_token = unlock_token
174
+ unlock_token = Devise.token_generator.digest(self, :unlock_token, unlock_token)
175
+
176
+ lockable = find_or_initialize_with_error_by(:unlock_token, unlock_token)
177
+ lockable.unlock_access! if lockable.persisted?
178
+ lockable.unlock_token = original_token
179
+ lockable
180
+ end
181
+
182
+ # Is the unlock enabled for the given unlock strategy?
183
+ def unlock_strategy_enabled?(strategy)
184
+ [:both, strategy].include?(self.unlock_strategy)
185
+ end
186
+
187
+ # Is the lock enabled for the given lock strategy?
188
+ def lock_strategy_enabled?(strategy)
189
+ self.lock_strategy == strategy
190
+ end
191
+
192
+ Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys)
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,27 @@
1
+ require 'devise/omniauth'
2
+
3
+ module Devise
4
+ module Models
5
+ # Adds OmniAuth support to your model.
6
+ #
7
+ # == Options
8
+ #
9
+ # Oauthable adds the following options to devise_for:
10
+ #
11
+ # * +omniauth_providers+: Which providers are available to this model. It expects an array:
12
+ #
13
+ # devise_for :database_authenticatable, :omniauthable, omniauth_providers: [:twitter]
14
+ #
15
+ module Omniauthable
16
+ extend ActiveSupport::Concern
17
+
18
+ def self.required_fields(klass)
19
+ []
20
+ end
21
+
22
+ module ClassMethods
23
+ Devise::Models.config(self, :omniauth_providers)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,131 @@
1
+ module Devise
2
+ module Models
3
+
4
+ # Recoverable takes care of resetting the user password and send reset instructions.
5
+ #
6
+ # ==Options
7
+ #
8
+ # Recoverable adds the following options to devise_for:
9
+ #
10
+ # * +reset_password_keys+: the keys you want to use when recovering the password for an account
11
+ #
12
+ # == Examples
13
+ #
14
+ # # resets the user password and save the record, true if valid passwords are given, otherwise false
15
+ # User.find(1).reset_password!('password123', 'password123')
16
+ #
17
+ # # only resets the user password, without saving the record
18
+ # user = User.find(1)
19
+ # user.reset_password('password123', 'password123')
20
+ #
21
+ # # creates a new token and send it with instructions about how to reset the password
22
+ # User.find(1).send_reset_password_instructions
23
+ #
24
+ module Recoverable
25
+ extend ActiveSupport::Concern
26
+
27
+ def self.required_fields(klass)
28
+ [:reset_password_sent_at, :reset_password_token]
29
+ end
30
+
31
+ # Update password saving the record and clearing token. Returns true if
32
+ # the passwords are valid and the record was saved, false otherwise.
33
+ def reset_password!(new_password, new_password_confirmation)
34
+ self.password = new_password
35
+ self.password_confirmation = new_password_confirmation
36
+
37
+ if valid?
38
+ clear_reset_password_token
39
+ after_password_reset
40
+ end
41
+
42
+ save
43
+ end
44
+
45
+ # Resets reset password token and send reset password instructions by email.
46
+ # Returns the token sent in the e-mail.
47
+ def send_reset_password_instructions
48
+ raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
49
+
50
+ self.reset_password_token = enc
51
+ self.reset_password_sent_at = Time.now.utc
52
+ self.save(validate: false)
53
+
54
+ send_devise_notification(:reset_password_instructions, raw, {})
55
+ raw
56
+ end
57
+
58
+ # Checks if the reset password token sent is within the limit time.
59
+ # We do this by calculating if the difference between today and the
60
+ # sending date does not exceed the confirm in time configured.
61
+ # Returns true if the resource is not responding to reset_password_sent_at at all.
62
+ # reset_password_within is a model configuration, must always be an integer value.
63
+ #
64
+ # Example:
65
+ #
66
+ # # reset_password_within = 1.day and reset_password_sent_at = today
67
+ # reset_password_period_valid? # returns true
68
+ #
69
+ # # reset_password_within = 5.days and reset_password_sent_at = 4.days.ago
70
+ # reset_password_period_valid? # returns true
71
+ #
72
+ # # reset_password_within = 5.days and reset_password_sent_at = 5.days.ago
73
+ # reset_password_period_valid? # returns false
74
+ #
75
+ # # reset_password_within = 0.days
76
+ # reset_password_period_valid? # will always return false
77
+ #
78
+ def reset_password_period_valid?
79
+ reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago
80
+ end
81
+
82
+ protected
83
+
84
+ # Removes reset_password token
85
+ def clear_reset_password_token
86
+ self.reset_password_token = nil
87
+ self.reset_password_sent_at = nil
88
+ end
89
+
90
+ def after_password_reset
91
+ end
92
+
93
+ module ClassMethods
94
+ # Attempt to find a user by its email. If a record is found, send new
95
+ # password instructions to it. If user is not found, returns a new user
96
+ # with an email not found error.
97
+ # Attributes must contain the user's email
98
+ def send_reset_password_instructions(attributes={})
99
+ recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
100
+ recoverable.send_reset_password_instructions if recoverable.persisted?
101
+ recoverable
102
+ end
103
+
104
+ # Attempt to find a user by its reset_password_token to reset its
105
+ # password. If a user is found and token is still valid, reset its password and automatically
106
+ # try saving the record. If not user is found, returns a new user
107
+ # containing an error in reset_password_token attribute.
108
+ # Attributes must contain reset_password_token, password and confirmation
109
+ def reset_password_by_token(attributes={})
110
+ original_token = attributes[:reset_password_token]
111
+ reset_password_token = Devise.token_generator.digest(self, :reset_password_token, original_token)
112
+
113
+ recoverable = find_or_initialize_with_error_by(:reset_password_token, reset_password_token)
114
+
115
+ if recoverable.persisted?
116
+ if recoverable.reset_password_period_valid?
117
+ recoverable.reset_password!(attributes[:password], attributes[:password_confirmation])
118
+ else
119
+ recoverable.errors.add(:reset_password_token, :expired)
120
+ end
121
+ end
122
+
123
+ recoverable.reset_password_token = original_token
124
+ recoverable
125
+ end
126
+
127
+ Devise::Models.config(self, :reset_password_keys, :reset_password_within)
128
+ end
129
+ end
130
+ end
131
+ end