module Authlogic
module Session
# Just like ActiveRecord has "magic" columns, such as: created_at and updated_at. Authlogic has its own "magic" columns too:
#
# Column name Description
# login_count Increased every time an explicit login is made. This will *NOT* increase if logging in by a session, cookie, or basic http auth
# failed_login_count This increases for each consecutive failed login. See Authlogic::Session::BruteForceProtection and the consecutive_failed_logins_limit config option for more details.
# last_request_at Updates every time the user logs in, either by explicitly logging in, or logging in by cookie, session, or http auth
# current_login_at Updates with the current time when an explicit login is made.
# last_login_at Updates with the value of current_login_at before it is reset.
# current_login_ip Updates with the request remote_ip when an explicit login is made.
# last_login_ip Updates with the value of current_login_ip before it is reset.
module MagicColumns
def self.included(klass)
klass.class_eval do
extend Config
include InstanceMethods
after_persisting :set_last_request_at, :if => :set_last_request_at?
validate :increase_failed_login_count
before_save :update_info
before_save :set_last_request_at, :if => :set_last_request_at?
end
end
# Configuration for the magic columns feature.
module Config
# Every time a session is found the last_request_at field for that record is updatd with the current time, if that field exists.
# If you want to limit how frequent that field is updated specify the threshold here. For example, if your user is making a
# request every 5 seconds, and you feel this is too frequent, and feel a minute is a good threashold. Set this to 1.minute.
# Once a minute has passed in between requests the field will be updated.
#
# * Default: 0
# * Accepts: integer representing time in seconds
def last_request_at_threshold(value = nil)
rw_config(:last_request_at_threshold, value, 0)
end
alias_method :last_request_at_threshold=, :last_request_at_threshold
end
# The methods available for an Authlogic::Session::Base object that make up the magic columns feature.
module InstanceMethods
private
def increase_failed_login_count
if invalid_password? && attempted_record.respond_to?(:failed_login_count)
attempted_record.failed_login_count ||= 0
attempted_record.failed_login_count += 1
end
end
def update_info
record.login_count = (record.login_count.blank? ? 1 : record.login_count + 1) if record.respond_to?(:login_count)
record.failed_login_count = 0 if record.respond_to?(:failed_login_count)
if record.respond_to?(:current_login_at)
record.last_login_at = record.current_login_at if record.respond_to?(:last_login_at)
record.current_login_at = klass.default_timezone == :utc ? Time.now.utc : Time.now
end
if record.respond_to?(:current_login_ip)
record.last_login_ip = record.current_login_ip if record.respond_to?(:last_login_ip)
record.current_login_ip = controller.request.remote_ip
end
end
# This method lets authlogic know whether it should allow the last_request_at field to be updated
# with the current time (Time.now). One thing to note here is that it also checks for the existence of a
# last_request_update_allowed? method in your controller. This allows you to control this method pragmatically
# in your controller.
#
# For example, what if you had a javascript function that polled the server updating how much time is left in their
# session before it times out. Obviously you would want to ignore this request, because then the user would never time out.
# So you can do something like this in your controller:
#
# def last_request_update_allowed?
# action_name =! "update_session_time_left"
# end
#
# You can do whatever you want with that method.
def set_last_request_at? # :doc:
return false if !record || !klass.column_names.include?("last_request_at")
return controller.last_request_update_allowed? if controller.responds_to_last_request_update_allowed?
record.last_request_at.blank? || last_request_at_threshold.to_i.seconds.ago >= record.last_request_at
end
def set_last_request_at
record.last_request_at = klass.default_timezone == :utc ? Time.now.utc : Time.now
end
def last_request_at_threshold
self.class.last_request_at_threshold
end
end
end
end
end