diff --git a/lib/cancan/ability.rb b/lib/cancan/ability.rb index ebf4414..2b4a7f8 100644 --- a/lib/cancan/ability.rb +++ b/lib/cancan/ability.rb @@ -221,6 +221,10 @@ module CanCan attributes end + def has_block?(action, subject) + relevant_can_definitions(action, subject).any?(&:only_block?) + end + private def unauthorized_message_keys(action, subject) diff --git a/lib/cancan/controller_resource.rb b/lib/cancan/controller_resource.rb index d093219..84145e3 100644 --- a/lib/cancan/controller_resource.rb +++ b/lib/cancan/controller_resource.rb @@ -28,6 +28,8 @@ module CanCan def load_resource if !resource_instance && (parent? || member_action?) @controller.instance_variable_set("@#{instance_name}", load_resource_instance) + elsif load_collection? + @controller.instance_variable_set("@#{instance_name.pluralize}", load_collection) end end @@ -49,6 +51,16 @@ module CanCan end end + def load_collection? + !parent? && collection_actions.include?(@params[:action].to_sym) && + resource_base.respond_to?(:accessible_by) && + !@controller.current_ability.has_block?(authorization_action, resource_class) + end + + def load_collection + resource_base.accessible_by(@controller.current_ability) + end + def build_resource resource = resource_base.send(@options[:singleton] ? "build_#{name}" : "new") initial_attributes.each do |name, value| diff --git a/spec/cancan/ability_spec.rb b/spec/cancan/ability_spec.rb index 42bc618..0b0fe5f 100644 --- a/spec/cancan/ability_spec.rb +++ b/spec/cancan/ability_spec.rb @@ -282,6 +282,15 @@ describe CanCan::Ability do lambda { @ability.authorize!(:read, :foo) }.should_not raise_error end + it "should know when block is used in conditions" do + @ability.can :read, :foo + @ability.should_not have_block(:read, :foo) + @ability.can :read, :foo do |foo| + false + end + @ability.should have_block(:read, :foo) + end + it "should raise access denied exception with default message if not specified" do begin @ability.authorize! :read, :foo diff --git a/spec/cancan/controller_resource_spec.rb b/spec/cancan/controller_resource_spec.rb index 9c9f23b..3ec37dd 100644 --- a/spec/cancan/controller_resource_spec.rb +++ b/spec/cancan/controller_resource_spec.rb @@ -4,8 +4,9 @@ describe CanCan::ControllerResource do before(:each) do @params = HashWithIndifferentAccess.new(:controller => "projects") @controller = Object.new # simple stub for now + @ability = Ability.new(nil) stub(@controller).params { @params } - stub(@controller).current_ability.stub!.attributes_for { {} } + stub(@controller).current_ability { @ability } end it "should load the resource into an instance variable if params[:id] is specified" do @@ -49,7 +50,7 @@ describe CanCan::ControllerResource do it "should build a new resource with attributes from current ability" do @params.merge!(:action => "new") - stub(@controller).current_ability.stub!.attributes_for(:new, Project) { {:name => "from conditions"} } + @ability.can(:create, Project, :name => "from conditions") resource = CanCan::ControllerResource.new(@controller) resource.load_resource @controller.instance_variable_get(:@project).name.should == "from conditions" @@ -57,17 +58,37 @@ describe CanCan::ControllerResource do it "should override initial attributes with params" do @params.merge!(:action => "new", :project => {:name => "from params"}) - stub(@controller).current_ability.stub!.attributes_for(:new, Project) { {:name => "foobar"} } + @ability.can(:create, Project, :name => "from conditions") resource = CanCan::ControllerResource.new(@controller) resource.load_resource @controller.instance_variable_get(:@project).name.should == "from params" end - it "should not build a resource when on index action" do + it "should build a collection when on index action when class responds to accessible_by" do + stub(Project).accessible_by(@ability) { :found_projects } @params[:action] = "index" resource = CanCan::ControllerResource.new(@controller) resource.load_resource @controller.instance_variable_get(:@project).should be_nil + @controller.instance_variable_get(:@projects).should == :found_projects + end + + it "should not build a collection when on index action when class does not respond to accessible_by" do + @params[:action] = "index" + resource = CanCan::ControllerResource.new(@controller) + resource.load_resource + @controller.instance_variable_get(:@project).should be_nil + @controller.instance_variable_defined?(:@projects).should be_false + end + + it "should not use accessible_by when defining abilities through a block" do + stub(Project).accessible_by(@ability) { :found_projects } + @params[:action] = "index" + @ability.can(:read, Project) { |p| false } + resource = CanCan::ControllerResource.new(@controller) + resource.load_resource + @controller.instance_variable_get(:@project).should be_nil + @controller.instance_variable_defined?(:@projects).should be_false end it "should perform authorization using controller action and loaded model" do