Added trackable for sign_in_count, sign_in_at and sign_in_ip.

This commit is contained in:
José Valim 2009-11-24 15:18:42 -02:00
parent 52885725a9
commit f9c5dd6a79
11 changed files with 134 additions and 24 deletions

View File

@ -1,5 +1,6 @@
module Devise
ALL = [:authenticatable, :confirmable, :recoverable, :rememberable, :timeoutable, :validatable].freeze
ALL = [:authenticatable, :confirmable, :recoverable, :rememberable,
:trackable, :timeoutable, :validatable].freeze
# Maps controller names to devise modules
CONTROLLERS = {

View File

@ -4,16 +4,13 @@
# record is set, we set the last request time inside it's scoped session to
# verify timeout in the following request.
Warden::Manager.after_set_user do |record, warden, options|
if record && record.respond_to?(:timeout?)
scope = options[:scope]
# Record may have already been logged out by another hook (ie confirmable).
if warden.authenticated?(scope)
last_request_at = warden.session(scope)['last_request_at']
if record.timeout?(last_request_at)
warden.logout(scope)
throw :warden, :scope => scope, :message => :timeout
end
warden.session(scope)['last_request_at'] = Time.now.utc
scope = options[:scope]
if record && record.respond_to?(:timeout?) && warden.authenticated?(scope)
last_request_at = warden.session(scope)['last_request_at']
if record.timeout?(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

@ -0,0 +1,18 @@
# After each sign in, update sign in time, sign in count and sign in IP.
Warden::Manager.after_authentication do |record, warden, options|
scope = options[:scope]
if Devise.mappings[scope].try(:trackable?) && warden.authenticated?(scope)
old_current, new_current = record.current_sign_in_at, Time.now
record.last_sign_in_at = old_current || new_current
record.current_sign_in_at = new_current
old_current, new_current = record.current_sign_in_ip, warden.request.remote_ip
record.last_sign_in_ip = old_current || new_current
record.current_sign_in_ip = new_current
record.sign_in_count ||= 0
record.sign_in_count += 1
record.save(false)
end
end

View File

@ -75,6 +75,7 @@ module Devise
options = modules.extract_options!
modules = Devise.all if modules.include?(:all)
modules -= Array(options.delete(:except))
modules = Devise::ALL & modules
if !modules.include?(:authenticatable)
modules = [:authenticatable] | modules

View File

@ -0,0 +1,16 @@
require 'devise/hooks/trackable'
module Devise
module Models
# Track information about your user sign in. It tracks the following columns:
#
# * sign_in_count - Increased every time a sign in is made (by form, openid, oauth)
# * current_sign_in_at - A tiemstamp updated when the user signs in
# * last_sign_in_at - Holds the timestamp of the previous sign in
# * current_sign_in_ip - The remote ip updated when the user sign in
# * last_sign_in_at - Holds the remote ip of the previous sign in
#
module Trackable
end
end
end

View File

@ -41,6 +41,16 @@ module Devise
apply_schema :remember_created_at, DateTime
end
# Creates sign_in_count, current_sign_in_at, last_sign_in_at,
# current_sign_in_ip, last_sign_in_in.
def trackable
apply_schema :sign_in_count, Integer
apply_schema :current_sign_in_at, DateTime
apply_schema :last_sign_in_at, DateTime
apply_schema :current_sign_in_ip, String
apply_schema :last_sign_in_ip, String
end
# Overwrite with specific modification to create your own schema.
def apply_schema(name, type, options={})
raise NotImplementedError

View File

@ -0,0 +1,62 @@
require 'test/test_helper'
class TrackableTest < ActionController::IntegrationTest
test "current and last sign in timestamps are updated on each sign in" do
user = create_user
assert_nil user.current_sign_in_at
assert_nil user.last_sign_in_at
sign_in_as_user
user.reload
assert_kind_of Time, user.current_sign_in_at
assert_kind_of Time, user.last_sign_in_at
assert_equal user.current_sign_in_at, user.last_sign_in_at
assert user.current_sign_in_at >= user.created_at
visit destroy_user_session_path
new_time = 2.seconds.from_now
Time.stubs(:now).returns(new_time)
sign_in_as_user
user.reload
assert user.current_sign_in_at > user.last_sign_in_at
end
test "current and last sign in remote ip are updated on each sign in" do
user = create_user
assert_nil user.current_sign_in_ip
assert_nil user.last_sign_in_ip
sign_in_as_user
user.reload
assert_equal "127.0.0.1", user.current_sign_in_ip
assert_equal "127.0.0.1", user.last_sign_in_ip
end
test "increase sign in count" do
user = create_user
assert_nil user.sign_in_count
sign_in_as_user
user.reload
assert_equal 1, user.sign_in_count
visit destroy_user_session_path
sign_in_as_user
user.reload
assert_equal 2, user.sign_in_count
end
test "does not update anything if user is signed out along the way" do
user = create_user(:confirm => false)
sign_in_as_user
user.reload
assert_nil user.current_sign_in_at
assert_nil user.last_sign_in_at
end
end

View File

@ -0,0 +1,5 @@
require 'test/test_helper'
class TrackableTest < ActiveSupport::TestCase
end

View File

@ -16,6 +16,10 @@ class Rememberable < User
devise :authenticatable, :rememberable
end
class Trackable < User
devise :authenticatable, :trackable
end
class Timeoutable < User
devise :authenticatable, :timeoutable
end
@ -50,52 +54,47 @@ class ActiveRecordTest < ActiveSupport::TestCase
modules.each do |mod|
assert include_module?(klass, mod)
end
end
def assert_not_include_modules(klass, *modules)
modules.each do |mod|
(Devise::ALL - modules).each do |mod|
assert_not include_module?(klass, mod)
end
end
test 'include by default authenticatable only' do
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, :timeoutable, :validatable
end
test 'add recoverable module only' do
assert_include_modules Recoverable, :authenticatable, :recoverable
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, :timeoutable, :validatable
end
test 'add trackable module only' do
assert_include_modules Trackable, :authenticatable, :trackable
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, :timeoutable, :rememberable
end
test 'add all modules' do
assert_include_modules Devisable,
:authenticatable, :confirmable, :recoverable, :rememberable, :timeoutable, :validatable
:authenticatable, :confirmable, :recoverable, :rememberable, :trackable, :timeoutable, :validatable
end
test 'configure modules with except option' do
assert_include_modules Exceptable, :authenticatable, :confirmable
assert_not_include_modules Exceptable, :recoverable, :rememberable, :validatable
assert_include_modules Exceptable, :authenticatable, :confirmable, :trackable, :timeoutable
end
test 'set a default value for stretches' do

View File

@ -1,5 +1,5 @@
class Admin < ActiveRecord::Base
devise :all, :except => [:recoverable, :confirmable, :rememberable, :validatable]
devise :all, :except => [:recoverable, :confirmable, :rememberable, :validatable, :trackable]
def self.find_for_authentication(conditions)
last(:conditions => conditions)

View File

@ -25,6 +25,7 @@ ActiveRecord::Schema.define(:version => 1) do
t.confirmable
t.recoverable
t.rememberable
t.trackable
end
t.timestamps