zammad/zammad

View on GitHub
app/controllers/application_controller/has_user.rb

Summary

Maintainability
A
0 mins
Test Coverage
# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/

module ApplicationController::HasUser
  extend ActiveSupport::Concern

  included do
    before_action :set_user, :session_update
  end

  private

  def current_user
    current_user_on_behalf || current_user_real
  end

  # Finds the User with the ID stored in the session with the key
  # :current_user_id This is a common way to handle user login in
  # a Rails application; logging in sets the session value and
  # logging out removes it.
  def current_user_real
    @_current_user ||= User.lookup(id: session[:user_id]) # rubocop:disable Naming/MemoizedInstanceVariableName
  end

  def request_header_from
    @request_header_from ||= begin
      if request.headers['X-On-Behalf-Of'].present?
        ActiveSupport::Deprecation.warn("Header 'X-On-Behalf-Of' is deprecated. Please use header 'From' instead.")
      end

      request.headers['From'] || request.headers['X-On-Behalf-Of']
    end
  end

  # Finds the user based on the id, login or email which is given
  # in the headers. If it is found then all api activities are done
  # with the behalf of user. With this functionality it is possible
  # to do changes with a user which is different from the admin user.
  # E.g. create a ticket as a customer user based on a user with admin rights.
  def current_user_on_behalf
    return if request_header_from.blank? # require header
    return @_user_on_behalf if @_user_on_behalf         # return memoized user
    return if !current_user_real                        # require session user
    if !SessionsPolicy.new(current_user_real, Sessions).impersonate?
      raise Exceptions::Forbidden, __("Current user has no permission to use 'From'/'X-On-Behalf-Of'!")
    end

    @_user_on_behalf = find_on_behalf_user request_header_from.to_s.downcase.strip

    # no behalf of user found
    if !@_user_on_behalf
      raise Exceptions::Forbidden, "No such user '#{request_header_from}'"
    end

    @_user_on_behalf
  end

  def current_user_set(user, auth_type = 'session')
    session[:user_id] = user.id
    @_auth_type = auth_type
    @_current_user = user
    set_user
  end

  # Sets the current user into a named Thread location so that it can be accessed
  # by models and observers
  def set_user
    UserInfo.current_user_id = current_user&.id || 1
  end

  # update session updated_at
  def session_update
    # sleep 0.6

    session[:ping] = Time.zone.now.iso8601

    # check if remote ip needs to be updated
    if session[:user_id]
      if !session[:remote_ip] || session[:remote_ip] != request.remote_ip # rubocop:disable Style/SoleNestedConditional
        session[:remote_ip] = request.remote_ip
        session[:geo]       = Service::GeoIp.location(request.remote_ip)
      end
    end

    # fill user agent
    return if session[:user_agent]

    session[:user_agent] = request.env['HTTP_USER_AGENT']
  end

  # find on behalf user by ID, login or email
  def find_on_behalf_user(identifier)
    # ActiveRecord casts string beginning with a numeric characters
    # to numeric characters by dropping textual bits altogether
    # thus 123@example.com returns user with ID 123
    if identifier.match?(%r{^\d+$})
      user = User.find_by(id: identifier)
      return user if user
    end

    # find user for execution based on the header
    User.where('login = :param OR email = :param', param: identifier).first
  end
end