1
0
Fork 0
mirror of https://github.com/heartcombo/devise.git synced 2022-11-09 12:18:31 -05:00

Add reset_password_within configuration variable.

This commit is contained in:
Jean-Daniel Guyot 2011-01-24 23:48:44 +08:00 committed by José Valim
parent 8a3deb98cd
commit be2aeee70f
5 changed files with 91 additions and 14 deletions

View file

@ -171,6 +171,10 @@ module Devise
mattr_accessor :reset_password_keys
@@reset_password_keys = [ :email ]
# Time interval you can reset your password with a reset password key
mattr_accessor :reset_password_within
@@reset_password_within = 1.hour
# The default scope which is used by warden.
mattr_accessor :default_scope
@@default_scope = nil

View file

@ -35,15 +35,39 @@ module Devise
# Resets reset password token and send reset password instructions by email
def send_reset_password_instructions
generate_reset_password_token!
generate_reset_password_token! if self.confirmation_token.nil? or !reset_password_period_valid?
::Devise.mailer.reset_password_instructions(self).deliver
end
# Checks if the reset password token sent is within the limit time.
# We do this by calculating if the difference between today and the
# sending date does not exceed the confirm in time configured.
# reset_password_within is a model configuration, must always be an integer value.
#
# Example:
#
# # reset_password_within = 1.day and reset_password_sent_at = today
# reset_password_period_valid? # returns true
#
# # reset_password_within = 5.days and reset_password_sent_at = 4.days.ago
# reset_password_period_valid? # returns true
#
# # reset_password_within = 5.days and reset_password_sent_at = 5.days.ago
# reset_password_period_valid? # returns false
#
# # reset_password_within = 0.days
# reset_password_period_valid? # will always return false
#
def reset_password_period_valid?
reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago
end
protected
# Generates a new random token for reset password
def generate_reset_password_token
self.reset_password_token = self.class.reset_password_token
self.reset_password_sent_at = Time.now.utc
end
# Resets the reset password token with and save the record without
@ -55,6 +79,7 @@ module Devise
# Removes reset_password token
def clear_reset_password_token
self.reset_password_token = nil
self.reset_password_sent_at = nil
end
module ClassMethods
@ -73,18 +98,25 @@ module Devise
generate_token(:reset_password_token)
end
# Attempt to find a user by it's reset_password_token to reset it's
# password. If a user is found, reset it's password and automatically
# Attempt to find a user by it's reset_password_token to reset its
# password. If a user is found and token is still valid, reset its password and automatically
# try saving the record. If not user is found, returns a new user
# containing an error in reset_password_token attribute.
# Attributes must contain reset_password_token, password and confirmation
def reset_password_by_token(attributes={})
recoverable = find_or_initialize_with_error_by(:reset_password_token, attributes[:reset_password_token])
recoverable.reset_password!(attributes[:password], attributes[:password_confirmation]) if recoverable.persisted?
if recoverable.persisted?
if recoverable.reset_password_period_valid?
recoverable.reset_password!(attributes[:password], attributes[:password_confirmation])
else
recoverable.errors.add(:reset_password_token, :invalid)
end
end
recoverable
end
Devise::Models.config(self, :reset_password_keys)
Devise::Models.config(self, :reset_password_within)
end
end
end

View file

@ -38,9 +38,10 @@ module Devise
apply_devise_schema :confirmation_sent_at, DateTime
end
# Creates reset_password_token.
# Creates reset_password_token and reset_password_sent_at.
def recoverable
apply_devise_schema :reset_password_token, String
apply_devise_schema :reset_password_sent_at, DateTime
end
# Creates remember_token and remember_created_at.

View file

@ -125,6 +125,11 @@ Devise.setup do |config|
#
# Defines which key will be used when recovering the password for an account
# config.reset_password_keys = [ :email ]
#
# Time interval you can reset your password with a reset password key
# Don't put a too small interval or your users won't have the time to change their passwords
# Default to 1 hour
config.reset_password_within = 1.hour
# ==> Configuration for :encryptable
# Allow you to use another encryption algorithm besides bcrypt (default). You can use

View file

@ -10,15 +10,6 @@ class RecoverableTest < ActiveSupport::TestCase
assert_nil new_user.reset_password_token
end
test 'should regenerate reset password token each time' do
user = create_user
3.times do
token = user.reset_password_token
user.send_reset_password_instructions
assert_not_equal token, user.reset_password_token
end
end
test 'should never generate the same reset password token for different users' do
reset_password_tokens = []
3.times do
@ -161,4 +152,48 @@ class RecoverableTest < ActiveSupport::TestCase
assert_not user.valid_password?(old_password)
assert user.valid_password?('new_password')
end
test 'should not reset reset password token during reset_password_within time' do
swap Devise, :reset_password_within => 1.hour do
user = create_user
user.send_reset_password_instructions
3.times do
token = user.reset_password_token
user.send_reset_password_instructions
assert_equal token, user.reset_password_token
end
end
end
test 'should reset reset password token after reset_password_within time' do
swap Devise, :reset_password_within => 1.hour do
user = create_user
user.reset_password_sent_at = 2.days.ago
token = user.reset_password_token
user.send_reset_password_instructions
assert_not_equal token, user.reset_password_token
end
end
test 'should not reset password after reset_password_within time' do
swap Devise, :reset_password_within => 1.hour do
user = create_user
old_password = user.password
user.send :generate_reset_password_token!
user.reset_password_sent_at = 2.days.ago
user.save!
reset_password_user = User.reset_password_by_token(
:reset_password_token => user.reset_password_token,
:password => 'new_password',
:password_confirmation => 'new_password'
)
user.reload
assert user.valid_password?(old_password)
assert_not user.valid_password?('new_password')
assert_equal "is invalid", reset_password_user.errors[:reset_password_token].join
end
end
end