Skip to content
This repository has been archived by the owner on Dec 12, 2021. It is now read-only.

Setting for strict class access #968

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .rbenv-version

This file was deleted.

30 changes: 28 additions & 2 deletions lib/cancan/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module CanCan
# end
#
module Ability

# Check if the user has permission to perform a given action on an object.
#
# can? :destroy, @project
Expand Down Expand Up @@ -122,7 +123,7 @@ def cannot?(*args)
# end
#
def can(action = nil, subject = nil, conditions = nil, &block)
rules << Rule.new(true, action, subject, conditions, block)
rules << Rule.new(true, action, subject, conditions, @strict_class_access, block)
end

# Defines an ability which cannot be done. Accepts the same arguments as "can".
Expand All @@ -138,7 +139,32 @@ def can(action = nil, subject = nil, conditions = nil, &block)
# end
#
def cannot(action = nil, subject = nil, conditions = nil, &block)
rules << Rule.new(false, action, subject, conditions, block)
rules << Rule.new(false, action, subject, conditions, @strict_class_access, block)
end

# Set strict class access.
#
# If you define a block for testing objects of a certain class, the default behaviour
# is that "can?" returns true for all methods you test on that class:
#
# ability.can :destroy, :all { |object| false }
#
# ability.can? :destroy, {} => false # block called, and returns false
# ability.can? :destroy, Hash # => true # careful - block not called, true returned by default
#
# If you enable strick class access, you need to specifically permit class methods:
#
# ability.strict_class_access = true # enable strict class access
# ability.can :destroy, :all { |object| false }
#
# ability.can? :destroy, {} => false # block is called, and returns false
# ability.can? :destroy, Hash # => false # block not called, false returned since strict class access is on
#
# ability.can :destroy, Hash # specifically allow destroying Hashes
# ability.can? :destroy, Hash # => true # true returned, since we specifically allowed it
#
def strict_class_access
@strict_class_access = true
end

# Alias one or more actions into another one.
Expand Down
7 changes: 4 additions & 3 deletions lib/cancan/rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ class Rule # :nodoc:
# value. True for "can" and false for "cannot". The next two arguments are the action
# and subject respectively (such as :read, @project). The third argument is a hash
# of conditions and the last one is the block passed to the "can" call.
def initialize(base_behavior, action, subject, conditions, block)
def initialize(base_behavior, action, subject, conditions, strict_class_access=false, block=nil)
raise Error, "You are not able to supply a block with a hash of conditions in #{action} #{subject} ability. Use either one." if conditions.kind_of?(Hash) && !block.nil?
@match_all = action.nil? && subject.nil?
@base_behavior = base_behavior
@actions = [action].flatten
@subjects = [subject].flatten
@conditions = conditions || {}
@strict_class_access = strict_class_access
@block = block
end

Expand All @@ -30,8 +31,8 @@ def relevant?(action, subject)
def matches_conditions?(action, subject, extra_args)
if @match_all
call_block_with_all(action, subject, extra_args)
elsif @block && !subject_class?(subject)
@block.call(subject, *extra_args)
elsif @block
subject_class?(subject) ? @strict_class_access!=true : @block.call(subject, *extra_args)
elsif @conditions.kind_of?(Hash) && subject.class == Hash
nested_subject_matches_conditions?(subject)
elsif @conditions.kind_of?(Hash) && !subject_class?(subject)
Expand Down
23 changes: 23 additions & 0 deletions spec/cancan/ability_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,29 @@
@block_called.should be_false
end

it "should not allow methods by default when strict class access was enabled" do
@block_called = false
@ability.strict_class_access
@ability.can :destroy, :all do |object|
@block_called = true
false
end
@ability.can?(:destroy, Hash).should be_false
@block_called.should be_false
end

it "should allow granted method when strict class access was enabled" do
@block_called = false
@ability.strict_class_access
@ability.can :destroy, :all do |object|
@block_called = true
false
end
@ability.can :destroy, Hash
@ability.can?(:destroy, Hash).should be_true
@block_called.should be_false
end

it "should pass only object for global manage actions" do
@ability.can :manage, String do |object|
object.should == "foo"
Expand Down