Recently, I had to implement several changes to the authorization logic of a Rails application, but the process did not go as smoothly as I had anticipated.
The app is an internal project management tool that has an admin panel built using the Administrate gem. Initially, only the admin
role had access to that view, but as requirements changed, new user roles had to be able to use the admin panel, yet not be able to interact with all the resources. To make things more complicated, I couldn't rely on existing Pundit policies, since the scope of the resources allowed inside the admin panel was more limited.
I started by reading the documentation for Administrate and found out that you could define a policy_namespace
method inside the Admin::ProjectsController
. After creating the method and setting the namespace to be [:admin]
, Pundit was not loading the newly created /policies/admin/project_policy.rb
. It was still relying on the main /policies/project_policy.rb
.
After some debugging and looking for any errors on my part, I discovered through their issues that a quick workaround involves making the following modifications to Admin::ApplicationController
:
Remove the call to
include Administrate::Punditize
Include Pundit
Override Pundit's methods, adding the
:admin
namespace
Here's what I've ended up with:
# app/controllers/admin/application_controller.rb
module Admin
class ApplicationController < Administrate::ApplicationController
include Pundit
before_action :authenticate_admin
ALLOWED_ROLES = %w[admin editor].freeze
def authenticate_admin
return true if ALLOWED_ROLES.include? current_user.role
redirect_to root_path, alert: 'Unauthorized!'
end
def policy_scope(scope)
super([:admin, scope])
end
def authorize(record, query = nil)
super([:admin, record], query)
end
def scoped_resource
policy_scope super
end
def authorize_resource(resource)
authorize resource
end
def show_action?(action, resource)
Pundit.policy!(pundit_user, [:admin, resource]).send("#{action}?".to_sym)
end
end
end
And the corresponding policy:
# app/policies/admin/project_policy.rb
module Admin
class ProjectPolicy < ApplicationPolicy
def index?
user.admin?
end
def show?
index?
end
def create?
show?
end
def edit?
create?
end
def update?
edit?
end
def destroy?
update?
end
end
end
This is a quick workaround, and I will continue looking for an optimal solution, but for now it effectively gets the job done.
I hope this is helpful to anyone encountering the same issue.