mirror of
https://github.com/heartcombo/devise.git
synced 2022-11-09 12:18:31 -05:00
Use HMAC on tokens stored in the DB
This commit is contained in:
parent
32648027e2
commit
143794d701
19 changed files with 177 additions and 192 deletions
|
@ -1,15 +1,18 @@
|
|||
class Devise::Mailer < Devise.parent_mailer.constantize
|
||||
include Devise::Mailers::Helpers
|
||||
|
||||
def confirmation_instructions(record, opts={})
|
||||
def confirmation_instructions(record, token, opts={})
|
||||
@token = token
|
||||
devise_mail(record, :confirmation_instructions, opts)
|
||||
end
|
||||
|
||||
def reset_password_instructions(record, opts={})
|
||||
def reset_password_instructions(record, token, opts={})
|
||||
@token = token
|
||||
devise_mail(record, :reset_password_instructions, opts)
|
||||
end
|
||||
|
||||
def unlock_instructions(record, opts={})
|
||||
def unlock_instructions(record, token, opts={})
|
||||
@token = token
|
||||
devise_mail(record, :unlock_instructions, opts)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
<p>You can confirm your account email through the link below:</p>
|
||||
|
||||
<p><%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %></p>
|
||||
<p><%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @token) %></p>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<p>Someone has requested a link to change your password. You can do this through the link below.</p>
|
||||
|
||||
<p><%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %></p>
|
||||
<p><%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @token) %></p>
|
||||
|
||||
<p>If you didn't request this, please ignore this email.</p>
|
||||
<p>Your password won't change until you access the link above and create a new one.</p>
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
|
||||
<p>Click the link below to unlock your account:</p>
|
||||
|
||||
<p><%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %></p>
|
||||
<p><%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @token) %></p>
|
||||
|
|
|
@ -14,6 +14,7 @@ module Devise
|
|||
autoload :ParameterSanitizer, 'devise/parameter_sanitizer'
|
||||
autoload :TestHelpers, 'devise/test_helpers'
|
||||
autoload :TimeInflector, 'devise/time_inflector'
|
||||
autoload :TokenGenerator, 'devise/token_generator'
|
||||
|
||||
module Controllers
|
||||
autoload :Helpers, 'devise/controllers/helpers'
|
||||
|
@ -49,6 +50,10 @@ module Devise
|
|||
mattr_accessor :secret_key
|
||||
@@secret_key = nil
|
||||
|
||||
# Secret key used by the key generator
|
||||
mattr_accessor :token_generator
|
||||
@@token_generator = nil
|
||||
|
||||
# Custom domain or key for cookies. Not set by default
|
||||
mattr_accessor :rememberable_options
|
||||
@@rememberable_options = {}
|
||||
|
|
|
@ -83,11 +83,8 @@ module Devise
|
|||
|
||||
devise_modules_hook! do
|
||||
include Devise::Models::Authenticatable
|
||||
selected_modules.each do |m|
|
||||
if m == :encryptable && !(defined?(Devise::Models::Encryptable))
|
||||
warn "[DEVISE] You're trying to include :encryptable in your model but it is not bundled with the Devise gem anymore. Please add `devise-encryptable` to your Gemfile to proceed.\n"
|
||||
end
|
||||
|
||||
selected_modules.each do |m|
|
||||
mod = Devise::Models.const_get(m.to_s.classify)
|
||||
|
||||
if mod.const_defined?("ClassMethods")
|
||||
|
|
|
@ -144,20 +144,20 @@ module Devise
|
|||
#
|
||||
# protected
|
||||
#
|
||||
# def send_devise_notification(notification, opts = {})
|
||||
# # if the record is new or changed then delay the
|
||||
# def send_devise_notification(notification, *args)
|
||||
# # If the record is new or changed then delay the
|
||||
# # delivery until the after_commit callback otherwise
|
||||
# # send now because after_commit will not be called.
|
||||
# if new_record? || changed?
|
||||
# pending_notifications << [notification, opts]
|
||||
# pending_notifications << [notification, args]
|
||||
# else
|
||||
# devise_mailer.send(notification, self, opts).deliver
|
||||
# devise_mailer.send(notification, self, *args).deliver
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# def send_pending_notifications
|
||||
# pending_notifications.each do |n, opts|
|
||||
# devise_mailer.send(n, self, opts).deliver
|
||||
# pending_notifications.each do |notification, args|
|
||||
# devise_mailer.send(notification, self, *args).deliver
|
||||
# end
|
||||
#
|
||||
# # Empty the pending notifications array because the
|
||||
|
@ -171,8 +171,8 @@ module Devise
|
|||
# end
|
||||
# end
|
||||
#
|
||||
def send_devise_notification(notification, opts={})
|
||||
devise_mailer.send(notification, self, opts).deliver
|
||||
def send_devise_notification(notification, *args)
|
||||
devise_mailer.send(notification, self, *args).deliver
|
||||
end
|
||||
|
||||
def downcase_keys
|
||||
|
@ -279,14 +279,6 @@ module Devise
|
|||
def devise_parameter_filter
|
||||
@devise_parameter_filter ||= Devise::ParameterFilter.new(case_insensitive_keys, strip_whitespace_keys)
|
||||
end
|
||||
|
||||
# Generate a token by looping and ensuring does not already exist.
|
||||
def generate_token(column)
|
||||
loop do
|
||||
token = Devise.friendly_token
|
||||
break token unless to_adapter.find_first({ column => token })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,9 +40,10 @@ module Devise
|
|||
end
|
||||
|
||||
def initialize(*args, &block)
|
||||
@bypass_postpone = false
|
||||
@bypass_confirmation_postpone = false
|
||||
@reconfirmation_required = false
|
||||
@skip_confirmation_notification = false
|
||||
@raw_confirmation_token = nil
|
||||
super
|
||||
end
|
||||
|
||||
|
@ -93,10 +94,12 @@ module Devise
|
|||
|
||||
# Send confirmation instructions by email
|
||||
def send_confirmation_instructions
|
||||
ensure_confirmation_token!
|
||||
unless @raw_confirmation_token
|
||||
generate_confirmation_token!
|
||||
end
|
||||
|
||||
opts = pending_reconfirmation? ? { :to => unconfirmed_email } : { }
|
||||
send_devise_notification(:confirmation_instructions, opts)
|
||||
send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
|
||||
end
|
||||
|
||||
def send_reconfirmation_instructions
|
||||
|
@ -109,17 +112,11 @@ module Devise
|
|||
|
||||
# Resend confirmation token.
|
||||
# Regenerates the token if the period is expired.
|
||||
def resend_confirmation_token
|
||||
def resend_confirmation_instructions
|
||||
pending_any_confirmation do
|
||||
regenerate_confirmation_token! if confirmation_period_expired?
|
||||
send_confirmation_instructions
|
||||
end
|
||||
end
|
||||
|
||||
# Generate a confirmation token unless already exists and save the record.
|
||||
def ensure_confirmation_token!
|
||||
generate_confirmation_token! if should_generate_confirmation_token?
|
||||
end
|
||||
|
||||
# Overwrites active_for_authentication? for confirmation
|
||||
# by verifying whether a user is active to sign in or not. If the user
|
||||
|
@ -149,19 +146,16 @@ module Devise
|
|||
# If you don't want reconfirmation to be sent, neither a code
|
||||
# to be generated, call skip_reconfirmation!
|
||||
def skip_reconfirmation!
|
||||
@bypass_postpone = true
|
||||
@bypass_confirmation_postpone = true
|
||||
end
|
||||
|
||||
protected
|
||||
def should_generate_confirmation_token?
|
||||
confirmation_token.nil? || confirmation_period_expired?
|
||||
end
|
||||
|
||||
# A callback method used to deliver confirmation
|
||||
# instructions on creation. This can be overriden
|
||||
# in models to map to a nice sign up e-mail.
|
||||
def send_on_create_confirmation_instructions
|
||||
send_devise_notification(:confirmation_instructions)
|
||||
send_confirmation_instructions
|
||||
end
|
||||
|
||||
# Callback to overwrite if confirmation is required or not.
|
||||
|
@ -221,10 +215,12 @@ module Devise
|
|||
end
|
||||
end
|
||||
|
||||
# Generates a new random token for confirmation, and stores the time
|
||||
# this token is being generated
|
||||
# Generates a new random token for confirmation, and stores
|
||||
# the time this token is being generated
|
||||
def generate_confirmation_token
|
||||
self.confirmation_token = self.class.confirmation_token
|
||||
raw, enc = Devise.token_generator.generate(self.class, :confirmation_token)
|
||||
@raw_confirmation_token = raw
|
||||
self.confirmation_token = enc
|
||||
self.confirmation_sent_at = Time.now.utc
|
||||
end
|
||||
|
||||
|
@ -232,15 +228,6 @@ module Devise
|
|||
generate_confirmation_token && save(:validate => false)
|
||||
end
|
||||
|
||||
# Regenerates a new token.
|
||||
def regenerate_confirmation_token
|
||||
generate_confirmation_token
|
||||
end
|
||||
|
||||
def regenerate_confirmation_token!
|
||||
regenerate_confirmation_token && save(:validate => false)
|
||||
end
|
||||
|
||||
def after_password_reset
|
||||
super
|
||||
confirm! unless confirmed?
|
||||
|
@ -250,12 +237,12 @@ module Devise
|
|||
@reconfirmation_required = true
|
||||
self.unconfirmed_email = self.email
|
||||
self.email = self.email_was
|
||||
regenerate_confirmation_token
|
||||
generate_confirmation_token
|
||||
end
|
||||
|
||||
def postpone_email_change?
|
||||
postpone = self.class.reconfirmable && email_changed? && !@bypass_postpone && !self.email.blank?
|
||||
@bypass_postpone = false
|
||||
postpone = self.class.reconfirmable && email_changed? && !@bypass_confirmation_postpone && !self.email.blank?
|
||||
@bypass_confirmation_postpone = false
|
||||
postpone
|
||||
end
|
||||
|
||||
|
@ -280,7 +267,7 @@ module Devise
|
|||
unless confirmable.try(:persisted?)
|
||||
confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
|
||||
end
|
||||
confirmable.resend_confirmation_token if confirmable.persisted?
|
||||
confirmable.resend_confirmation_instructions if confirmable.persisted?
|
||||
confirmable
|
||||
end
|
||||
|
||||
|
@ -289,16 +276,16 @@ module Devise
|
|||
# If the user is already confirmed, create an error for the user
|
||||
# Options must have the confirmation_token
|
||||
def confirm_by_token(confirmation_token)
|
||||
original_token = confirmation_token
|
||||
confirmation_token = Devise.token_generator.digest(self, :confirmation_token, confirmation_token)
|
||||
confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
|
||||
unless confirmable.persisted?
|
||||
confirmable = find_or_initialize_with_error_by(:confirmation_token, original_token)
|
||||
end
|
||||
confirmable.confirm! if confirmable.persisted?
|
||||
confirmable
|
||||
end
|
||||
|
||||
# Generate a token checking if one does not already exist in the database.
|
||||
def confirmation_token
|
||||
generate_token(:confirmation_token)
|
||||
end
|
||||
|
||||
# Find a record for confirmation by unconfirmed email field
|
||||
def find_by_unconfirmed_email_with_errors(attributes = {})
|
||||
unconfirmed_required_attributes = confirmation_keys.map { |k| k == :email ? :unconfirmed_email : k }
|
||||
|
|
|
@ -38,7 +38,6 @@ module Devise
|
|||
self.locked_at = Time.now.utc
|
||||
|
||||
if unlock_strategy_enabled?(:email)
|
||||
generate_unlock_token!
|
||||
send_unlock_instructions
|
||||
else
|
||||
save(:validate => false)
|
||||
|
@ -60,11 +59,15 @@ module Devise
|
|||
|
||||
# Send unlock instructions by email
|
||||
def send_unlock_instructions
|
||||
send_devise_notification(:unlock_instructions)
|
||||
raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
|
||||
self.unlock_token = enc
|
||||
self.save(:validate => false)
|
||||
send_devise_notification(:unlock_instructions, raw, {})
|
||||
raw
|
||||
end
|
||||
|
||||
# Resend the unlock instructions if the user is locked.
|
||||
def resend_unlock_token
|
||||
def resend_unlock_instructions
|
||||
if_access_locked { send_unlock_instructions }
|
||||
end
|
||||
|
||||
|
@ -122,15 +125,6 @@ module Devise
|
|||
self.failed_attempts > self.class.maximum_attempts
|
||||
end
|
||||
|
||||
# Generates unlock token
|
||||
def generate_unlock_token
|
||||
self.unlock_token = self.class.unlock_token
|
||||
end
|
||||
|
||||
def generate_unlock_token!
|
||||
generate_unlock_token && save(:validate => false)
|
||||
end
|
||||
|
||||
# Tells if the lock is expired if :time unlock strategy is active
|
||||
def lock_expired?
|
||||
if unlock_strategy_enabled?(:time)
|
||||
|
@ -158,7 +152,7 @@ module Devise
|
|||
# Options must contain the user's unlock keys
|
||||
def send_unlock_instructions(attributes={})
|
||||
lockable = find_or_initialize_with_errors(unlock_keys, attributes, :not_found)
|
||||
lockable.resend_unlock_token if lockable.persisted?
|
||||
lockable.resend_unlock_instructions if lockable.persisted?
|
||||
lockable
|
||||
end
|
||||
|
||||
|
@ -167,7 +161,14 @@ module Devise
|
|||
# If the user is not locked, creates an error for the user
|
||||
# Options must have the unlock_token
|
||||
def unlock_access_by_token(unlock_token)
|
||||
original_token = unlock_token
|
||||
unlock_token = Devise.token_generator.digest(self, :unlock_token, unlock_token)
|
||||
|
||||
lockable = find_or_initialize_with_error_by(:unlock_token, unlock_token)
|
||||
unless lockable.persisted?
|
||||
lockable = find_or_initialize_with_error_by(:unlock_token, original_token)
|
||||
end
|
||||
|
||||
lockable.unlock_access! if lockable.persisted?
|
||||
lockable
|
||||
end
|
||||
|
@ -182,10 +183,6 @@ module Devise
|
|||
self.lock_strategy == strategy
|
||||
end
|
||||
|
||||
def unlock_token
|
||||
Devise.friendly_token
|
||||
end
|
||||
|
||||
Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,17 +42,19 @@ module Devise
|
|||
save
|
||||
end
|
||||
|
||||
# Resets reset password token and send reset password instructions by email
|
||||
# Resets reset password token and send reset password instructions by email.
|
||||
# Returns the token sent in the e-mail.
|
||||
def send_reset_password_instructions
|
||||
ensure_reset_password_token!
|
||||
send_devise_notification(:reset_password_instructions)
|
||||
raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
|
||||
|
||||
self.reset_password_token = enc
|
||||
self.reset_password_sent_at = Time.now.utc
|
||||
self.save(:validate => false)
|
||||
|
||||
send_devise_notification(:reset_password_instructions, raw, {})
|
||||
raw
|
||||
end
|
||||
|
||||
# Generate reset password token unless already exists and save the record.
|
||||
def ensure_reset_password_token!
|
||||
generate_reset_password_token! if should_generate_reset_token?
|
||||
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.
|
||||
|
@ -79,23 +81,6 @@ module Devise
|
|||
|
||||
protected
|
||||
|
||||
def should_generate_reset_token?
|
||||
reset_password_token.nil? || !reset_password_period_valid?
|
||||
end
|
||||
|
||||
# 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
|
||||
self.reset_password_token
|
||||
end
|
||||
|
||||
# Resets the reset password token with and save the record without
|
||||
# validating
|
||||
def generate_reset_password_token!
|
||||
generate_reset_password_token && save(:validate => false)
|
||||
end
|
||||
|
||||
# Removes reset_password token
|
||||
def clear_reset_password_token
|
||||
self.reset_password_token = nil
|
||||
|
@ -127,7 +112,14 @@ module Devise
|
|||
# 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])
|
||||
original_token = attributes[:reset_password_token]
|
||||
reset_password_token = Devise.token_generator.digest(self, :reset_password_token, original_token)
|
||||
|
||||
recoverable = find_or_initialize_with_error_by(:reset_password_token, reset_password_token)
|
||||
unless recoverable.persisted?
|
||||
recoverable = find_or_initialize_with_error_by(:reset_password_token, original_token)
|
||||
end
|
||||
|
||||
if recoverable.persisted?
|
||||
if recoverable.reset_password_period_valid?
|
||||
recoverable.reset_password!(attributes[:password], attributes[:password_confirmation])
|
||||
|
|
|
@ -79,7 +79,10 @@ module Devise
|
|||
|
||||
# Generate a token checking if one does not already exist in the database.
|
||||
def authentication_token
|
||||
generate_token(:authentication_token)
|
||||
loop do
|
||||
token = Devise.friendly_token
|
||||
break token unless to_adapter.find_first({ :authentication_token => token })
|
||||
end
|
||||
end
|
||||
|
||||
Devise::Models.config(self, :token_authentication_key, :expire_auth_token_on_timeout)
|
||||
|
|
|
@ -30,7 +30,11 @@ module Devise
|
|||
end
|
||||
|
||||
initializer "devise.secret_key" do
|
||||
unless Devise.secret_key
|
||||
if secret_key = Devise.secret_key
|
||||
Devise.token_generator = Devise::TokenGenerator.new(
|
||||
Devise::CachingKeyGenerator.new(Devise::KeyGenerator.new(secret_key))
|
||||
)
|
||||
else
|
||||
raise <<-ERROR
|
||||
Devise.secret_key was not set. Please add the following to your Devise initializer:
|
||||
|
||||
|
|
|
@ -1,9 +1,36 @@
|
|||
# Deprecate: Copied verbatim from Rails source, remove once we move to Rails 4 only.
|
||||
require 'thread_safe'
|
||||
require 'openssl'
|
||||
require 'secure_random'
|
||||
require 'securerandom'
|
||||
|
||||
module Devise
|
||||
class TokenGenerator
|
||||
def initialize(key_generator)
|
||||
@key_generator = key_generator
|
||||
end
|
||||
|
||||
def digest(klass, column, value)
|
||||
value.present? && OpenSSL::HMAC.hexdigest("SHA1", key_for(klass, column), value.to_s)
|
||||
end
|
||||
|
||||
def generate(klass, column)
|
||||
adapter = klass.to_adapter
|
||||
key = key_for(klass, column)
|
||||
|
||||
loop do
|
||||
raw = Devise.friendly_token
|
||||
enc = OpenSSL::HMAC.hexdigest("SHA1", key, raw)
|
||||
break [raw, enc] unless adapter.find_first({ column => enc })
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def key_for(klass, column)
|
||||
@key_generator.generate_key("#{klass.name} #{column}")
|
||||
end
|
||||
end
|
||||
|
||||
# KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2
|
||||
# It can be used to derive a number of keys for various purposes from a given secret.
|
||||
# This lets Rails applications have a single secure secret, but avoid reusing that
|
|
@ -84,8 +84,12 @@ 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\?confirmation_token=#{user.confirmation_token}">}
|
||||
assert_match confirmation_url_regexp, mail.body.encoded
|
||||
|
||||
if mail.body.encoded =~ %r{<a href=\"http://#{host}/users/confirmation\?confirmation_token=([^"]+)">}
|
||||
assert_equal Devise.token_generator.digest(user.class, :confirmation_token, $1), user.confirmation_token
|
||||
else
|
||||
flunk "expected confirmation url regex to match"
|
||||
end
|
||||
end
|
||||
|
||||
test 'renders a scoped if scoped_views is set to true' do
|
||||
|
|
|
@ -80,8 +80,12 @@ class ResetPasswordInstructionsTest < ActionMailer::TestCase
|
|||
|
||||
test 'body should have link to confirm the account' do
|
||||
host = ActionMailer::Base.default_url_options[:host]
|
||||
reset_url_regexp = %r{<a href=\"http://#{host}/users/password/edit\?reset_password_token=#{user.reset_password_token}">}
|
||||
assert_match reset_url_regexp, mail.body.encoded
|
||||
|
||||
if mail.body.encoded =~ %r{<a href=\"http://#{host}/users/password/edit\?reset_password_token=([^"]+)">}
|
||||
assert_equal Devise.token_generator.digest(user.class, :reset_password_token, $1), user.reset_password_token
|
||||
else
|
||||
flunk "expected reset password url regex to match"
|
||||
end
|
||||
end
|
||||
|
||||
test 'mailer sender accepts a proc' do
|
||||
|
|
|
@ -81,7 +81,11 @@ class UnlockInstructionsTest < ActionMailer::TestCase
|
|||
|
||||
test 'body should have link to unlock the account' do
|
||||
host = ActionMailer::Base.default_url_options[:host]
|
||||
unlock_url_regexp = %r{<a href=\"http://#{host}/users/unlock\?unlock_token=#{user.unlock_token}">}
|
||||
assert_match unlock_url_regexp, mail.body.encoded
|
||||
|
||||
if mail.body.encoded =~ %r{<a href=\"http://#{host}/users/unlock\?unlock_token=([^"]+)">}
|
||||
assert_equal Devise.token_generator.digest(user.class, :unlock_token, $1), user.unlock_token
|
||||
else
|
||||
flunk "expected unlock url regex to match"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -51,13 +51,21 @@ class ConfirmableTest < ActiveSupport::TestCase
|
|||
assert_equal "was already confirmed, please try signing in", user.errors[:email].join
|
||||
end
|
||||
|
||||
test 'should find and confirm a user automatically' do
|
||||
test 'DEPRECATED: should find and confirm a user automatically' do
|
||||
user = create_user
|
||||
confirmed_user = User.confirm_by_token(user.confirmation_token)
|
||||
assert_equal confirmed_user, user
|
||||
assert user.reload.confirmed?
|
||||
end
|
||||
|
||||
test 'should find and confirm a user automatically based on the raw token' do
|
||||
user = create_user
|
||||
raw = user.instance_variable_get(:@raw_confirmation_token)
|
||||
confirmed_user = User.confirm_by_token(raw)
|
||||
assert_equal confirmed_user, user
|
||||
assert user.reload.confirmed?
|
||||
end
|
||||
|
||||
test 'should return a new record with errors when a invalid token is given' do
|
||||
confirmed_user = User.confirm_by_token('invalid_confirmation_token')
|
||||
assert_not confirmed_user.persisted?
|
||||
|
@ -176,7 +184,7 @@ class ConfirmableTest < ActiveSupport::TestCase
|
|||
test 'should not be able to send instructions if the user is already confirmed' do
|
||||
user = create_user
|
||||
user.confirm!
|
||||
assert_not user.resend_confirmation_token
|
||||
assert_not user.resend_confirmation_instructions
|
||||
assert user.confirmed?
|
||||
assert_equal 'was already confirmed, please try signing in', user.errors[:email].join
|
||||
end
|
||||
|
@ -285,32 +293,12 @@ class ConfirmableTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
test 'should generate a new token if the previous one has expired' do
|
||||
swap Devise, :confirm_within => 3.days do
|
||||
user = create_user
|
||||
user.update_attribute(:confirmation_sent_at, 4.days.ago)
|
||||
old = user.confirmation_token
|
||||
user.resend_confirmation_token
|
||||
assert_not_equal user.confirmation_token, old
|
||||
end
|
||||
end
|
||||
|
||||
test 'should generate a new token when a valid one does not exist' do
|
||||
swap Devise, :confirm_within => 3.days do
|
||||
user = create_user
|
||||
user.update_attribute(:confirmation_sent_at, 4.days.ago)
|
||||
old = user.confirmation_token
|
||||
user.ensure_confirmation_token!
|
||||
assert_not_equal user.confirmation_token, old
|
||||
end
|
||||
end
|
||||
|
||||
test 'should not generate a new token when a valid one exists' do
|
||||
test 'always generate a new token on resend' do
|
||||
user = create_user
|
||||
assert_not_nil user.confirmation_token
|
||||
old = user.confirmation_token
|
||||
user.ensure_confirmation_token!
|
||||
assert_equal user.confirmation_token, old
|
||||
old = user.confirmation_token
|
||||
user = User.find(user.id)
|
||||
user.resend_confirmation_instructions
|
||||
assert_not_equal user.confirmation_token, old
|
||||
end
|
||||
|
||||
test 'should call after_confirmation if confirmed' do
|
||||
|
|
|
@ -139,7 +139,7 @@ class LockableTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
test 'should find and unlock a user automatically' do
|
||||
test 'DEPRECATED: should find and unlock a user automatically' do
|
||||
user = create_user
|
||||
user.lock_access!
|
||||
locked_user = User.unlock_access_by_token(user.unlock_token)
|
||||
|
@ -147,6 +147,14 @@ class LockableTest < ActiveSupport::TestCase
|
|||
assert_not user.reload.access_locked?
|
||||
end
|
||||
|
||||
test 'should find and unlock a user automatically based on raw token' do
|
||||
user = create_user
|
||||
raw = user.send_unlock_instructions
|
||||
locked_user = User.unlock_access_by_token(raw)
|
||||
assert_equal locked_user, user
|
||||
assert_not user.reload.access_locked?
|
||||
end
|
||||
|
||||
test 'should return a new record with errors when a invalid token is given' do
|
||||
locked_user = User.unlock_access_by_token('invalid_token')
|
||||
assert_not locked_user.persisted?
|
||||
|
@ -195,7 +203,7 @@ class LockableTest < ActiveSupport::TestCase
|
|||
|
||||
test 'should not be able to send instructions if the user is not locked' do
|
||||
user = create_user
|
||||
assert_not user.resend_unlock_token
|
||||
assert_not user.resend_unlock_instructions
|
||||
assert_not user.access_locked?
|
||||
assert_equal 'was not locked', user.errors[:email].join
|
||||
end
|
||||
|
@ -203,7 +211,7 @@ class LockableTest < ActiveSupport::TestCase
|
|||
test 'should not be able to send instructions if the user if not locked and have username as unlock key' do
|
||||
swap Devise, :unlock_keys => [:username] do
|
||||
user = create_user
|
||||
assert_not user.resend_unlock_token
|
||||
assert_not user.resend_unlock_instructions
|
||||
assert_not user.access_locked?
|
||||
assert_equal 'was not locked', user.errors[:username].join
|
||||
end
|
||||
|
|
|
@ -108,14 +108,22 @@ class RecoverableTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
test 'should find a user to reset his password based on reset_password_token' do
|
||||
test 'DEPRECATED: should find a user to reset his password based on reset_password_token' do
|
||||
user = create_user
|
||||
user.ensure_reset_password_token!
|
||||
user.send_reset_password_instructions
|
||||
|
||||
reset_password_user = User.reset_password_by_token(:reset_password_token => user.reset_password_token)
|
||||
assert_equal reset_password_user, user
|
||||
end
|
||||
|
||||
test 'should find a user to reset his password based on the raw token' do
|
||||
user = create_user
|
||||
raw = user.send_reset_password_instructions
|
||||
|
||||
reset_password_user = User.reset_password_by_token(:reset_password_token => raw)
|
||||
assert_equal reset_password_user, user
|
||||
end
|
||||
|
||||
test 'should return a new record with errors if no reset_password_token is found' do
|
||||
reset_password_user = User.reset_password_by_token(:reset_password_token => 'invalid_token')
|
||||
assert_not reset_password_user.persisted?
|
||||
|
@ -130,9 +138,9 @@ class RecoverableTest < ActiveSupport::TestCase
|
|||
|
||||
test 'should return a new record with errors if password is blank' do
|
||||
user = create_user
|
||||
user.ensure_reset_password_token!
|
||||
raw = user.send_reset_password_instructions
|
||||
|
||||
reset_password_user = User.reset_password_by_token(:reset_password_token => user.reset_password_token, :password => '')
|
||||
reset_password_user = User.reset_password_by_token(:reset_password_token => raw, :password => '')
|
||||
assert_not reset_password_user.errors.empty?
|
||||
assert_match "can't be blank", reset_password_user.errors[:password].join
|
||||
end
|
||||
|
@ -140,10 +148,10 @@ class RecoverableTest < ActiveSupport::TestCase
|
|||
test 'should reset successfully user password given the new password and confirmation' do
|
||||
user = create_user
|
||||
old_password = user.password
|
||||
user.ensure_reset_password_token!
|
||||
raw = user.send_reset_password_instructions
|
||||
|
||||
User.reset_password_by_token(
|
||||
:reset_password_token => user.reset_password_token,
|
||||
:reset_password_token => raw,
|
||||
:password => 'new_password',
|
||||
:password_confirmation => 'new_password'
|
||||
)
|
||||
|
@ -153,38 +161,17 @@ class RecoverableTest < ActiveSupport::TestCase
|
|||
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
|
||||
raw = user.send_reset_password_instructions
|
||||
|
||||
old_password = user.password
|
||||
user.ensure_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,
|
||||
:reset_password_token => raw,
|
||||
:password => 'new_password',
|
||||
:password_confirmation => 'new_password'
|
||||
)
|
||||
|
@ -201,22 +188,5 @@ class RecoverableTest < ActiveSupport::TestCase
|
|||
:reset_password_sent_at,
|
||||
:reset_password_token
|
||||
]
|
||||
end
|
||||
|
||||
test 'should generate a new token when a valid one does not exist' do
|
||||
user = create_user
|
||||
assert_nil user.reset_password_token
|
||||
|
||||
user.ensure_reset_password_token!
|
||||
assert_not_nil user.reset_password_token
|
||||
end
|
||||
|
||||
test 'should not generate a new token when a valid one exists' do
|
||||
user = create_user
|
||||
user.send :generate_reset_password_token!
|
||||
assert_not_nil user.reset_password_token
|
||||
old = user.reset_password_token
|
||||
user.ensure_reset_password_token!
|
||||
assert_equal user.reset_password_token, old
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue