Skip to content

Commit

Permalink
Merge pull request #441 from pablocrivella/allow-overriding-policy-cl…
Browse files Browse the repository at this point in the history
…ass-on-authorize-method

Allow overriding policy class on authorize method
  • Loading branch information
Linuus committed Jul 3, 2018
2 parents 79be5f3 + 2cd4cc4 commit 1f93b8b
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 6 deletions.
21 changes: 21 additions & 0 deletions README.md
Expand Up @@ -133,6 +133,18 @@ def publish
end
```

You can pass an argument to override the policy class if necessary. For example:

```ruby
def create
@publication = find_publication # assume this method returns any model that behaves like a publication
# @publication.class => Post
authorize @publication, policy_class: PublicationPolicy
@publication.publish!
redirect_to @publication
end
```

If you don't have an instance for the first argument to `authorize`, then you can pass
the class. For example:

Expand Down Expand Up @@ -271,6 +283,15 @@ def show
end
```

Like with the authorize method, you can also override the policy scope class:

``` ruby
def index
# publication_class => Post
@publications = policy_scope(publication_class, policy_scope_class: PublicationPolicy::Scope)
end
```

Just as with your policy, this will automatically infer that you want to use
the `PostPolicy::Scope` class, it will instantiate this class and call
`resolve` on the instance. In this case it is a shortcut for doing:
Expand Down
15 changes: 9 additions & 6 deletions lib/pundit.rb
Expand Up @@ -61,10 +61,11 @@ class << self
# @param user [Object] the user that initiated the action
# @param record [Object] the object we're checking permissions of
# @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`)
# @param policy_class [Class] the policy class we want to force use of
# @raise [NotAuthorizedError] if the given query method returned false
# @return [Object] Always returns the passed object record
def authorize(user, record, query)
policy = policy!(user, record)
def authorize(user, record, query, policy_class: nil)
policy = policy_class ? policy_class.new(user, record) : policy!(user, record)

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

Expand Down Expand Up @@ -195,14 +196,15 @@ def verify_policy_scoped
# @param record [Object] the object we're checking permissions of
# @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`).
# If omitted then this defaults to the Rails controller action name.
# @param policy_class [Class] the policy class we want to force use of
# @raise [NotAuthorizedError] if the given query method returned false
# @return [Object] Always returns the passed object record
def authorize(record, query = nil)
def authorize(record, query = nil, policy_class: nil)
query ||= "#{action_name}?"

@_pundit_policy_authorized = true

policy = policy(record)
policy = policy_class ? policy_class.new(pundit_user, record) : policy(record)

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

Expand All @@ -229,10 +231,11 @@ def skip_policy_scope
#
# @see https://github.com/elabs/pundit#scopes
# @param scope [Object] the object we're retrieving the policy scope for
# @param policy_scope_class [Class] the policy scope class we want to force use of
# @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
def policy_scope(scope)
def policy_scope(scope, policy_scope_class: nil)
@_pundit_policy_scoped = true
pundit_policy_scope(scope)
policy_scope_class ? policy_scope_class.new(pundit_user, scope).resolve : pundit_policy_scope(scope)
end

# Retrieves the policy for the given record.
Expand Down
12 changes: 12 additions & 0 deletions spec/pundit_spec.rb
Expand Up @@ -22,6 +22,10 @@
expect(Pundit.authorize(user, post, :update?)).to be_truthy
end

it "can be given a different policy class" do
expect(Pundit.authorize(user, post, :create?, policy_class: PublicationPolicy)).to be_truthy
end

it "works with anonymous class policies" do
expect(Pundit.authorize(user, article_tag, :show?)).to be_truthy
expect { Pundit.authorize(user, article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
Expand Down Expand Up @@ -405,6 +409,10 @@
expect { controller.authorize(post, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
end

it "can be given a different policy class" do
expect(controller.authorize(post, :create?, policy_class: PublicationPolicy)).to be_truthy
end

it "works with anonymous class policies" do
expect(controller.authorize(article_tag, :show?)).to be_truthy
expect { controller.authorize(article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
Expand Down Expand Up @@ -481,6 +489,10 @@
expect(controller.policy_scope(Post)).to eq :published
end

it "allows policy scope class to be overriden" do
expect(controller.policy_scope(Post, policy_scope_class: PublicationPolicy::Scope)).to eq :published
end

it "throws an exception if the given policy can't be found" do
expect { controller.policy_scope(Article) }.to raise_error(Pundit::NotDefinedError)
end
Expand Down
12 changes: 12 additions & 0 deletions spec/spec_helper.rb
Expand Up @@ -110,6 +110,18 @@ def resolve
end
end

class PublicationPolicy < Struct.new(:user, :publication)
class Scope < Struct.new(:user, :scope)
def resolve
scope.published
end
end

def create?
true
end
end

class Comment
extend ActiveModel::Naming
end
Expand Down

0 comments on commit 1f93b8b

Please sign in to comment.