adding check_authorization and skip_authorization controller class methods to ensure authorization is triggered (thanks justinko) - closes #135

This commit is contained in:
Ryan Bates 2010-09-03 14:38:55 -07:00
parent 7c5243321f
commit 1af6c6f395
4 changed files with 71 additions and 27 deletions

View File

@ -151,6 +151,20 @@ module CanCan
def authorize_resource(*args)
ControllerResource.add_before_filter(self, :authorize_resource, *args)
end
def skip_authorization(*args)
self.before_filter(*args) do |controller|
controller.instance_variable_set(:@_authorized, true)
end
end
def check_authorization(*args)
self.after_filter(*args) do |controller|
unless controller.instance_variable_defined?(:@_authorized)
raise AuthorizationNotPerformed, "This action does not authorize the user. Add authorize! or authorize_resource to the controller."
end
end
end
end
def self.included(base)
@ -186,6 +200,7 @@ module CanCan
# See the load_and_authorize_resource method to automatically add the authorize! behavior
# to the default RESTful actions.
def authorize!(*args)
@_authorized = true
current_ability.authorize!(*args)
end

View File

@ -5,6 +5,9 @@ module CanCan
# Raised when removed code is called, an alternative solution is provided in message.
class ImplementationRemoved < Error; end
# Raised when using check_authorization without calling authorized!
class AuthorizationNotPerformed < Error; end
# This error is raised when a user isn't allowed to access a given controller action.
# This usually happens within a call to ControllerAdditions#authorize! but can be
# raised manually.

View File

@ -265,6 +265,34 @@ describe CanCan::Ability do
@ability.attributes_for(:new, Range).should == {:foo => "foo", :bar => 123, :baz => "baz"}
end
it "should raise access denied exception if ability us unauthorized to perform a certain action" do
begin
@ability.authorize! :read, :foo, 1, 2, 3, :message => "Access denied!"
rescue CanCan::AccessDenied => e
e.message.should == "Access denied!"
e.action.should == :read
e.subject.should == :foo
else
fail "Expected CanCan::AccessDenied exception to be raised"
end
end
it "should not raise access denied exception if ability is authorized to perform an action" do
@ability.can :read, :foo
lambda { @ability.authorize!(:read, :foo) }.should_not raise_error
end
it "should raise access denied exception with default message if not specified" do
begin
@ability.authorize! :read, :foo
rescue CanCan::AccessDenied => e
e.default_message = "Access denied!"
e.message.should == "Access denied!"
else
fail "Expected CanCan::AccessDenied exception to be raised"
end
end
describe "unauthorized message" do
after(:each) do
I18n.backend = nil

View File

@ -14,33 +14,10 @@ describe CanCan::ControllerAdditions do
lambda { @controller.unauthorized! }.should raise_error(CanCan::ImplementationRemoved)
end
it "should raise access denied exception if ability us unauthorized to perform a certain action" do
# TODO this should probably be moved into Ability spec
begin
@controller.authorize! :read, :foo, 1, 2, 3, :message => "Access denied!"
rescue CanCan::AccessDenied => e
e.message.should == "Access denied!"
e.action.should == :read
e.subject.should == :foo
else
fail "Expected CanCan::AccessDenied exception to be raised"
end
end
it "should not raise access denied exception if ability is authorized to perform an action" do
@controller.current_ability.can :read, :foo
lambda { @controller.authorize!(:read, :foo) }.should_not raise_error
end
it "should raise access denied exception with default message if not specified" do
begin
@controller.authorize! :read, :foo
rescue CanCan::AccessDenied => e
e.default_message = "Access denied!"
e.message.should == "Access denied!"
else
fail "Expected CanCan::AccessDenied exception to be raised"
end
it "authorize! should assign @_authorized instance variable and pass args to current ability" do
mock(@controller.current_ability).authorize!(:foo, :bar)
@controller.authorize!(:foo, :bar)
@controller.instance_variable_get(:@_authorized).should be_true
end
it "should have a current_ability method which generates an ability for the current user" do
@ -76,4 +53,25 @@ describe CanCan::ControllerAdditions do
mock(@controller_class).before_filter(:only => [:show, :index]) { |options, block| block.call(@controller) }
@controller_class.load_resource :foo => :bar, :only => [:show, :index]
end
it "skip_authorization should set up a before filter which sets @_authorized to true" do
mock(@controller_class).before_filter(:filter_options) { |options, block| block.call(@controller) }
@controller_class.skip_authorization(:filter_options)
@controller.instance_variable_get(:@_authorized).should be_true
end
it "check_authorization should trigger AuthorizationNotPerformed in after filter" do
mock(@controller_class).after_filter(:some_options) { |options, block| block.call(@controller) }
lambda {
@controller_class.check_authorization(:some_options)
}.should raise_error(CanCan::AuthorizationNotPerformed)
end
it "check_authorization should not raise error when @_authorized is set" do
@controller.instance_variable_set(:@_authorized, true)
mock(@controller_class).after_filter(:some_options) { |options, block| block.call(@controller) }
lambda {
@controller_class.check_authorization(:some_options)
}.should_not raise_error(CanCan::AuthorizationNotPerformed)
end
end