Bring rememberable back.

This commit is contained in:
José Valim 2010-01-14 15:47:14 +01:00
parent f46d1b1d81
commit 2afad49a96
12 changed files with 152 additions and 98 deletions

View File

@ -27,8 +27,8 @@ module Devise
autoload :MongoMapper, 'devise/orm/mongo_mapper'
end
ALL = [:authenticatable, :activatable, :confirmable, :recoverable, :rememberable,
:timeoutable, :trackable, :validatable, :lockable]
ALL = [:authenticatable, :activatable, :confirmable, :recoverable,
:rememberable, :validatable, :trackable, :timeoutable, :lockable]
# Maps controller names to devise modules
CONTROLLERS = {

View File

@ -1,7 +1,4 @@
# Each time the user is set we verify if it is still able to really sign in.
# This is done by checking the time frame the user is able to sign in without
# confirming it's account. If the user has not confirmed it's account during
# this time frame, he/she will not able to sign in anymore.
# Deny user access whenever his account is not active yet.
Warden::Manager.after_set_user do |record, warden, options|
if record && record.respond_to?(:active?) && !record.active?
scope = options[:scope]

View File

@ -0,0 +1,30 @@
# After authenticate hook to verify if the user in the given scope asked to be
# remembered while he does not sign out. Generates a new remember token for
# that specific user and adds a cookie with this user info to sign in this user
# automatically without asking for credentials. Refer to rememberable strategy
# for more info.
Warden::Manager.after_authentication do |record, warden, options|
scope = options[:scope]
remember_me = warden.params[scope].try(:fetch, :remember_me, nil)
if Devise::TRUE_VALUES.include?(remember_me) &&
warden.authenticated?(scope) && record.respond_to?(:remember_me!)
record.remember_me!
warden.response.set_cookie "remember_#{scope}_token", {
:value => record.class.serialize_into_cookie(record),
:expires => record.remember_expires_at,
:path => "/"
}
end
end
# Before logout hook to forget the user in the given scope, only if rememberable
# is activated for this scope. Also clear remember token to ensure the user
# won't be remembered again.
Warden::Manager.before_logout do |record, warden, scope|
if record.respond_to?(:forget_me!)
record.forget_me!
warden.response.delete_cookie "remember_#{scope}_token"
end
end

View File

@ -5,12 +5,14 @@
# verify timeout in the following request.
Warden::Manager.after_set_user do |record, warden, options|
scope = options[:scope]
if record && record.respond_to?(:timeout?) && warden.authenticated?(scope)
if record && record.respond_to?(:timedout?) && warden.authenticated?(scope)
last_request_at = warden.session(scope)['last_request_at']
if record.timeout?(last_request_at)
if record.timedout?(last_request_at)
warden.logout(scope)
throw :warden, :scope => scope, :message => :timeout
end
warden.session(scope)['last_request_at'] = Time.now.utc
end
end

View File

@ -1,3 +1,6 @@
require 'devise/strategies/rememberable'
require 'devise/hooks/rememberable'
module Devise
module Models
# Rememberable manages generating and clearing token for remember the user

View File

@ -16,7 +16,7 @@ module Devise
end
# Checks whether the user session has expired based on configured time.
def timeout?(last_access)
def timedout?(last_access)
last_access && last_access <= self.class.timeout_in.ago
end

View File

@ -1,12 +1,6 @@
# Taken from RailsWarden, thanks to Hassox. http://github.com/hassox/rails_warden
module Warden::Mixins::Common
def request
return @request if @request
if env['action_controller.rescue.request']
@request = env['action_controller.rescue.request']
else
Rack::Request.new(env)
end
@request ||= env['action_controller.rescue.request']
end
def reset_session!
@ -15,22 +9,15 @@ module Warden::Mixins::Common
end
def response
return @response if @response
if env['action_controller.rescue.response']
@response = env['action_controller.rescue.response']
else
Rack::Response.new(env)
end
@response ||= env['action_controller.rescue.response']
end
end
class Warden::SessionSerializer
# Hook to serialize user into session. Overwrite if you want.
def serialize(record)
[record.class, record.id]
end
# Hook to serialize user from session. Overwrite if you want.
def deserialize(keys)
klass, id = keys
klass.find(:first, :conditions => { :id => id })

View File

@ -4,9 +4,7 @@ module Devise
module Strategies
# Default strategy for signing in a user, based on his email and password.
# Redirects to sign_in page if it's not authenticated
class Authenticatable < Warden::Strategies::Base
include Devise::Strategies::Base
class Authenticatable < Base
def valid?
super && params[scope] && params[scope][:password].present?
end

View File

@ -1,7 +1,7 @@
module Devise
module Strategies
# Base strategy for Devise. Responsible for verifying correct scope and mapping.
module Base
class Base < ::Warden::Strategies::Base
# Validate strategy. By default will raise an error if no scope or an
# invalid mapping is found.
def valid?

View File

@ -0,0 +1,37 @@
require 'devise/strategies/base'
module Devise
module Strategies
# Remember the user through the remember token. This strategy is responsible
# to verify whether there is a cookie with the remember token, and to
# recreate the user from this cookie if it exists. Must be called *before*
# authenticatable.
class Rememberable < Devise::Strategies::Base
# A valid strategy for rememberable needs a remember token in the cookies.
def valid?
super && remember_me_cookie.present?
end
# To authenticate a user we deserialize the cookie and attempt finding
# the record in the database. If the attempt fails, we pass to another
# strategy handle the authentication.
def authenticate!
if resource = mapping.to.serialize_from_cookie(remember_me_cookie)
success!(resource)
else
pass
end
end
private
# Accessor for remember cookie
def remember_me_cookie
@remember_me_cookie ||= request.cookies["remember_#{mapping.name}_token"]
end
end
end
end
Warden::Strategies.add(:rememberable, Devise::Strategies::Rememberable)

View File

@ -1,63 +1,63 @@
# 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['warden.user.user.key'] = User.serialize_into_cookie(user) + add_to_token
# user
# end
#
# test 'do not remember the user if he has not checked remember me option' do
# user = sign_in_as_user
# assert_nil user.reload.remember_token
# end
#
# test 'generate remember token after sign in' do
# user = sign_in_as_user :remember_me => true
# assert_not_nil user.reload.remember_token
# end
#
# test 'remember the user before sign in' do
# user = create_user_and_remember
# get users_path
# assert_response :success
# assert warden.authenticated?(:user)
# assert warden.user(:user) == user
# end
#
# test 'do not remember with invalid token' do
# user = create_user_and_remember('add')
# get users_path
# assert_response :success
# 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
# assert warden.authenticated?(:user)
# get destroy_user_session_path
# assert_not warden.authenticated?(:user)
# assert_nil user.reload.remember_token
# end
#
# test 'do not remember the user anymore after forget' do
# user = create_user_and_remember
# get users_path
# assert warden.authenticated?(:user)
# get destroy_user_session_path
# get users_path
# assert_not warden.authenticated?(:user)
# end
# end
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_user_token'] = User.serialize_into_cookie(user) + add_to_token
user
end
test 'do not remember the user if he has not checked remember me option' do
user = sign_in_as_user
assert_nil user.reload.remember_token
end
test 'generate remember token after sign in' do
user = sign_in_as_user :remember_me => true
assert_not_nil user.reload.remember_token
end
test 'remember the user before sign in' do
user = create_user_and_remember
get users_path
assert_response :success
assert warden.authenticated?(:user)
assert warden.user(:user) == user
end
test 'do not remember with invalid token' do
user = create_user_and_remember('add')
get users_path
assert_response :success
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
assert warden.authenticated?(:user)
get destroy_user_session_path
assert_not warden.authenticated?(:user)
assert_nil user.reload.remember_token
end
test 'do not remember the user anymore after forget' do
user = create_user_and_remember
get users_path
assert warden.authenticated?(:user)
get destroy_user_session_path
get users_path
assert_not warden.authenticated?(:user)
end
end

View File

@ -3,26 +3,26 @@ require 'test/test_helper'
class TimeoutableTest < ActiveSupport::TestCase
test 'should be expired' do
assert new_user.timeout?(31.minutes.ago)
assert new_user.timedout?(31.minutes.ago)
end
test 'should not be expired' do
assert_not new_user.timeout?(29.minutes.ago)
assert_not new_user.timedout?(29.minutes.ago)
end
test 'should not be expired when params is nil' do
assert_not new_user.timeout?(nil)
assert_not new_user.timedout?(nil)
end
test 'fallback to Devise config option' do
swap Devise, :timeout_in => 1.minute do
user = new_user
assert user.timeout?(2.minutes.ago)
assert_not user.timeout?(30.seconds.ago)
assert user.timedout?(2.minutes.ago)
assert_not user.timedout?(30.seconds.ago)
Devise.timeout_in = 5.minutes
assert_not user.timeout?(2.minutes.ago)
assert user.timeout?(6.minutes.ago)
assert_not user.timedout?(2.minutes.ago)
assert user.timedout?(6.minutes.ago)
end
end
end