From ec87230e9219bd6e5e7ac8c39d1318e490946930 Mon Sep 17 00:00:00 2001 From: Scott Jacobsen Date: Fri, 7 Nov 2014 10:13:38 -0700 Subject: [PATCH] Make Pundit#authorize a module method. There are cases where service objects could make use of the authorize method. Make it a module method so it can be called without mixing in Pundit. Have the Pundit#authorize instance method delegate to Pundit::authorize module method. Be sure the instance method continues to cache the policy if found. --- lib/pundit.rb | 23 +++++++++++++---------- spec/pundit_spec.rb | 44 ++++++++++++++++++++++++++++++++++++-------- spec/spec_helper.rb | 6 ++++++ 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/lib/pundit.rb b/lib/pundit.rb index 96fe4841..4cec6eca 100644 --- a/lib/pundit.rb +++ b/lib/pundit.rb @@ -17,6 +17,18 @@ class NotDefinedError < StandardError; end extend ActiveSupport::Concern class << self + def authorize(user, record, query, policy = nil) + policy ||= policy!(user, record) + unless policy.public_send(query) + error = NotAuthorizedError.new("not allowed to #{query} this #{record}") + error.query, error.record, error.policy = query, record, policy + + raise error + end + + true + end + def policy_scope(user, scope) policy_scope = PolicyFinder.new(scope).scope policy_scope.new(user, scope).resolve if policy_scope @@ -65,16 +77,7 @@ def verify_policy_scoped def authorize(record, query=nil) query ||= params[:action].to_s + "?" @_pundit_policy_authorized = true - - policy = policy(record) - unless policy.public_send(query) - error = NotAuthorizedError.new("not allowed to #{query} this #{record}") - error.query, error.record, error.policy = query, record, policy - - raise error - end - - true + Pundit.authorize(pundit_user, record, query, policy(record)) end def policy_scope(scope) diff --git a/spec/pundit_spec.rb b/spec/pundit_spec.rb index 4dba32b2..5f591d99 100644 --- a/spec/pundit_spec.rb +++ b/spec/pundit_spec.rb @@ -9,6 +9,32 @@ let(:artificial_blog) { ArtificialBlog.new } let(:article_tag) { ArticleTag.new } + describe ".authorize" do + it "infers the policy and authorizes based on it" do + expect(Pundit.authorize(user, post, :update?)).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) + end + + it "raises an error with a query and action" do + expect { Pundit.authorize(user, post, :destroy?) }.to raise_error(Pundit::NotAuthorizedError) do |error| + expect(error.query).to eq :destroy? + expect(error.record).to eq post + expect(error.policy).to eq Pundit.policy(user, post) + end + end + + it "uses the specified optional policy instance" do + denier = DenierPolicy.new(nil, nil) + expect { Pundit.authorize(user, post, :update?, denier) }.to raise_error(Pundit::NotAuthorizedError) do |error| + expect(error.policy).to eq denier + end + end + end + describe ".policy_scope" do it "returns an instantiated policy scope given a plain model class" do expect(Pundit.policy_scope(user, Post)).to eq :published @@ -166,7 +192,7 @@ end describe "#authorize" do - it "infers the policy name and authorized based on it" do + it "infers the policy name and authorizes based on it" do expect(controller.authorize(post)).to be_truthy end @@ -180,16 +206,18 @@ expect { controller.authorize(article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError) end - it "raises an error when the permission check fails" do + it "throws an exception when the permission check fails" do expect { controller.authorize(Post.new) }.to raise_error(Pundit::NotAuthorizedError) end - it "raises an error with a query and action" do - expect { controller.authorize(post, :destroy?) }.to raise_error do |error| - expect(error.query).to eq :destroy? - expect(error.record).to eq post - expect(error.policy).to eq controller.policy(post) - end + it "throws an exception when a policy cannot be found" do + expect { controller.authorize(Article) }.to raise_error(Pundit::NotDefinedError) + end + + it "caches the policy" do + expect(controller.policies[post]).to be_nil + controller.authorize(post) + expect(controller.policies[post]).not_to be_nil end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e86278a4..d0dfcf43 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -85,6 +85,12 @@ def destroy? class DashboardPolicy < Struct.new(:user, :dashboard); end +class DenierPolicy < Struct.new(:user, :record) + def update? + false + end +end + class Controller include Pundit