passing a hash to can? will check permissions on association, this is done automatically in authorize_resource - closes #121
This commit is contained in:
parent
737f8acff5
commit
c88cb8f459
|
@ -21,6 +21,7 @@ module CanCan
|
||||||
|
|
||||||
# Matches both the subject and action, not necessarily the conditions
|
# Matches both the subject and action, not necessarily the conditions
|
||||||
def relevant?(action, subject)
|
def relevant?(action, subject)
|
||||||
|
subject = subject.values.first if subject.kind_of? Hash
|
||||||
@match_all || (matches_action?(action) && matches_subject?(subject))
|
@match_all || (matches_action?(action) && matches_subject?(subject))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,9 +29,11 @@ module CanCan
|
||||||
def matches_conditions?(action, subject, extra_args)
|
def matches_conditions?(action, subject, extra_args)
|
||||||
if @match_all
|
if @match_all
|
||||||
call_block_with_all(action, subject, extra_args)
|
call_block_with_all(action, subject, extra_args)
|
||||||
elsif @block && subject.class != Class
|
elsif @block && !subject_class?(subject)
|
||||||
@block.call(subject, *extra_args)
|
@block.call(subject, *extra_args)
|
||||||
elsif @conditions.kind_of?(Hash) && subject.class != Class
|
elsif @conditions.kind_of?(Hash) && subject.kind_of?(Hash)
|
||||||
|
nested_subject_matches_conditions?(subject)
|
||||||
|
elsif @conditions.kind_of?(Hash) && !subject_class?(subject)
|
||||||
matches_conditions_hash?(subject)
|
matches_conditions_hash?(subject)
|
||||||
else
|
else
|
||||||
@base_behavior
|
@base_behavior
|
||||||
|
@ -66,6 +69,10 @@ module CanCan
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def subject_class?(subject)
|
||||||
|
(subject.kind_of?(Hash) ? subject.values.first : subject).class == Class
|
||||||
|
end
|
||||||
|
|
||||||
def matches_action?(action)
|
def matches_action?(action)
|
||||||
@expanded_actions.include?(:manage) || @expanded_actions.include?(action)
|
@expanded_actions.include?(:manage) || @expanded_actions.include?(action)
|
||||||
end
|
end
|
||||||
|
@ -95,6 +102,11 @@ module CanCan
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def nested_subject_matches_conditions?(subject_hash)
|
||||||
|
parent, child = subject_hash.shift
|
||||||
|
matches_conditions_hash?(parent, @conditions[parent.class.name.downcase.to_sym])
|
||||||
|
end
|
||||||
|
|
||||||
def call_block_with_all(action, subject, extra_args)
|
def call_block_with_all(action, subject, extra_args)
|
||||||
if subject.class == Class
|
if subject.class == Class
|
||||||
@block.call(action, subject, nil, *extra_args)
|
@block.call(action, subject, nil, *extra_args)
|
||||||
|
|
|
@ -32,7 +32,7 @@ module CanCan
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorize_resource
|
def authorize_resource
|
||||||
@controller.authorize!(authorization_action, resource_instance || resource_class)
|
@controller.authorize!(authorization_action, resource_instance || resource_class_with_parent)
|
||||||
end
|
end
|
||||||
|
|
||||||
def parent?
|
def parent?
|
||||||
|
@ -86,6 +86,10 @@ module CanCan
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def resource_class_with_parent
|
||||||
|
parent_resource ? {parent_resource => resource_class} : resource_class
|
||||||
|
end
|
||||||
|
|
||||||
def resource_instance
|
def resource_instance
|
||||||
@controller.instance_variable_get("@#{instance_name}")
|
@controller.instance_variable_get("@#{instance_name}")
|
||||||
end
|
end
|
||||||
|
@ -94,15 +98,15 @@ module CanCan
|
||||||
# If the :through option is passed it will go through an association on that instance.
|
# If the :through option is passed it will go through an association on that instance.
|
||||||
# If the :singleton option is passed it won't use the association because it needs to be handled later.
|
# If the :singleton option is passed it won't use the association because it needs to be handled later.
|
||||||
def resource_base
|
def resource_base
|
||||||
if through_resource
|
if parent_resource
|
||||||
@options[:singleton] ? through_resource : through_resource.send(name.to_s.pluralize)
|
@options[:singleton] ? parent_resource : parent_resource.send(name.to_s.pluralize)
|
||||||
else
|
else
|
||||||
resource_class
|
resource_class
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# The object to load this resource through.
|
# The object to load this resource through.
|
||||||
def through_resource
|
def parent_resource
|
||||||
@options[:through] && [@options[:through]].flatten.map { |i| @controller.instance_variable_get("@#{i}") }.compact.first
|
@options[:through] && [@options[:through]].flatten.map { |i| @controller.instance_variable_get("@#{i}") }.compact.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -57,11 +57,11 @@ describe CanCan::Ability do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should pass only object for global manage actions" do
|
it "should pass only object for global manage actions" do
|
||||||
@ability.can :manage, Array do |object|
|
@ability.can :manage, String do |object|
|
||||||
object.should == [1, 2]
|
object.should == "foo"
|
||||||
@block_called = true
|
@block_called = true
|
||||||
end
|
end
|
||||||
@ability.can?(:stuff, [1, 2]).should
|
@ability.can?(:stuff, "foo").should
|
||||||
@block_called.should be_true
|
@block_called.should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -130,9 +130,9 @@ describe CanCan::Ability do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should be able to specify multiple classes and match any" do
|
it "should be able to specify multiple classes and match any" do
|
||||||
@ability.can :update, [String, Array]
|
@ability.can :update, [String, Range]
|
||||||
@ability.can?(:update, "foo").should be_true
|
@ability.can?(:update, "foo").should be_true
|
||||||
@ability.can?(:update, []).should be_true
|
@ability.can?(:update, 1..3).should be_true
|
||||||
@ability.can?(:update, 123).should be_false
|
@ability.can?(:update, 123).should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -210,44 +210,50 @@ describe CanCan::Ability do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should use conditions as third parameter and determine abilities from it" do
|
it "should use conditions as third parameter and determine abilities from it" do
|
||||||
@ability.can :read, Array, :first => 1, :last => 3
|
@ability.can :read, Range, :begin => 1, :end => 3
|
||||||
@ability.can?(:read, [1, 2, 3]).should be_true
|
@ability.can?(:read, 1..3).should be_true
|
||||||
@ability.can?(:read, [1, 2, 3, 4]).should be_false
|
@ability.can?(:read, 1..4).should be_false
|
||||||
@ability.can?(:read, Array).should be_true
|
@ability.can?(:read, Range).should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should allow an array of options in conditions hash" do
|
it "should allow an array of options in conditions hash" do
|
||||||
@ability.can :read, Array, :first => [1, 3, 5]
|
@ability.can :read, Range, :begin => [1, 3, 5]
|
||||||
@ability.can?(:read, [1, 2, 3]).should be_true
|
@ability.can?(:read, 1..3).should be_true
|
||||||
@ability.can?(:read, [2, 3]).should be_false
|
@ability.can?(:read, 2..4).should be_false
|
||||||
@ability.can?(:read, [3, 4]).should be_true
|
@ability.can?(:read, 3..5).should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should allow a range of options in conditions hash" do
|
it "should allow a range of options in conditions hash" do
|
||||||
@ability.can :read, Array, :first => 1..3
|
@ability.can :read, Range, :begin => 1..3
|
||||||
@ability.can?(:read, [1, 2, 3]).should be_true
|
@ability.can?(:read, 1..10).should be_true
|
||||||
@ability.can?(:read, [3, 4]).should be_true
|
@ability.can?(:read, 3..30).should be_true
|
||||||
@ability.can?(:read, [4, 5]).should be_false
|
@ability.can?(:read, 4..40).should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should allow nested hashes in conditions hash" do
|
it "should allow nested hashes in conditions hash" do
|
||||||
@ability.can :read, Array, :first => { :length => 5 }
|
@ability.can :read, Range, :begin => { :to_i => 5 }
|
||||||
@ability.can?(:read, ["foo", "bar"]).should be_false
|
@ability.can?(:read, 5..7).should be_true
|
||||||
@ability.can?(:read, ["test1", "foo"]).should be_true
|
@ability.can?(:read, 6..8).should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should allow nested hash of arrays and match any element" do
|
it "should match any element passed in to nesting if it's an array (for has_many associations)" do
|
||||||
@ability.can :read, Array, :first => { :to_i => 3 }
|
@ability.can :read, Range, :to_a => { :to_i => 3 }
|
||||||
@ability.can?(:read, [[1, 2, 3]]).should be_true
|
@ability.can?(:read, 1..5).should be_true
|
||||||
@ability.can?(:read, [[4, 5, 6]]).should be_false
|
@ability.can?(:read, 4..6).should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should not stop at cannot definition when comparing class" do
|
it "should not stop at cannot definition when comparing class" do
|
||||||
@ability.can :read, Array
|
@ability.can :read, Range
|
||||||
@ability.cannot :read, Array, :first => 1
|
@ability.cannot :read, Range, :begin => 1
|
||||||
@ability.can?(:read, [2, 3, 5]).should be_true
|
@ability.can?(:read, 2..5).should be_true
|
||||||
@ability.can?(:read, [1, 3, 5]).should be_false
|
@ability.can?(:read, 1..5).should be_false
|
||||||
@ability.can?(:read, Array).should be_true
|
@ability.can?(:read, Range).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "passing a hash of subjects should check permissions through association" do
|
||||||
|
@ability.can :read, Range, :string => {:length => 3}
|
||||||
|
@ability.can?(:read, "foo" => Range).should be_true
|
||||||
|
@ability.can?(:read, "foobar" => Range).should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "unauthorized message" do
|
describe "unauthorized message" do
|
||||||
|
|
|
@ -161,6 +161,15 @@ describe CanCan::ControllerResource do
|
||||||
@controller.instance_variable_get(:@ability).should == :some_ability
|
@controller.instance_variable_get(:@ability).should == :some_ability
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should authorize nested resource through parent association on index action" do
|
||||||
|
@params.merge!(:action => "index")
|
||||||
|
person = Object.new
|
||||||
|
@controller.instance_variable_set(:@person, person)
|
||||||
|
stub(@controller).authorize!(:index, person => Ability) { raise CanCan::AccessDenied }
|
||||||
|
resource = CanCan::ControllerResource.new(@controller, :through => :person)
|
||||||
|
lambda { resource.authorize_resource }.should raise_error(CanCan::AccessDenied)
|
||||||
|
end
|
||||||
|
|
||||||
it "should load through first matching if multiple are given" do
|
it "should load through first matching if multiple are given" do
|
||||||
@params.merge!(:action => "show", :id => 123)
|
@params.merge!(:action => "show", :id => 123)
|
||||||
person = Object.new
|
person = Object.new
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
--color
|
--color
|
||||||
|
--backtrace
|
||||||
|
|
Loading…
Reference in New Issue
Block a user