mirror of
https://github.com/heartcombo/devise.git
synced 2026-01-09 14:58:05 -05:00
Separating perishable token into confirmation and reset_password tokens. Adding confirmation_sent_at attribute.
This commit is contained in:
@@ -40,10 +40,12 @@ We're assuming here you want a User model. First of all you have to setup a migr
|
||||
t.string :email, :null => false
|
||||
t.string :encrypted_password, :null => false
|
||||
t.string :password_salt, :null => false
|
||||
# required for recoverable and/or confirmable
|
||||
t.string :perishable_token
|
||||
# required for confirmable
|
||||
t.string :confirmation_token
|
||||
t.datetime :confirmation_sent_at
|
||||
t.datetime :confirmed_at
|
||||
# required for recoverable
|
||||
t.string :reset_password_token
|
||||
|
||||
Now let's setup a User model adding the devise line to have your authentication working:
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class ConfirmationsController < ApplicationController
|
||||
|
||||
# GET /resource/confirmation?perishable_token=abcdef
|
||||
def show
|
||||
self.resource = resource_class.confirm!(:perishable_token => params[:perishable_token])
|
||||
self.resource = resource_class.confirm!(:confirmation_token => params[:confirmation_token])
|
||||
|
||||
if resource.errors.empty?
|
||||
set_flash_message :success, :confirmed
|
||||
|
||||
@@ -20,7 +20,7 @@ class PasswordsController < ApplicationController
|
||||
# GET /resource/password/edit?perishable_token=abcdef
|
||||
def edit
|
||||
self.resource = resource_class.new
|
||||
resource.perishable_token = params[:perishable_token]
|
||||
resource.reset_password_token = params[:reset_password_token]
|
||||
end
|
||||
|
||||
# PUT /resource/password
|
||||
|
||||
@@ -2,4 +2,4 @@ Welcome <%= @resource.email %>!
|
||||
|
||||
You can confirm your account through the link below:
|
||||
|
||||
<%= link_to 'Confirm my account', confirmation_url(@resource, :perishable_token => @resource.perishable_token) %>
|
||||
<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
|
||||
|
||||
@@ -2,7 +2,7 @@ Hello <%= @resource.email %>!
|
||||
|
||||
Someone has requested a link to change your password, and you can do this through the link below.
|
||||
|
||||
<%= link_to 'Change my password', edit_password_url(@resource, :perishable_token => @resource.perishable_token) %>
|
||||
<%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>
|
||||
|
||||
If you didn't request this, please ignore this email.
|
||||
Your password won't change until you access the link above and create a new one.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<% form_for resource_name, :url => password_path(resource_name), :html => { :method => :put } do |f| %>
|
||||
<%= f.error_messages %>
|
||||
<%= f.hidden_field :perishable_token %>
|
||||
<%= f.hidden_field :reset_password_token %>
|
||||
|
||||
<p><%= f.label :password %></p>
|
||||
<p><%= f.password_field :password %></p>
|
||||
|
||||
@@ -24,7 +24,6 @@ module Devise
|
||||
def devise(*options)
|
||||
options = [:confirmable, :recoverable, :validatable] if options.include?(:all)
|
||||
options |= [:authenticable]
|
||||
options |= [:perishable] if options.include?(:confirmable) || options.include?(:recoverable)
|
||||
|
||||
options.each do |m|
|
||||
devise_modules << m.to_sym
|
||||
|
||||
@@ -78,6 +78,16 @@ module Devise
|
||||
authenticable = self.find_by_email(attributes[:email])
|
||||
authenticable if authenticable.try(:valid_password?, attributes[:password])
|
||||
end
|
||||
|
||||
# Attempt to find a user by it's email. If not user is found, returns a
|
||||
# new user with an email not found error.
|
||||
def find_or_initialize_with_error_by_email(email)
|
||||
perishable = find_or_initialize_by_email(email)
|
||||
if perishable.new_record?
|
||||
perishable.errors.add(:email, :not_found, :default => 'not found')
|
||||
end
|
||||
perishable
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,7 +30,7 @@ module Devise
|
||||
# is already confirmed, add en error to email field
|
||||
def confirm!
|
||||
unless_confirmed do
|
||||
clear_perishable_token
|
||||
clear_confirmation_token
|
||||
update_attribute(:confirmed_at, Time.now)
|
||||
end
|
||||
end
|
||||
@@ -58,15 +58,15 @@ module Devise
|
||||
|
||||
private
|
||||
|
||||
# Remove confirmation date from the user, ensuring after a user update it's
|
||||
# email, it won't be able to sign in without confirming it.
|
||||
# Remove confirmation date from the user, ensuring after a user update
|
||||
# it's email, it won't be able to sign in without confirming it.
|
||||
def reset_confirmation
|
||||
reset_perishable_token
|
||||
generate_confirmation_token
|
||||
self.confirmed_at = nil
|
||||
end
|
||||
|
||||
# Checks whether the record is confirmed or not, yielding to the block if
|
||||
# it's already confirmed, otherwise adds an error to email.
|
||||
# Checks whether the record is confirmed or not, yielding to the block
|
||||
# if it's already confirmed, otherwise adds an error to email.
|
||||
def unless_confirmed
|
||||
unless confirmed?
|
||||
yield
|
||||
@@ -76,6 +76,24 @@ module Devise
|
||||
end
|
||||
end
|
||||
|
||||
# Generates a new random token for confirmation, and stores the time
|
||||
# this token is being generated
|
||||
def generate_confirmation_token
|
||||
self.confirmation_token = friendly_token
|
||||
self.confirmation_sent_at = Time.now.utc
|
||||
end
|
||||
|
||||
# Resets the confirmation token with and save the record without
|
||||
# validating.
|
||||
# def generate_confirmation_token!
|
||||
# generate_confirmation_token && save(false)
|
||||
# end
|
||||
|
||||
# Removes confirmation token
|
||||
def clear_confirmation_token
|
||||
self.confirmation_token = nil
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
# Attempt to find a user by it's email. If a record is found, send new
|
||||
@@ -93,10 +111,21 @@ module Devise
|
||||
# If the user is already confirmed, create an error for the user
|
||||
# Options must have the perishable_token
|
||||
def confirm!(attributes={})
|
||||
confirmable = find_or_initialize_with_error_by_perishable_token(attributes[:perishable_token])
|
||||
confirmable = find_or_initialize_with_error_by_confirmation_token(attributes[:confirmation_token])
|
||||
confirmable.confirm! unless confirmable.new_record?
|
||||
confirmable
|
||||
end
|
||||
|
||||
# Attempt to find a user by and incoming confirmation_token. If no user
|
||||
# is found, initialize a new one and adds an :invalid error to
|
||||
# confirmation_token.
|
||||
def find_or_initialize_with_error_by_confirmation_token(confirmation_token)
|
||||
confirmable = find_or_initialize_by_confirmation_token(confirmation_token)
|
||||
if confirmable.new_record?
|
||||
confirmable.errors.add(:confirmation_token, :invalid)
|
||||
end
|
||||
confirmable
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
module Devise
|
||||
module Models
|
||||
|
||||
# Perishable is mainly responsible for recreating tokens used inside
|
||||
# Recoverable and Confirmable modules.
|
||||
# It also has some utility class methods to find or initialize records based
|
||||
# on perishable_token or email, and adding an error to specific fields if no
|
||||
# record is found.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# # create a new token and save the record, without validating
|
||||
# User.find(1).reset_perishable_token!
|
||||
#
|
||||
# # only create a new token, without saving the record
|
||||
# user = User.find(1)
|
||||
# user.reset_perishable_token
|
||||
module Perishable
|
||||
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
extend ClassMethods
|
||||
end
|
||||
end
|
||||
|
||||
# Generates a new random token for confirmation, based on actual Time and salt
|
||||
def reset_perishable_token
|
||||
self.perishable_token = friendly_token
|
||||
end
|
||||
|
||||
# Resets the perishable token with and save the record without validating
|
||||
def reset_perishable_token!
|
||||
reset_perishable_token && save(false)
|
||||
end
|
||||
|
||||
# Removes perishable token
|
||||
def clear_perishable_token
|
||||
self.perishable_token = nil
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
# Attempt to find a user by and incoming perishable_token. If no user is
|
||||
# found, initialize a new one and adds an :invalid error to perishable_token
|
||||
def find_or_initialize_with_error_by_perishable_token(perishable_token)
|
||||
perishable = find_or_initialize_by_perishable_token(perishable_token)
|
||||
if perishable.new_record?
|
||||
perishable.errors.add(:perishable_token, :invalid, :default => 'invalid confirmation')
|
||||
end
|
||||
perishable
|
||||
end
|
||||
|
||||
# Attempt to find a user by it's email. If not user is found, returns a
|
||||
# new user with an email not found error.
|
||||
def find_or_initialize_with_error_by_email(email)
|
||||
perishable = find_or_initialize_by_email(email)
|
||||
if perishable.new_record?
|
||||
perishable.errors.add(:email, :not_found, :default => 'not found')
|
||||
end
|
||||
perishable
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -28,18 +28,47 @@ module Devise
|
||||
# the passwords are valid and the record was saved, false otherwise.
|
||||
def reset_password!(new_password, new_password_confirmation)
|
||||
reset_password(new_password, new_password_confirmation)
|
||||
clear_perishable_token
|
||||
clear_reset_password_token
|
||||
save
|
||||
end
|
||||
|
||||
# Resets perishable token and send reset password instructions by email
|
||||
def send_reset_password_instructions
|
||||
reset_perishable_token!
|
||||
generate_reset_password_token!
|
||||
::Notifier.deliver_reset_password_instructions(self)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Generates a new random token for reset password
|
||||
def generate_reset_password_token
|
||||
self.reset_password_token = friendly_token
|
||||
end
|
||||
|
||||
# Resets the reset password token with and save the record without
|
||||
# validating
|
||||
def generate_reset_password_token!
|
||||
generate_reset_password_token && save(false)
|
||||
end
|
||||
|
||||
# Removes reset_password token
|
||||
def clear_reset_password_token
|
||||
self.reset_password_token = nil
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
# Attempt to find a user by and incoming reset_password_token. If no
|
||||
# user is found, initialize a new one and adds an :invalid error to
|
||||
# reset_password_token
|
||||
def find_or_initialize_with_error_by_reset_password_token(reset_password_token)
|
||||
recoverable = find_or_initialize_by_reset_password_token(reset_password_token)
|
||||
if recoverable.new_record?
|
||||
recoverable.errors.add(:reset_password_token, :invalid)
|
||||
end
|
||||
recoverable
|
||||
end
|
||||
|
||||
# Attempt to find a user by it's email. If a record is found, send new
|
||||
# password instructions to it. If not user is found, returns a new user
|
||||
# with an email not found error.
|
||||
@@ -50,13 +79,13 @@ module Devise
|
||||
recoverable
|
||||
end
|
||||
|
||||
# Attempt to find a user by it's perishable_token to reset it's password.
|
||||
# If a user is found, reset it's password and automatically try saving the
|
||||
# record. If not user is found, returns a new user containing an error
|
||||
# in perishable_token attribute.
|
||||
# Attributes must contain perishable_token, password and confirmation
|
||||
# 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
|
||||
# try saving the record. If not user is found, returns a new user
|
||||
# containing an error in perishable_token attribute.
|
||||
# Attributes must contain reset_password_token, password and confirmation
|
||||
def reset_password!(attributes={})
|
||||
recoverable = find_or_initialize_with_error_by_perishable_token(attributes[:perishable_token])
|
||||
recoverable = find_or_initialize_with_error_by_reset_password_token(attributes[:reset_password_token])
|
||||
recoverable.reset_password!(attributes[:password], attributes[:password_confirmation]) unless recoverable.new_record?
|
||||
recoverable
|
||||
end
|
||||
|
||||
@@ -17,20 +17,20 @@ class ConfirmationTest < ActionController::IntegrationTest
|
||||
assert_equal 1, ActionMailer::Base.deliveries.size
|
||||
end
|
||||
|
||||
test 'user with invalid perishable token should not be able to confirm an account' do
|
||||
visit user_confirmation_path(:perishable_token => 'invalid_perishable')
|
||||
test 'user with invalid confirmation token should not be able to confirm an account' do
|
||||
visit user_confirmation_path(:confirmation_token => 'invalid_confirmation')
|
||||
|
||||
assert_response :success
|
||||
assert_template 'confirmations/new'
|
||||
assert_have_selector '#errorExplanation'
|
||||
assert_contain 'invalid confirmation'
|
||||
assert_contain 'Confirmation token is invalid'
|
||||
end
|
||||
|
||||
test 'user with valid perishable token should be able to confirm an account' do
|
||||
test 'user with valid confirmation token should be able to confirm an account' do
|
||||
user = create_user(:confirm => false)
|
||||
assert_not user.confirmed?
|
||||
|
||||
visit user_confirmation_path(:perishable_token => user.perishable_token)
|
||||
visit user_confirmation_path(:confirmation_token => user.confirmation_token)
|
||||
|
||||
assert_template 'sessions/new'
|
||||
assert_contain 'Your account was successfully confirmed!'
|
||||
@@ -40,7 +40,7 @@ class ConfirmationTest < ActionController::IntegrationTest
|
||||
|
||||
test 'user already confirmed user should not be able to confirm the account again' do
|
||||
user = create_user
|
||||
visit user_confirmation_path(:perishable_token => user.perishable_token)
|
||||
visit user_confirmation_path(:confirmation_token => user.confirmation_token)
|
||||
|
||||
assert_template 'confirmations/new'
|
||||
assert_have_selector '#errorExplanation'
|
||||
|
||||
@@ -20,7 +20,7 @@ class PasswordTest < ActionController::IntegrationTest
|
||||
end
|
||||
|
||||
def reset_password(options={}, &block)
|
||||
visit edit_user_password_path(:perishable_token => options[:perishable_token])
|
||||
visit edit_user_password_path(:reset_password_token => options[:reset_password_token])
|
||||
assert_response :success
|
||||
assert_template 'passwords/edit'
|
||||
|
||||
@@ -69,21 +69,21 @@ class PasswordTest < ActionController::IntegrationTest
|
||||
assert warden.authenticated?(:user)
|
||||
end
|
||||
|
||||
test 'not authenticated user with invalid perishable token should not be able to change his password' do
|
||||
test 'not authenticated user with invalid reset password token should not be able to change his password' do
|
||||
user = create_user
|
||||
reset_password :perishable_token => 'invalid_perishable'
|
||||
reset_password :reset_password_token => 'invalid_reset_password'
|
||||
|
||||
assert_response :success
|
||||
assert_template 'passwords/edit'
|
||||
assert_have_selector '#errorExplanation'
|
||||
assert_contain 'invalid confirmation'
|
||||
assert_contain 'Reset password token is invalid'
|
||||
assert_not user.reload.valid_password?('987654321')
|
||||
end
|
||||
|
||||
test 'not authenticated user with valid perisable token but invalid password should not be able to change his password' do
|
||||
test 'not authenticated user with valid reset password token but invalid password should not be able to change his password' do
|
||||
user = create_user
|
||||
request_forgot_password
|
||||
reset_password :perishable_token => user.reload.perishable_token do
|
||||
reset_password :reset_password_token => user.reload.reset_password_token do
|
||||
fill_in 'Password confirmation', :with => 'other_password'
|
||||
end
|
||||
|
||||
@@ -97,7 +97,7 @@ class PasswordTest < ActionController::IntegrationTest
|
||||
test 'not authenticated user with valid data should be able to change his password' do
|
||||
user = create_user
|
||||
request_forgot_password
|
||||
reset_password :perishable_token => user.reload.perishable_token
|
||||
reset_password :reset_password_token => user.reload.reset_password_token
|
||||
|
||||
assert_template 'sessions/new'
|
||||
assert_contain 'Your password was changed successfully.'
|
||||
|
||||
@@ -36,7 +36,7 @@ class ConfirmationInstructionsTest < ActionMailer::TestCase
|
||||
|
||||
test 'body should have link to confirm the account' do
|
||||
host = ActionMailer::Base.default_url_options[:host]
|
||||
confirmation_url_regexp = %r{<a href=\"http://#{host}/users/confirmation\?perishable_token=#{@user.perishable_token}">}
|
||||
confirmation_url_regexp = %r{<a href=\"http://#{host}/users/confirmation\?confirmation_token=#{@user.confirmation_token}">}
|
||||
assert_match confirmation_url_regexp, @mail.body
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,7 +7,8 @@ class ResetPasswordInstructionsTest < ActionMailer::TestCase
|
||||
I18n.backend.store_translations :en, {:devise => { :notifier => { :reset_password_instructions => 'Reset instructions' } }}
|
||||
Notifier.sender = 'test@example.com'
|
||||
@user = create_user
|
||||
@mail = Notifier.deliver_reset_password_instructions(@user)
|
||||
@user.send_reset_password_instructions
|
||||
@mail = ActionMailer::Base.deliveries.last
|
||||
end
|
||||
|
||||
test 'email sent after reseting the user password' do
|
||||
@@ -36,7 +37,7 @@ class ResetPasswordInstructionsTest < ActionMailer::TestCase
|
||||
|
||||
test 'body should have link to confirm the account' do
|
||||
host = ActionMailer::Base.default_url_options[:host]
|
||||
confirmation_url_regexp = %r{<a href=\"http://#{host}/users/password/edit\?perishable_token=#{@user.perishable_token}">}
|
||||
confirmation_url_regexp = %r{<a href=\"http://#{host}/users/password/edit\?reset_password_token=#{@user.reset_password_token}">}
|
||||
assert_match confirmation_url_regexp, @mail.body
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,6 +10,37 @@ class ConfirmableTest < ActiveSupport::TestCase
|
||||
assert_not field_accessible?(:confirmed_at)
|
||||
end
|
||||
|
||||
test 'should not have confirmation token accessible' do
|
||||
assert_not field_accessible?(:confirmation_token)
|
||||
end
|
||||
|
||||
test 'should not have confirmation sent at accessible' do
|
||||
assert_not field_accessible?(:confirmation_sent_at)
|
||||
end
|
||||
|
||||
test 'should generate confirmation token after creating a record' do
|
||||
assert_nil new_user.confirmation_token
|
||||
assert_not_nil create_user.confirmation_token
|
||||
end
|
||||
|
||||
test 'should regenerate confirmation token each time' do
|
||||
user = create_user
|
||||
3.times do
|
||||
token = user.confirmation_token
|
||||
user.reset_confirmation!
|
||||
assert_not_equal token, user.confirmation_token
|
||||
end
|
||||
end
|
||||
|
||||
test 'should never generate the same confirmation token for different users' do
|
||||
confirmation_tokens = []
|
||||
10.times do
|
||||
token = create_user.confirmation_token
|
||||
assert !confirmation_tokens.include?(token)
|
||||
confirmation_tokens << token
|
||||
end
|
||||
end
|
||||
|
||||
test 'should confirm a user updating confirmed at' do
|
||||
user = create_user
|
||||
assert_nil user.confirmed_at
|
||||
@@ -17,11 +48,11 @@ class ConfirmableTest < ActiveSupport::TestCase
|
||||
assert_not_nil user.confirmed_at
|
||||
end
|
||||
|
||||
test 'should clear perishable token while confirming a user' do
|
||||
test 'should clear confirmation token while confirming a user' do
|
||||
user = create_user
|
||||
assert_present user.perishable_token
|
||||
assert_present user.confirmation_token
|
||||
user.confirm!
|
||||
assert_nil user.perishable_token
|
||||
assert_nil user.confirmation_token
|
||||
end
|
||||
|
||||
test 'should verify whether a user is confirmed or not' do
|
||||
@@ -43,27 +74,27 @@ class ConfirmableTest < ActiveSupport::TestCase
|
||||
|
||||
test 'should find and confirm an user automatically' do
|
||||
user = create_user
|
||||
confirmed_user = User.confirm!(:perishable_token => user.perishable_token)
|
||||
confirmed_user = User.confirm!(:confirmation_token => user.confirmation_token)
|
||||
assert_not_nil confirmed_user
|
||||
assert_equal confirmed_user, user
|
||||
assert user.reload.confirmed?
|
||||
end
|
||||
|
||||
test 'should return a new user with errors if no user exists while trying to confirm' do
|
||||
confirmed_user = User.confirm!(:perishable_token => 'invalid_perishable_token')
|
||||
confirmed_user = User.confirm!(:confirmation_token => 'invalid_confirmation_token')
|
||||
assert confirmed_user.new_record?
|
||||
end
|
||||
|
||||
test 'should return errors for a new user when trying to confirm' do
|
||||
confirmed_user = User.confirm!(:perishable_token => 'invalid_perishable_token')
|
||||
assert_not_nil confirmed_user.errors[:perishable_token]
|
||||
assert_equal "invalid confirmation", confirmed_user.errors[:perishable_token]
|
||||
confirmed_user = User.confirm!(:confirmation_token => 'invalid_confirmation_token')
|
||||
assert_not_nil confirmed_user.errors[:confirmation_token]
|
||||
assert_equal 'is invalid', confirmed_user.errors[:confirmation_token]
|
||||
end
|
||||
|
||||
test 'should generate errors for a user email if user is already confirmed' do
|
||||
user = create_user
|
||||
user.confirm!
|
||||
confirmed_user = User.confirm!(:perishable_token => user.perishable_token)
|
||||
confirmed_user = User.confirm!(:confirmation_token => user.confirmation_token)
|
||||
assert confirmed_user.confirmed?
|
||||
assert confirmed_user.errors[:email]
|
||||
end
|
||||
@@ -109,11 +140,11 @@ class ConfirmableTest < ActiveSupport::TestCase
|
||||
assert_equal 'not found', confirmation_user.errors[:email]
|
||||
end
|
||||
|
||||
test 'should reset perishable token before send the confirmation instructions email' do
|
||||
test 'should reset confirmation token before send the confirmation instructions email' do
|
||||
user = create_user
|
||||
token = user.perishable_token
|
||||
token = user.confirmation_token
|
||||
confirmation_user = User.send_confirmation_instructions(:email => user.email)
|
||||
assert_not_equal token, user.reload.perishable_token
|
||||
assert_not_equal token, user.reload.confirmation_token
|
||||
end
|
||||
|
||||
test 'should reset confirmation status when sending the confirmation instructions' do
|
||||
@@ -156,12 +187,12 @@ class ConfirmableTest < ActiveSupport::TestCase
|
||||
assert_not user.reload.confirmed?
|
||||
end
|
||||
|
||||
test 'should reset perishable token when updating email' do
|
||||
test 'should reset confirmation token when updating email' do
|
||||
user = create_user
|
||||
token = user.perishable_token
|
||||
token = user.confirmation_token
|
||||
user.email = 'new_test@example.com'
|
||||
user.save!
|
||||
assert_not_equal token, user.reload.perishable_token
|
||||
assert_not_equal token, user.reload.confirmation_token
|
||||
end
|
||||
|
||||
test 'should not be able to send instructions if the user is already confirmed' do
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
require 'test/test_helper'
|
||||
|
||||
class PerishableTest < ActiveSupport::TestCase
|
||||
|
||||
test 'should not have perishable token accessible' do
|
||||
assert_not field_accessible?(:perishable_token)
|
||||
end
|
||||
|
||||
test 'should generate perishable token after creating a record' do
|
||||
assert_nil new_user.perishable_token
|
||||
assert_not_nil create_user.perishable_token
|
||||
end
|
||||
|
||||
test 'should reset perisable token each time' do
|
||||
user = new_user
|
||||
3.times do
|
||||
token = user.perishable_token
|
||||
user.reset_perishable_token
|
||||
assert_not_equal token, user.perishable_token
|
||||
end
|
||||
end
|
||||
|
||||
test 'should reset perishable token and save the record' do
|
||||
user = new_user
|
||||
assert_nil user.perishable_token
|
||||
user.reset_perishable_token!
|
||||
assert_not_nil user.perishable_token
|
||||
assert !user.new_record?
|
||||
end
|
||||
|
||||
test 'should save without validations when reseting perisable token' do
|
||||
user = new_user
|
||||
user.expects(:valid?).never
|
||||
user.reset_perishable_token!
|
||||
end
|
||||
|
||||
test 'should never generate the same perishable token for different users' do
|
||||
perishable_tokens = []
|
||||
10.times do
|
||||
token = create_user.perishable_token
|
||||
assert !perishable_tokens.include?(token)
|
||||
perishable_tokens << token
|
||||
end
|
||||
end
|
||||
|
||||
test 'should not change perishable token when updating' do
|
||||
user = create_user
|
||||
token = user.perishable_token
|
||||
user.expects(:perishable_token=).never
|
||||
user.save!
|
||||
assert_equal token, user.perishable_token
|
||||
end
|
||||
|
||||
test 'should generate a sha1 hash for perishable token' do
|
||||
ActiveSupport::SecureRandom.expects(:base64).with(15).twice.returns('perishable token')
|
||||
assert_equal 'perishable token', create_user.perishable_token
|
||||
end
|
||||
end
|
||||
@@ -6,6 +6,35 @@ class RecoverableTest < ActiveSupport::TestCase
|
||||
setup_mailer
|
||||
end
|
||||
|
||||
test 'should not have reset password token accessible' do
|
||||
assert_not field_accessible?(:reset_password_token)
|
||||
end
|
||||
|
||||
test 'should not generate reset password token after creating a record' do
|
||||
assert_nil new_user.reset_password_token
|
||||
assert_nil create_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 = []
|
||||
10.times do
|
||||
user = create_user
|
||||
user.send_reset_password_instructions
|
||||
token = user.reset_password_token
|
||||
assert !reset_password_tokens.include?(token)
|
||||
reset_password_tokens << token
|
||||
end
|
||||
end
|
||||
|
||||
test 'should reset password and password confirmation from params' do
|
||||
user = create_user
|
||||
user.reset_password('123456789', '987654321')
|
||||
@@ -17,11 +46,13 @@ class RecoverableTest < ActiveSupport::TestCase
|
||||
assert create_user.reset_password!('123456789', '123456789')
|
||||
end
|
||||
|
||||
test 'should clear perishable token while reseting the password' do
|
||||
test 'should clear reset password token while reseting the password' do
|
||||
user = create_user
|
||||
assert_present user.perishable_token
|
||||
assert_nil user.reset_password_token
|
||||
user.send_reset_password_instructions
|
||||
assert_present user.reset_password_token
|
||||
user.reset_password!('123456789', '123456789')
|
||||
assert_nil user.perishable_token
|
||||
assert_nil user.reset_password_token
|
||||
end
|
||||
|
||||
test 'should not reset password with invalid data' do
|
||||
@@ -30,12 +61,12 @@ class RecoverableTest < ActiveSupport::TestCase
|
||||
assert_not user.reset_password!('123456789', '987654321')
|
||||
end
|
||||
|
||||
test 'should reset perishable token and send instructions by email' do
|
||||
test 'should reset reset password token and send instructions by email' do
|
||||
user = create_user
|
||||
assert_email_sent do
|
||||
token = user.perishable_token
|
||||
token = user.reset_password_token
|
||||
user.send_reset_password_instructions
|
||||
assert_not_equal token, user.perishable_token
|
||||
assert_not_equal token, user.reset_password_token
|
||||
end
|
||||
end
|
||||
|
||||
@@ -58,11 +89,11 @@ class RecoverableTest < ActiveSupport::TestCase
|
||||
assert_equal 'not found', reset_password_user.errors[:email]
|
||||
end
|
||||
|
||||
test 'should reset perishable token before send the reset instructions email' do
|
||||
test 'should reset reset password token before send the reset instructions email' do
|
||||
user = create_user
|
||||
token = user.perishable_token
|
||||
token = user.reset_password_token
|
||||
reset_password_user = User.send_reset_password_instructions(:email => user.email)
|
||||
assert_not_equal token, user.reload.perishable_token
|
||||
assert_not_equal token, user.reload.reset_password_token
|
||||
end
|
||||
|
||||
test 'should send email instructions to the user reset it\'s password' do
|
||||
@@ -72,30 +103,30 @@ class RecoverableTest < ActiveSupport::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
test 'should find a user to reset it\'s password based on perishable_token' do
|
||||
test 'should find a user to reset it\'s password based on reset_password_token' do
|
||||
user = create_user
|
||||
reset_password_user = User.reset_password!(:perishable_token => user.perishable_token)
|
||||
reset_password_user = User.reset_password!(:reset_password_token => user.reset_password_token)
|
||||
assert_not_nil reset_password_user
|
||||
assert_equal reset_password_user, user
|
||||
end
|
||||
|
||||
test 'should return a new user when trying to reset it\'s password if no perishable_token is found' do
|
||||
reset_password_user = User.reset_password!(:perishable_token => 'invalid_token')
|
||||
test 'should return a new user when trying to reset it\'s password if no reset_password_token is found' do
|
||||
reset_password_user = User.reset_password!(:reset_password_token => 'invalid_token')
|
||||
assert_not_nil reset_password_user
|
||||
assert reset_password_user.new_record?
|
||||
end
|
||||
|
||||
test 'should add error to new user email if no perishable token was found' do
|
||||
reset_password_user = User.reset_password!(:perishable_token => "invalid_token")
|
||||
assert reset_password_user.errors[:perishable_token]
|
||||
assert_equal 'invalid confirmation', reset_password_user.errors[:perishable_token]
|
||||
test 'should add error to new user email if no reset password token was found' do
|
||||
reset_password_user = User.reset_password!(:reset_password_token => "invalid_token")
|
||||
assert reset_password_user.errors[:reset_password_token]
|
||||
assert_equal 'is invalid', reset_password_user.errors[:reset_password_token]
|
||||
end
|
||||
|
||||
test 'should reset successfully user password given the new password and confirmation' do
|
||||
user = create_user
|
||||
old_password = user.password
|
||||
reset_password_user = User.reset_password!(
|
||||
:perishable_token => user.perishable_token,
|
||||
:reset_password_token => user.reset_password_token,
|
||||
:password => 'new_password',
|
||||
:password_confirmation => 'new_password'
|
||||
)
|
||||
|
||||
@@ -16,11 +16,13 @@ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":me
|
||||
ActiveRecord::Schema.define(:version => 1) do
|
||||
[:users, :admins].each do |table|
|
||||
create_table table do |t|
|
||||
t.string :email, :null => false
|
||||
t.string :encrypted_password, :null => false
|
||||
t.string :password_salt, :null => false
|
||||
t.string :perishable_token
|
||||
t.string :email, :null => false
|
||||
t.string :encrypted_password, :null => false
|
||||
t.string :password_salt, :null => false
|
||||
t.string :confirmation_token
|
||||
t.datetime :confirmation_sent_at
|
||||
t.datetime :confirmed_at
|
||||
t.string :reset_password_token
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user