Pundit: Pass class as argument when checking a policy

Created on 21 Sep 2017  路  8Comments  路  Source: varvet/pundit

Suppose I have an Order model and the following policy:

class OrderPolicy < ApplicationPolicy
  def create?
    true
  end

  def revalidate?
    true
  end  
end

But I wanna have only CRUD methods in my policies, so I create a new policy:

class RevalidationOrderPolicy < ApplicationPolicy
  def create?
    true
  end

However, I couldn't find a way to pass an order to my new policy (reading the code, I think it's not supported right now), I have tried things like:

policy(:revalidation_order).create?

But the method create? receives :revalidation_order as record (I need to receive an order instance). Just to confirm, is there a way to do it? If not, what do you think about these api below?

policy(order, klass: RevalidationOrder).create?
# or
policy(order, klass: [:revalidation_order]).create?

It would be a pleasure to implement.

Most helpful comment

@glaucocustodio Cool, in that case, I used a very flexible patch:

```lang=ruby
# ApplicationController
class_attribute :pundit_policy_class

def self.set_pundit_policy_class(klass)
self.pundit_policy_class = klass
end

def authorize(record, query: nil, policy_class: nil)
query ||= params[:action].to_s + "?"

@_pundit_policy_authorized = true

if policy_class
  policy = policy_class.new(pundit_user, record)
elsif self.pundit_policy_class
  policy = self.pundit_policy_class.new(pundit_user, record)
else
  policy = policy(record)
end

unless policy.public_send(query)
  raise NotAuthorizedError, query: query, record: record, policy: policy
end

record

end

This allows you to specify policy on a per case basis:

authorize(@book, policy_class:FooPolicy)

This also allows me to set policy on a controller wide scope:

class FooController
set_pundit_policy_class FooFooPolicy
```

All 8 comments

+1

Has somebody found a workaround yet? I hoped that I would be able to manually specify the class analogous to the scope example from the documentation:

@posts = PostPolicy::Scope.new(current_user, Post).resolve

But I couldn't come up with something working.

It's a bit confusing that in your title you said "Pass class as argument" but in the description you passed :revalidation_order. When you say "Class" do you mean a Policy class?

I think the easiest is to override policy in that controller (assuming all actions in that controller uses the same policy).

def policy(record)
    policies[record] ||= RevalidationOrderPolicy.new(pundit_user, record)
end

But if you need to use different policies in one controller let me know.

I am already doing this override of policy in the controller today.

But would be nice if we could have such api: policy(order, klass: RevalidationOrder).create? (or any other with the same purpose)

@glaucocustodio Cool, in that case, I used a very flexible patch:

```lang=ruby
# ApplicationController
class_attribute :pundit_policy_class

def self.set_pundit_policy_class(klass)
self.pundit_policy_class = klass
end

def authorize(record, query: nil, policy_class: nil)
query ||= params[:action].to_s + "?"

@_pundit_policy_authorized = true

if policy_class
  policy = policy_class.new(pundit_user, record)
elsif self.pundit_policy_class
  policy = self.pundit_policy_class.new(pundit_user, record)
else
  policy = policy(record)
end

unless policy.public_send(query)
  raise NotAuthorizedError, query: query, record: record, policy: policy
end

record

end

This allows you to specify policy on a per case basis:

authorize(@book, policy_class:FooPolicy)

This also allows me to set policy on a controller wide scope:

class FooController
set_pundit_policy_class FooFooPolicy
```

I think this could be related to this: https://github.com/varvet/pundit/pull/441

@pablocrivella: It certainly is. Thanks for pointing out.

It sure is related to #441

The easiest, and perhaps the best, way to do it right now is to create new RevalidationOrder object and authorize based on that. If you don't need any other functionality it's super easy by wrapping the Order object using SimpleDelegator.

I'll close this as we already track this in #441

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mackshkatz picture mackshkatz  路  4Comments

epinault picture epinault  路  6Comments

richjdsmith picture richjdsmith  路  4Comments

guzart picture guzart  路  4Comments

chevinbrown picture chevinbrown  路  4Comments