# frozen_string_literal: true
module Authlogic
module ActsAsAuthentic
# This is one of my favorite features that I think is pretty cool. It's
# things like this that make a library great and let you know you are on the
# right track.
#
# Just to clear up any confusion, Authlogic stores both the record id and
# the persistence token in the session. Why? So stale sessions can not be
# persisted. It stores the id so it can quickly find the record, and the
# persistence token to ensure no sessions are stale. So if the persistence
# token changes, the user must log back in.
#
# Well, the persistence token changes with the password. What happens if the
# user changes his own password? He shouldn't have to log back in, he's the
# one that made the change.
#
# That being said, wouldn't it be nice if their session and cookie
# information was automatically updated? Instead of cluttering up your
# controller with redundant session code. The same thing goes for new
# registrations.
#
# That's what this module is all about. This will automatically maintain the
# cookie and session values as records are saved.
module SessionMaintenance
def self.included(klass)
klass.class_eval do
extend Config
add_acts_as_authentic_module(Methods)
end
end
# Configuration for the session maintenance aspect of acts_as_authentic.
# These methods become class methods of ::ActiveRecord::Base.
module Config
# In order to turn off automatic maintenance of sessions
# after create, just set this to false.
#
# * Default: true
# * Accepts: Boolean
def log_in_after_create(value = nil)
rw_config(:log_in_after_create, value, true)
end
alias log_in_after_create= log_in_after_create
# In order to turn off automatic maintenance of sessions when updating
# the password, just set this to false.
#
# * Default: true
# * Accepts: Boolean
def log_in_after_password_change(value = nil)
rw_config(:log_in_after_password_change, value, true)
end
alias log_in_after_password_change= log_in_after_password_change
# As you may know, authlogic sessions can be separate by id (See
# Authlogic::Session::Base#id). You can specify here what session ids
# you want auto maintained. By default it is the main session, which has
# an id of nil.
#
# * Default: [nil]
# * Accepts: Array
def session_ids(value = nil)
rw_config(:session_ids, value, [nil])
end
alias session_ids= session_ids
# The name of the associated session class. This is inferred by the name
# of the model.
#
# * Default: "#{klass.name}Session".constantize
# * Accepts: Class
def session_class(value = nil)
const = begin
"#{base_class.name}Session".constantize
rescue NameError
nil
end
rw_config(:session_class, value, const)
end
alias session_class= session_class
end
# This module, as one of the `acts_as_authentic_modules`, is only included
# into an ActiveRecord model if that model calls `acts_as_authentic`.
module Methods
def self.included(klass)
klass.class_eval do
before_save :get_session_information, if: :update_sessions?
before_save :maintain_sessions, if: :update_sessions?
end
end
# Save the record and skip session maintenance all together.
def save_without_session_maintenance(*args)
self.skip_session_maintenance = true
result = save(*args)
self.skip_session_maintenance = false
result
end
private
def skip_session_maintenance=(value)
@skip_session_maintenance = value
end
def skip_session_maintenance
@skip_session_maintenance ||= false
end
def update_sessions?
!skip_session_maintenance &&
session_class &&
session_class.activated? &&
maintain_session? &&
!session_ids.blank? &&
will_save_change_to_persistence_token?
end
def maintain_session?
log_in_after_create? || log_in_after_password_change?
end
def get_session_information
# Need to determine if we are completely logged out, or logged in as
# another user.
@_sessions = []
session_ids.each do |session_id|
session = session_class.find(session_id, self)
@_sessions << session if session&.record
end
end
def maintain_sessions
if @_sessions.empty?
create_session
else
update_sessions
end
end
def create_session
# We only want to automatically login into the first session, since
# this is the main session. The other sessions are sessions that
# need to be created after logging into the main session.
session_id = session_ids.first
session_class.create(*[self, self, session_id].compact)
true
end
def update_sessions
# We found sessions above, let's update them with the new info
@_sessions.each do |stale_session|
next if stale_session.record != self
stale_session.unauthorized_record = self
stale_session.save
end
true
end
def session_ids
self.class.session_ids
end
def session_class
self.class.session_class
end
def log_in_after_create?
new_record? && self.class.log_in_after_create
end
def log_in_after_password_change?
will_save_change_to_persistence_token? && self.class.log_in_after_password_change
end
end
end
end
end