Store the salt in session and expire the session if the user changes his password

This commit is contained in:
José Valim 2010-09-25 17:24:42 +02:00
parent 6613653df0
commit 617e142e34
7 changed files with 59 additions and 12 deletions

View File

@ -11,6 +11,7 @@
* Ensure the friendly token does not include "_" or "-" since some e-mails may not autolink it properly (by github.com/rymai) * Ensure the friendly token does not include "_" or "-" since some e-mails may not autolink it properly (by github.com/rymai)
* Extracted encryptors into :encryptable for better bcrypt support * Extracted encryptors into :encryptable for better bcrypt support
* :rememberable is now able to use salt as token if no remember_token is provided * :rememberable is now able to use salt as token if no remember_token is provided
* Store the salt in session and expire the session if the user changes his password
* bugfix * bugfix
* after_sign_in_path_for always receives a resource * after_sign_in_path_for always receives a resource

View File

@ -31,6 +31,7 @@ class Devise::RegistrationsController < ApplicationController
def update def update
if resource.update_with_password(params[resource_name]) if resource.update_with_password(params[resource_name])
set_flash_message :notice, :updated set_flash_message :notice, :updated
sign_in resource_name, resource, :bypass => true
redirect_to after_update_path_for(resource) redirect_to after_update_path_for(resource)
else else
clean_up_passwords(resource) clean_up_passwords(resource)

View File

@ -87,18 +87,29 @@ module Devise
# Sign in an user that already was authenticated. This helper is useful for logging # Sign in an user that already was authenticated. This helper is useful for logging
# users in after sign up. # users in after sign up.
# #
# All options given to sign_in is passed forward to the set_user method in warden.
# The only exception is the :bypass option, which bypass warden callbacks and stores
# the user straight in session. This option is useful in cases the user is already
# signed in, but we want to refresh the credentials in session.
#
# Examples: # Examples:
# #
# sign_in :user, @user # sign_in(scope, resource) # sign_in :user, @user # sign_in(scope, resource)
# sign_in @user # sign_in(resource) # sign_in @user # sign_in(resource)
# sign_in @user, :event => :authentication # sign_in(resource, options) # sign_in @user, :event => :authentication # sign_in(resource, options)
# # sign_in @user, :bypass => true # sign_in(resource, options)
#
def sign_in(resource_or_scope, *args) def sign_in(resource_or_scope, *args)
options = args.extract_options! options = args.extract_options!
scope = Devise::Mapping.find_scope!(resource_or_scope) scope = Devise::Mapping.find_scope!(resource_or_scope)
resource = args.last || resource_or_scope resource = args.last || resource_or_scope
expire_session_data_after_sign_in!
warden.set_user(resource, options.merge!(:scope => scope)) if options[:bypass]
warden.session_serializer.store(resource, scope)
else
expire_session_data_after_sign_in!
warden.set_user(resource, options.merge!(:scope => scope))
end
end end
# Sign out a given user or scope. This helper is useful for signing out an user # Sign out a given user or scope. This helper is useful for signing out an user

View File

@ -73,6 +73,9 @@ module Devise
:inactive :inactive
end end
def authenticatable_salt
end
module ClassMethods module ClassMethods
Devise::Models.config(self, :authentication_keys, :request_keys, :http_authenticatable, :params_authenticatable) Devise::Models.config(self, :authentication_keys, :request_keys, :http_authenticatable, :params_authenticatable)

View File

@ -15,18 +15,28 @@ end
class Warden::SessionSerializer class Warden::SessionSerializer
def serialize(record) def serialize(record)
[record.class.name, record.id] [record.class.name, record.id, record.authenticatable_salt]
end end
def deserialize(keys) def deserialize(keys)
klass, id = keys if keys.size == 2
klass.constantize.find(:first, :conditions => { :id => id }) raise "Devise changed how it stores objects in session. If you are seeing this message, " <<
rescue NameError => e "you can fix it by changing one character in your cookie secret, forcing all previous " <<
if e.message =~ /uninitialized constant/ "cookies to expire, or cleaning up your database sessions if you are using a db store."
Rails.logger.debug "Trying to deserialize invalid class #{klass}" end
nil
else klass, id, salt = keys
raise
begin
record = klass.constantize.find(:first, :conditions => { :id => id })
record if record && record.authenticatable_salt == salt
rescue NameError => e
if e.message =~ /uninitialized constant/
Rails.logger.debug "Trying to deserialize invalid class #{klass}"
nil
else
raise
end
end end
end end
end end

View File

@ -100,6 +100,13 @@ class ControllerAuthenticableTest < ActionController::TestCase
@controller.sign_in(user) @controller.sign_in(user)
end end
test 'sign in accepts bypass as option' do
user = User.new
@mock_warden.expects(:session_serializer).returns(serializer = mock())
serializer.expects(:store).with(user, :user)
@controller.sign_in(user, :bypass => true)
end
test 'sign out proxy to logout on warden' do test 'sign out proxy to logout on warden' do
@mock_warden.expects(:user).with(:user).returns(true) @mock_warden.expects(:user).with(:user).returns(true)
@mock_warden.expects(:logout).with(:user).returns(true) @mock_warden.expects(:logout).with(:user).returns(true)

View File

@ -98,6 +98,20 @@ class RegistrationTest < ActionController::IntegrationTest
assert_equal "user.new@email.com", User.first.email assert_equal "user.new@email.com", User.first.email
end end
test 'a signed in user should still be able to use the website after changing his password' do
sign_in_as_user
get edit_user_registration_path
fill_in 'password', :with => '12345678'
fill_in 'password confirmation', :with => '12345678'
fill_in 'current password', :with => '123456'
click_button 'Update'
assert_contain 'You updated your account successfully.'
get users_path
assert warden.authenticated?(:user)
end
test 'a signed in user should not change his current user with invalid password' do test 'a signed in user should not change his current user with invalid password' do
sign_in_as_user sign_in_as_user
get edit_user_registration_path get edit_user_registration_path