From 5631b8dacde1c8335c1bc2f6df57b6dfcdee6982 Mon Sep 17 00:00:00 2001 From: "Carlos A. da Silva" Date: Thu, 22 Oct 2009 09:09:34 -0200 Subject: [PATCH] Remember the user only if the remember token has not expired. --- lib/devise/migrations.rb | 4 +- lib/devise/models/rememberable.rb | 25 ++++++-- test/integration/rememberable_test.rb | 9 +++ test/models/confirmable_test.rb | 2 +- test/models/rememberable_test.rb | 83 ++++++++++++++++++++++++++- 5 files changed, 112 insertions(+), 11 deletions(-) diff --git a/lib/devise/migrations.rb b/lib/devise/migrations.rb index 5601f5b6..d8ebe645 100644 --- a/lib/devise/migrations.rb +++ b/lib/devise/migrations.rb @@ -40,11 +40,11 @@ module Devise string :reset_password_token, :limit => 20 end - # Creates remember_token and remember_expires_at. + # Creates remember_token and remember_created_at. # def rememberable string :remember_token, :limit => 20 - datetime :remember_expires_at + datetime :remember_created_at end end diff --git a/lib/devise/models/rememberable.rb b/lib/devise/models/rememberable.rb index bfb872c9..aa17c994 100644 --- a/lib/devise/models/rememberable.rb +++ b/lib/devise/models/rememberable.rb @@ -22,6 +22,7 @@ module Devise # # lookup the user based on the incoming cookie information # User.serialize_from_cookie(cookie_string) module Rememberable + Devise.model_config(self, :remember_for, 0) def self.included(base) base.class_eval do @@ -36,6 +37,7 @@ module Devise # Generate a new remember token and save the record without validations. def remember_me! self.remember_token = friendly_token + self.remember_created_at = Time.now save(false) end @@ -44,27 +46,38 @@ module Devise def forget_me! if remember_token? self.remember_token = nil + self.remember_created_at = nil save(false) end end # Checks whether the incoming token matches or not with the record token. def valid_remember_token?(token) - remember_token.present? && remember_token == token + !remember_expired? && remember_token == token + end + + # Remember token should be expired if expiration time not overpass now. + def remember_expired? + !remember_token? || remember_expires_at <= Time.now.utc + end + + # Remember token expires at created time + remember_for configuration + def remember_expires_at + remember_created_at + remember_for end module ClassMethods # Create the cookie key using the record id and remember_token - def serialize_into_cookie(record) - "#{record.id}::#{record.remember_token}" + def serialize_into_cookie(rememberable) + "#{rememberable.id}::#{rememberable.remember_token}" end # Recreate the user based on the stored cookie def serialize_from_cookie(cookie) - record_id, remember_token = cookie.split('::') - record = find_by_id(record_id) - record if record.try(:valid_remember_token?, remember_token) + rememberable_id, remember_token = cookie.split('::') + rememberable = find_by_id(rememberable_id) if rememberable_id + rememberable if rememberable.try(:valid_remember_token?, remember_token) end end diff --git a/test/integration/rememberable_test.rb b/test/integration/rememberable_test.rb index 18b72fb6..9145491a 100644 --- a/test/integration/rememberable_test.rb +++ b/test/integration/rememberable_test.rb @@ -3,6 +3,7 @@ require 'test/test_helper' class RememberMeTest < ActionController::IntegrationTest def create_user_and_remember(add_to_token='') + Devise.remember_for = 1 user = create_user user.remember_me! cookies['remember_token'] = User.serialize_into_cookie(user) + add_to_token @@ -36,6 +37,14 @@ class RememberMeTest < ActionController::IntegrationTest assert_not warden.authenticated?(:user) end + test 'do not remember with token expired' do + user = create_user_and_remember + Devise.remember_for = 0 + get users_path + assert_response :success + assert_not warden.authenticated?(:user) + end + test 'forget the user before sign out' do user = create_user_and_remember get users_path diff --git a/test/models/confirmable_test.rb b/test/models/confirmable_test.rb index 153e510b..78e38915 100644 --- a/test/models/confirmable_test.rb +++ b/test/models/confirmable_test.rb @@ -209,7 +209,7 @@ class ConfirmableTest < ActiveSupport::TestCase confirm_in = Devise.confirm_in Devise.confirm_in = 1.day user = new_user - user.confirmation_sent_at = 2.day.ago + user.confirmation_sent_at = 2.days.ago assert_not user.active? Devise.confirm_in = 3.days assert user.active? diff --git a/test/models/rememberable_test.rb b/test/models/rememberable_test.rb index b33fd62e..c44bfa44 100644 --- a/test/models/rememberable_test.rb +++ b/test/models/rememberable_test.rb @@ -2,6 +2,10 @@ require 'test/test_helper' class RememberableTest < ActiveSupport::TestCase + def setup + Devise.remember_for = 1 + end + test 'should respond to remember_me attribute' do user = new_user assert user.respond_to?(:remember_me) @@ -20,16 +24,32 @@ class RememberableTest < ActiveSupport::TestCase assert_not user.changed? end + test 'remember_me should calculate expires_at based on remember_for setup' do + user = create_user + assert_not user.remember_created_at? + user.remember_me! + assert user.remember_created_at? + assert_equal Date.today, user.remember_created_at.to_date + end + test 'forget_me should clear remember token and save the record without validating' do user = create_user user.remember_me! - assert_not_nil user.remember_token + assert user.remember_token? user.expects(:valid?).never user.forget_me! - assert_nil user.remember_token + assert_not user.remember_token? assert_not user.changed? end + test 'forget_me should clear remember_expires_at' do + user = create_user + user.remember_me! + assert user.remember_created_at? + user.forget_me! + assert_not user.remember_created_at? + end + test 'forget should do nothing if no remember token exists' do user = create_user user.expects(:save).never @@ -45,6 +65,13 @@ class RememberableTest < ActiveSupport::TestCase assert_not user.valid_remember_token?(user.remember_token) end + test 'valid remember token should also verify if remember is not expired' do + user = create_user + user.remember_me! + user.update_attribute(:remember_created_at, 3.days.ago) + assert_not user.valid_remember_token?(user.remember_token) + end + test 'serialize into cookie' do user = create_user user.remember_me! @@ -65,4 +92,56 @@ class RememberableTest < ActiveSupport::TestCase user = create_user assert_nil User.serialize_from_cookie("#{user.id}::#{user.remember_token}123") end + + test 'remember for should fallback to devise remember for default configuration' do + begin + remember_for = Devise.remember_for + user = create_user + Devise.remember_for = 1.day + user.remember_me! + assert_not user.remember_expired? + Devise.remember_for = 0.days + user.remember_me! + assert user.remember_expired? + ensure + Devise.remember_for = remember_for + end + end + + test 'remember should be expired without remember token' do + user = create_user + assert user.remember_expired? + end + + test 'remember expires at should sum date of creation with remember for configuration' do + Devise.remember_for = 3.days + user = create_user + user.remember_me! + assert_equal 3.days.from_now.to_date, user.remember_expires_at.to_date + Devise.remember_for = 5.days + assert_equal 5.days.from_now.to_date, user.remember_expires_at.to_date + end + + test 'remember should be expired if remember_for is zero' do + Devise.remember_for = 0.days + user = create_user + user.remember_me! + assert user.remember_expired? + end + + test 'remember should be expired if it was created before limit time' do + Devise.remember_for = 1.day + user = create_user + user.remember_me! + user.update_attribute(:remember_created_at, 2.days.ago) + assert user.remember_expired? + end + + test 'remember should not be expired if it was created whitin the limit time' do + Devise.remember_for = 30.days + user = create_user + user.remember_me! + user.update_attribute(:remember_created_at, 30.days.ago + 2.minutes) + assert_not user.remember_expired? + end end