theforeman/foreman

View on GitHub
app/controllers/concerns/application_shared.rb

Summary

Maintainability
B
6 hrs
Test Coverage
module ApplicationShared
  extend ActiveSupport::Concern

  include Foreman::Controller::MigrationChecker
  include Foreman::Controller::Authentication
  include Foreman::Controller::Session
  include Foreman::Controller::TopbarSweeper
  include Foreman::Controller::Timezone
  include Foreman::ThreadSession::Cleaner
  include FindCommon

  def current_permission
    [action_permission, controller_permission].join('_')
  end

  def set_current_user(user)
    super
    set_taxonomy
    user.present?
  end

  def load_settings
    Foreman.settings.load_values
  end

  def set_taxonomy
    TopbarSweeper.expire_cache
    user = User.current
    return if user.nil?

    ['location', 'organization'].each do |taxonomy|
      available = user.send("my_#{taxonomy.pluralize}")
      determined_taxonomy = nil

      if api_request? # API request
        if params.has_key?("#{taxonomy}_id") # admin and non-admin who specified context explicitly
          if params["#{taxonomy}_id"].blank? # the key is present and explicitly set to nil which indicates "any" context
            determined_taxonomy = nil
          else
            determined_taxonomy = scope_by_resource_id(available, params["#{taxonomy}_id"]).first

            # in case user asked for taxonomy that does not exist or is not accessible, we reply with 404
            if determined_taxonomy.nil?
              not_found _("%{taxonomy} with id %{id} not found") % { :taxonomy => taxonomy.capitalize, :id => params["#{taxonomy}_id"] }
              return false
            end
          end
        elsif session.has_key?("#{taxonomy}_id") # session with taxonomy explicitly set to id or nil (any context)
          if session["#{taxonomy}_id"].present?
            determined_taxonomy = find_session_taxonomy(taxonomy, user) # specific id
          else
            determined_taxonomy = nil # explicitly set any context
          end
        else
          determined_taxonomy = nil
        end
      else # UI request
        if session["#{taxonomy}_id"].present?
          determined_taxonomy = find_session_taxonomy(taxonomy, user)
        elsif !user.admin? && available.count == 1
          determined_taxonomy = available.first
        end
      end

      set_current_taxonomy(taxonomy, determined_taxonomy)
      store_taxonomy(taxonomy, determined_taxonomy) unless api_request?
    end
  end

  # determined_taxonomy can be nil, which means any context
  def store_taxonomy(taxonomy, determined_taxonomy)
    # session can't store nil, so we use empty string to represent any context
    session["#{taxonomy}_id"] = determined_taxonomy.try(:id) || ''
  end

  def set_current_taxonomy(taxonomy, determined_taxonomy)
    taxonomy.classify.constantize.send 'current=', determined_taxonomy
  end

  def store_default_taxonomy(user, taxonomy)
    default_taxonomy = find_default_taxonomy(user, taxonomy)
    set_current_taxonomy(taxonomy, default_taxonomy)
    store_taxonomy(taxonomy, default_taxonomy)
  end

  # we want to be explicit to keep this readable
  def find_default_taxonomy(user, taxonomy)
    default_taxonomy = user.send "default_#{taxonomy}"
    available = user.send("my_#{taxonomy.pluralize}")

    if default_taxonomy.present? && available.include?(default_taxonomy)
      default_taxonomy
    elsif available.count == 1 && !user.admin?
      available.first
    # rubocop:disable Style/EmptyElse
    else
      # no available default taxonomy and user is either admin or user with more taxonomies, nil represents "Any Context"
      nil
    end
    # rubocop:enable Style/EmptyElse
  end

  # This method adds a scope by resource id, taking into account possibility of parametrization or friendly find
  def scope_by_resource_id(base_scope, resource_id)
    # If resource id is nil or empty string, return empty scope
    return base_scope.none if resource_id.blank?

    # First try to find the resource using friendly_find
    # the id could be a regular friendly id - e.g. 'name',
    # or it could be a resource with a numeric name, e.g. '123'
    # - in that case we want to return the friendly scope.
    # if there is no matching resources, fall back to numeric find
    if resource_id.is_a?(String) && base_scope.klass.respond_to?(:friendly)
      field = base_scope.klass.friendly_id_config.query_field
      friendly_scope = base_scope.where(field => resource_id)

      return friendly_scope if friendly_scope.any?
    end

    # If the id is an integer or parameterized string, scope by it
    return base_scope.where(id: resource_id.to_i) if resource_id.integer? || (resource_id.respond_to?(:start_with?) && resource_id.start_with?(/\d+-/))

    # The parameter doesn't match any supported format, return empty scope
    base_scope.none
  end

  def find_session_taxonomy(taxonomy, user)
    available = user.send("my_#{taxonomy.pluralize}")
    determined_taxonomy = available.where(:id => session["#{taxonomy}_id"]).first
    # warn user if taxonomy stored in session does not exist and delete it from session (probably taxonomy has been deleted meanwhile)
    if determined_taxonomy.nil?
      if api_request?
        not_found _("%{taxonomy} stored in session with id %{id} not found") % { :taxonomy => taxonomy.capitalize, :id => params["#{taxonomy}_id"] }
        return false
      else
        warning _("%s you had selected as your context has been deleted") % taxonomy.capitalize
      end
      session.delete("#{taxonomy}_id")
      determined_taxonomy = find_default_taxonomy(user, taxonomy)
    end
    determined_taxonomy
  end

  def find_selected_columns
    @selected_columns = User.current.table_preferences.find_by(name: controller_name)&.columns&.sort
  end
end