ActiveAdmin:Trace missing pundit authorization

From FVue
Jump to: navigation, search

Problem

Using ActiveAdmin & Pundit, the app throws an ActiveAdmin::AccessDenied, i.e. flash message "You are not authorized to perform this action.", but you have no clue where, what permission is missing.

Environment

  • activeadmin-2.14.0
  • pundit-2.3.1
  • rails-7.0.7.2

Solution

Be more verbose on ActiveAdmin::AccessDenied errors.

# config/initializers/active_admin.rb
config.on_unauthorized_access = :admin_user_not_authorized
# app/controllers/application_controller.rb
# @params exception, with added attributes: subject & action
def admin_user_not_authorized(exception)
  pa = ActiveAdmin::PunditAdapter.new(exception.subject, current_admin_user)
  policy = pa.retrieve_policy(exception.subject)
  policy_name = policy.class.to_s.underscore
 
  error_key = if policy.respond_to?(:policy_error_key) && policy.policy_error_key
                policy.policy_error_message
              else
                "#{exception.action}?"
              end
  msg = 'Not allowed'
  msg_admin = " (#{policy.class}.#{error_key}, app/policies/#{policy_name})"
  Rails.logger.error "#{msg}#{msg_admin}" # Always log
  flash[:error] = Rails.env.development? || current_user&.admin ? "#{msg}#{msg_admin}" : msg
  redirect_to(request.referrer || admin_root_path)
end

and while you're at it, be more verbose about regular Pundit::NotAuthorizedError as well:

# app/controllers/application_controller.rb
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
 
# @params exception, with added attributes: subject & action
def user_not_authorized(exception)
  policy = exception.policy
  policy_name = policy.class.to_s.underscore
 
  error_key = if policy.respond_to?(:policy_error_key) && policy.policy_error_key
                policy.policy_error_message
              else
                exception.query
              end
  msg = t('unauthorized', scope: 'pundit', default: :default).capitalize
  msg_admin = " (#{policy_name}.#{error_key})"
  Rails.logger.error "#{msg}#{msg_admin}" # Always log
  flash[:error] = Rails.env.development? || current_user&.admin ? "#{msg}#{msg_admin}" : msg
  redirect_to(request.referrer || root_path)
end