foreman/app/models/concerns/foreman/thread_session.rb @ 7422a1f8
475cdc84 | Ohad Levy | #
|
|
# In several cases we want to break chain of responsibility in MVC a bit and provide
|
|||
# a safe way to access current user (and maybe few more data items). Storing it as
|
|||
# a global variable (or class member) is not thread-safe. Including ThreadSession::
|
|||
# UserModel in models and ThreadSession::Controller in the application controller
|
|||
# allows this without any concurrent issues.
|
|||
#
|
|||
# Idea taken from sentinent_user rails plugin.
|
|||
#
|
|||
# http://github.com/bokmann/sentient_user
|
|||
# http://github.com/astrails/let_my_controller_go
|
|||
# http://rails-bestpractices.com/posts/47-fetch-current-user-in-models
|
|||
#
|
|||
module Foreman
|
|||
module ThreadSession
|
|||
6670e58c | Ivan Necas | # module to be include in controller to clear the session data
|
|
# after (and evenutally before) the request processing.
|
|||
# Without it we're risking inter-users interference.
|
|||
module Cleaner
|
|||
dc457681 | Joseph Mitchell Magen | extend ActiveSupport::Concern
|
|
included do
|
|||
df6a9f34 | Dominic Cleal | around_action :clear_thread
|
|
6670e58c | Ivan Necas | end
|
|
def clear_thread
|
|||
if Thread.current[:user] && !Rails.env.test?
|
|||
Rails.logger.warn("Current user is set, but not expected. Clearing")
|
|||
Thread.current[:user] = nil
|
|||
end
|
|||
yield
|
|||
ensure
|
|||
[:user, :organization, :location].each do |key|
|
|||
Thread.current[key] = nil
|
|||
end
|
|||
end
|
|||
end
|
|||
c8f5cd53 | Tomer Brisker | # This allows getting and setting all current values in case it's needed,
|
|
# for example to pass to an enumerator that is executed by a separate thread
|
|||
module Context
|
|||
def self.get
|
|||
{
|
|||
:user => User.current,
|
|||
:organization => Organization.current,
|
|||
:location => Location.current
|
|||
}
|
|||
end
|
|||
def self.set(user: nil, organization: nil, location: nil)
|
|||
User.current = user
|
|||
Organization.current = organization
|
|||
Location.current = location
|
|||
end
|
|||
end
|
|||
475cdc84 | Ohad Levy | # include this in the User model
|
|
module UserModel
|
|||
dc457681 | Joseph Mitchell Magen | extend ActiveSupport::Concern
|
|
475cdc84 | Ohad Levy | ||
dc457681 | Joseph Mitchell Magen | module ClassMethods
|
|
def current
|
|||
Thread.current[:user]
|
|||
end
|
|||
611f5bff | Amos Benari | ||
dc457681 | Joseph Mitchell Magen | def current=(o)
|
|
unless o.nil? || o.is_a?(self)
|
|||
raise(ArgumentError, "Unable to set current User, expected class '#{self}', got #{o.inspect}")
|
|||
475cdc84 | Ohad Levy | end
|
|
0ed2c98c | Ivan Necas | if o.is_a?(User)
|
|
7422a1f8 | Lukas Zapletal | user = o.login
|
|
type = o.admin? ? 'admin' : 'regular'
|
|||
if o.hidden?
|
|||
Rails.logger.debug("Current user set to #{user} (#{type})")
|
|||
else
|
|||
Rails.logger.info("Current user set to #{user} (#{type})")
|
|||
end
|
|||
0ed2c98c | Ivan Necas | end
|
|
7422a1f8 | Lukas Zapletal | ::Logging.mdc['user_login'] = o&.login
|
|
::Logging.mdc['user_admin'] = o&.admin? || false
|
|||
dc457681 | Joseph Mitchell Magen | Thread.current[:user] = o
|
|
end
|
|||
# Executes given block on behalf of a different user. Example:
|
|||
#
|
|||
# User.as :admin do
|
|||
# ...
|
|||
# end
|
|||
#
|
|||
# Use with care!
|
|||
#
|
|||
# @param [String] login to find from the database
|
|||
# @param [block] block to execute
|
|||
5f029ed6 | Daniel Lobato | def as(login)
|
|
dc457681 | Joseph Mitchell Magen | old_user = current
|
|
e07f9a12 | Dominic Cleal | self.current = User.unscoped.find_by_login(login)
|
|
raise ::Foreman::Exception.new(N_("Cannot find user %s when switching context"), login) unless self.current.present?
|
|||
dc457681 | Joseph Mitchell Magen | yield if block_given?
|
|
ensure
|
|||
self.current = old_user
|
|||
475cdc84 | Ohad Levy | end
|
|
e07f9a12 | Dominic Cleal | ||
def as_anonymous_admin(&block)
|
|||
as User::ANONYMOUS_ADMIN, &block
|
|||
end
|
|||
475cdc84 | Ohad Levy | end
|
|
end
|
|||
611f5bff | Amos Benari | ||
# include this in the Organization model object
|
|||
module OrganizationModel
|
|||
dc457681 | Joseph Mitchell Magen | extend ActiveSupport::Concern
|
|
611f5bff | Amos Benari | ||
dc457681 | Joseph Mitchell Magen | module ClassMethods
|
|
def current
|
|||
Thread.current[:organization]
|
|||
end
|
|||
611f5bff | Amos Benari | ||
dc457681 | Joseph Mitchell Magen | def current=(organization)
|
|
unless organization.nil? || organization.is_a?(self) || organization.is_a?(Array)
|
|||
raise(ArgumentError, "Unable to set current organization, expected class '#{self}', got #{organization.inspect}")
|
|||
611f5bff | Amos Benari | end
|
|
7422a1f8 | Lukas Zapletal | Rails.logger.debug "Current organization set to #{organization || 'none'}"
|
|
abbe2a04 | Lukas Zapletal | org_id = organization.try(:id)
|
|
::Logging.mdc['org_id'] = org_id if org_id
|
|||
dc457681 | Joseph Mitchell Magen | Thread.current[:organization] = organization
|
|
end
|
|||
# Executes given block in the scope of an org:
|
|||
#
|
|||
# Organization.as_org organization do
|
|||
# ...
|
|||
# end
|
|||
#
|
|||
# @param [org]
|
|||
# @param [block] block to execute
|
|||
5f029ed6 | Daniel Lobato | def as_org(org)
|
|
dc457681 | Joseph Mitchell Magen | old_org = current
|
|
self.current = org
|
|||
yield if block_given?
|
|||
ensure
|
|||
self.current = old_org
|
|||
611f5bff | Amos Benari | end
|
|
end
|
|||
end
|
|||
module LocationModel
|
|||
dc457681 | Joseph Mitchell Magen | extend ActiveSupport::Concern
|
|
611f5bff | Amos Benari | ||
dc457681 | Joseph Mitchell Magen | module ClassMethods
|
|
def current
|
|||
Thread.current[:location]
|
|||
end
|
|||
611f5bff | Amos Benari | ||
dc457681 | Joseph Mitchell Magen | def current=(location)
|
|
unless location.nil? || location.is_a?(self) || location.is_a?(Array)
|
|||
raise(ArgumentError, "Unable to set current location, expected class '#{self}'. got #{location.inspect}")
|
|||
611f5bff | Amos Benari | end
|
|
7422a1f8 | Lukas Zapletal | Rails.logger.debug "Current location set to #{location || 'none'}"
|
|
abbe2a04 | Lukas Zapletal | loc_id = location.try(:id)
|
|
::Logging.mdc['loc_id'] = loc_id if loc_id
|
|||
dc457681 | Joseph Mitchell Magen | Thread.current[:location] = location
|
|
end
|
|||
# Executes given block without the scope of a location:
|
|||
#
|
|||
# Location.as_location location do
|
|||
# ...
|
|||
# end
|
|||
#
|
|||
# @param [location]
|
|||
# @param [block] block to execute
|
|||
5f029ed6 | Daniel Lobato | def as_location(location)
|
|
dc457681 | Joseph Mitchell Magen | old_location = current
|
|
self.current = location
|
|||
yield if block_given?
|
|||
ensure
|
|||
self.current = old_location
|
|||
611f5bff | Amos Benari | end
|
|
end
|
|||
end
|
|||
475cdc84 | Ohad Levy | end
|
|
end
|