Permalink
Browse files

Introducing timeoutable to timeout sessions without activity.

  • Loading branch information...
1 parent 966b48c commit 099c77e867fd10aa9225ef25748cc0342e08e8ac @carlosantoniodasilva carlosantoniodasilva committed Nov 23, 2009
@@ -34,6 +34,9 @@
# The time the user will be remembered without asking for credentials again.
# config.remember_for = 2.weeks
+ # The time interval to timeout the user session without activity.
+ # config.timeout = 10.minutes
+
# Configure the e-mail address which will be shown in DeviseMailer.
# config.mailer_sender = "foo.bar@yourapp.com"
View
@@ -1,5 +1,5 @@
module Devise
- ALL = [:authenticatable, :confirmable, :recoverable, :rememberable, :validatable].freeze
+ ALL = [:authenticatable, :confirmable, :recoverable, :rememberable, :timeoutable, :validatable].freeze
# Maps controller names to devise modules
CONTROLLERS = {
@@ -45,6 +45,10 @@ module Devise
mattr_accessor :confirm_within
@@confirm_within = 0.days
+ # Time interval to timeout the user session without activity.
+ mattr_accessor :timeout
+ @@timeout = 10.minutes
+
# Used to define the password encryption algorithm.
mattr_accessor :encryptor
@@encryptor = :sha1
@@ -141,5 +145,4 @@ def friendly_token
require 'devise/strategies/base'
require 'devise/serializers/base'
-
require 'devise/rails'
@@ -6,7 +6,6 @@
if record && record.respond_to?(:active?) && !record.active?
scope = options[:scope]
warden.logout(scope)
-
if warden.winning_strategy
# If winning strategy was set, this is being called after authenticate and
# there is no need to force a redirect.
@@ -0,0 +1,17 @@
+Warden::Manager.after_set_user do |record, warden, options|
+ if record.present?
+ scope = options[:scope]
+ # Current record may have already be logged out by another hook.
+ # For instance, Devise confirmable hook may have logged the record out.
+ # TODO: move this verify to warden: he should stop the hooks if the record
+ # is logged out by any of them.
+ if warden.authenticated?(scope)
+ last_request_at = warden.session(scope)['last_request_at']
+ if last_request_at && last_request_at <= 10.minutes.ago.utc
+ warden.logout(scope)
+ throw :warden, :scope => scope, :message => :timeout
+ end
+ warden.session(scope)['last_request_at'] = Time.now.utc
+ end
+ end
+end
@@ -0,0 +1,21 @@
+require 'devise/hooks/timeoutable'
+
+module Devise
+ module Models
+
+ # Timeoutable
+ module Timeoutable
+
+ def self.included(base)
+ base.class_eval do
+ extend ClassMethods
+ end
+ end
+
+ module ClassMethods
+ end
+
+ Devise::Models.config(self, :timeout)
+ end
+ end
+end
View
@@ -1,5 +1,5 @@
require 'test/test_helper'
-require 'ostruct'
+require 'ostruct'
class FailureTest < ActiveSupport::TestCase
@@ -22,6 +22,18 @@ def call_failure(env_params={})
assert_equal '/users/sign_in?test=true', location
end
+ test 'uses the given message' do
+ warden = OpenStruct.new(:message => 'Hello world')
+ location = call_failure('warden' => warden).second['Location']
+ assert_equal '/users/sign_in?message=Hello+world', location
+ end
+
+ test 'setup default url' do
+ Devise::FailureApp.default_url = 'test/sign_in'
+ location = call_failure('warden.options' => { :scope => nil }).second['Location']
+ assert_equal '/test/sign_in?unauthenticated=true', location
+ end
+
test 'set content type to default text/plain' do
assert_equal 'text/plain', call_failure.second['Content-Type']
end
@@ -58,17 +58,17 @@ def visit_user_confirmation_with_token(confirmation_token)
assert warden.authenticated?(:user)
end
- test 'not confirmed user and setup to block without confirmation should not be able to sign in' do
+ test 'not confirmed user with setup to block without confirmation should not be able to sign in' do
Devise.confirm_within = 0
- user = sign_in_as_user(:confirm => false)
+ sign_in_as_user(:confirm => false)
assert_contain 'You have to confirm your account before continuing'
assert_not warden.authenticated?(:user)
end
test 'not confirmed user but configured with some days to confirm should be able to sign in' do
Devise.confirm_within = 1
- user = sign_in_as_user(:confirm => false)
+ sign_in_as_user(:confirm => false)
assert_response :success
assert warden.authenticated?(:user)
@@ -0,0 +1,44 @@
+require 'test/test_helper'
+
+class SessionTimeoutTest < ActionController::IntegrationTest
+
+ def last_request_at
+ @controller.user_session['last_request_at']
+ end
+
+ test 'set last request at in user session after each request' do
+ sign_in_as_user
+ old_last_request = last_request_at
+ assert_not_nil last_request_at
+ get users_path
+ assert_not_nil last_request_at
+ assert_not_equal old_last_request, last_request_at
+ end
+
+ test 'time out user session after default limit time' do
+ sign_in_as_user
+ assert_response :success
+ assert warden.authenticated?(:user)
+
+ # Setup last_request_at to timeout
+ get new_user_path
+ assert_not_nil last_request_at
+
+ get users_path
+ assert_redirected_to new_user_session_path(:timeout => true)
+ assert_not warden.authenticated?(:user)
+ end
+
+ test 'not time out user session before default limit time' do
+ user = sign_in_as_user
+
+ # Setup last_request_at to timeout
+ get edit_user_path(user)
+ assert_not_nil last_request_at
+
+ get users_path
+ assert_response :success
+ assert warden.authenticated?(:user)
+ end
+
+end
@@ -0,0 +1,5 @@
+require 'test/test_helper'
+
+class TimeoutableTest < ActiveSupport::TestCase
+
+end
View
@@ -16,6 +16,10 @@ class Rememberable < User
devise :authenticatable, :rememberable
end
+class Timeoutable < User
+ devise :timeoutable
+end
+
class Validatable < User
devise :authenticatable, :validatable
end
@@ -32,7 +36,8 @@ class Configurable < User
devise :all, :stretches => 15,
:pepper => 'abcdef',
:confirm_within => 5.days,
- :remember_for => 7.days
+ :remember_for => 7.days,
+ :timeout => 15.minutes
end
class ActiveRecordTest < ActiveSupport::TestCase
@@ -54,33 +59,38 @@ def assert_not_include_modules(klass, *modules)
end
test 'include by default authenticatable only' do
- assert_include_modules Authenticable, :authenticatable
- assert_not_include_modules Authenticable, :confirmable, :recoverable, :rememberable, :validatable
+ assert_include_modules Authenticatable, :authenticatable
+ assert_not_include_modules Authenticatable, :confirmable, :recoverable, :rememberable, :timeoutable, :validatable
end
test 'add confirmable module only' do
assert_include_modules Confirmable, :authenticatable, :confirmable
- assert_not_include_modules Confirmable, :recoverable, :rememberable, :validatable
+ assert_not_include_modules Confirmable, :recoverable, :rememberable, :timeoutable, :validatable
end
test 'add recoverable module only' do
assert_include_modules Recoverable, :authenticatable, :recoverable
- assert_not_include_modules Recoverable, :confirmable, :rememberable, :validatable
+ assert_not_include_modules Recoverable, :confirmable, :rememberable, :timeoutable, :validatable
end
test 'add rememberable module only' do
assert_include_modules Rememberable, :authenticatable, :rememberable
- assert_not_include_modules Rememberable, :confirmable, :recoverable, :validatable
+ assert_not_include_modules Rememberable, :confirmable, :recoverable, :timeoutable, :validatable
+ end
+
+ test 'add timeoutable module only' do
+ assert_include_modules Timeoutable, :authenticatable, :timeoutable
+ assert_not_include_modules Timeoutable, :confirmable, :recoverable, :rememberable, :validatable
end
test 'add validatable module only' do
assert_include_modules Validatable, :authenticatable, :validatable
- assert_not_include_modules Validatable, :confirmable, :recoverable, :rememberable
+ assert_not_include_modules Validatable, :confirmable, :recoverable, :timeoutable, :rememberable
end
test 'add all modules' do
assert_include_modules Devisable,
- :authenticatable, :confirmable, :recoverable, :rememberable, :validatable
+ :authenticatable, :confirmable, :recoverable, :rememberable, :timeoutable, :validatable
end
test 'configure modules with except option' do
@@ -104,6 +114,10 @@ def assert_not_include_modules(klass, *modules)
assert_equal 7.days, Configurable.remember_for
end
+ test 'set a default value for timeout' do
+ assert_equal 15.minutes, Configurable.new.timeout
+ end
+
test 'set null fields on migrations' do
Admin.create!
end
@@ -4,4 +4,14 @@ class UsersController < ApplicationController
def index
user_session[:cart] = "Cart"
end
+
+ def new
+ user_session['last_request_at'] = 11.minutes.ago.utc
+ render :text => 'New user!'
+ end
+
+ def edit
+ user_session['last_request_at'] = 9.minutes.ago.utc
+ render :text => 'Edit user!'
+ end
end
@@ -8,7 +8,7 @@
:path_prefix => '/:locale',
:requirements => { :extra => 'value' }
- map.resources :users, :only => :index
+ map.resources :users, :only => [:index, :new, :edit]
map.resources :admins, :only => :index
map.root :controller => :home
View
@@ -33,6 +33,7 @@
Webrat.configure do |config|
config.mode = :rails
+ config.open_error_files = false
end
class ActiveSupport::TestCase

0 comments on commit 099c77e

Please sign in to comment.