Search code examples
ruby-on-railsrubypundit

Pundit: Authorize specific resource to differently named controller


Let's say I am using the Pundit gem for authorization. I have the following controller:

class BlogsController < ApplicationController
  before_action :check_authorization
  ...

  private
  def check_authorization
    authorize :blog
  end
end

Pundit looks at the argument passed in, :blog and thus infers there is a BlogPolicy class. It then looks for the corresponding action name with a question mark, such as: BlogPolicy#index?.

If I want to look up the authorization on a specific resource I would just do this for that check_authorization method:

  def check_authorization
    authorize @blog
  end

Again, no problem. Pundit looks at the class of the resource, Blog, and then looks for the BlogPolicy class.

  • Example: The app could call the BlogPolicy#show? method and pass in that @blog resource.

Here is the issue: what do I do when I want to authorize on a specific controller name AND authorize on a specific resource, but that resource does not sync up with the controller name?

Example:

class SomeOtherBlogsController < ApplicationController
  before_action :check_authorization
  ...

  private
  def check_authorization
    authorize :some_other_blog, @blog
  end
end

The above code doesn't work, but hopefully it shows what I am trying to do. Lets pretend this is happening on SomeOtherBlogsController#show action.

  • I am trying to have pundit find the SomeOtherBlogPolicy#show? method,
  • Within that Policy method, I want to check the authorization access of the @blog resource as well.

Hopefully the issue is apparent. Since the resource class does not sync up with the controller name, It seems I am not able to do the above. So If I have the same resource used in various different controllers, I'm out of luck. I wouldn't be able to check authorization of the resource in those various controller contexts. Is this impossible to do with Pundit?

Update:

In the controller, I have also attempted to directly call the Policy method like so:

SomeOtherBlogPolicy.new(current_user, @blog).show?

However, calling that raised a Pundit::AuthorizationNotPerformedError. So it appears that more happens in that authorize method than just returning true or false.


Solution

  • You can manually specify the resource class for a model by:

    class Blog
      def self.policy_class
        SomeOtherBlogPolicy
      end
    end
    

    Unfortunately its not possible to specify the policy class from the controller when calling authorize...

    This was true when I originally wrote the answer. v2.00 added a policy_class option to authorize:

    authorize(@blog, policy_class: SomeOtherBlogPolicy)
    

    So the workaround in my original answer is no longer needed.