Search code examples
ruby-on-railsrubyauthorizationpundit

pundit authorisation multiple-inheritence (scopes, modules)?


I'm using Pundit for authorisation, and want to share logic between the policy classes. So far I've been using plain ruby modules and include, but haven't found a good solution for pundit's Scope classes.

For example granting an admin access to records with a particular tag is quite separate from restricting them to only published (not draft or discontinued/deleted) records.

For example

class PagePolicy < ApplicationPolicy
  # restrict access to current pages
  include PublishedOnlySharedPolicy
  # enable section editors to update their tagged content
  include TagsAclSharedPolicy
end

module TagsAclSharedPolicy
  def update?
    admin.in_tag_acl?(record) || super
  end

  def show?
    admin.in_tag_acl?(record) || super
  end

  def scope
    # ... can't do this?
  end
end

These modules work fine for the ordinary ACL methods create? update? etc, but scopes are a puzzle since they're class definitions instead of methods.

I'm expecting scope composition to let me take the base class's scope and restrict it by adding where statements as usual, or expand it by using super, pluck, and building a new scope using union.

Is there a clean way to do this without metaprogramming? Or does the library need changing to support dynamic scopes instead of using class definitions?

Why does Pundit use class definitions for scopes anyway?


Solution

  • There's an answer at https://github.com/elabs/pundit/issues/310 if anyone is curious.

    Basically add a whitelist ids method to application policy's scope, then override that with normal ruby in each policy's scope class, with any shared scope code going in its own module and being included in the scope.

    May be able to metaprogram something even dryer, but that works.