为什么我的CanCan Ability课程过于宽松?

时间:2011-05-24 18:05:56

标签: ruby-on-rails rspec cancan

我(最后)将CanCan / Ability连接到我的应用程序,我开始编写RSpec测试。但他们失败了 - 我的能力似乎过于宽容,我不明白为什么。

首先是Ability类。目的是非管理员用户只能自己管理。特别是,他们无法看到其他用户:

class Ability
include CanCan::Ability

  def initialize(user)
    user ||= User.new           # create guest user if needed
    if (user.has_role?(:admin))
      can(:manage, :all)
    else
      can(:manage, User, :id => user.id)
    end
  end
end

RSpec测试:

require 'spec_helper'
require 'cancan/matchers'

describe Ability do
  before(:each) do
    @user = User.create
  end

  describe 'guest user' do
    before(:each) do
      @guest = nil
      @ability = Ability.new(@guest)
    end

    it "should_not list other users" do
      @ability.should_not be_able_to(:read, User)
    end

    it "should_not show other user" do
      @ability.should_not be_able_to(:read, @user)
    end

    it "should_not create other user" do
      @ability.should_not be_able_to(:create, User)
    end

    it "should_not update other user" do
      @ability.should_not be_able_to(:update, @user)
    end

    it "should_not destroy other user" do
      @ability.should_not be_able_to(:destroy, @user)
    end
  end
end

所有这五项测试都失败了。我已经阅读了Ryan's documentation的部分,他说:

  

重要:如果是块或哈希   条件存在他们将被忽略   在检查课程时,它会   返回true。

......但最多只能解释五次失败中的两次。很明显,我错过了一些基本的东西。

3 个答案:

答案 0 :(得分:1)

我希望这可行:

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new           # create guest user if needed
    if (user.has_role?(:admin))
      can(:manage, :all)
    elsif user.persisted?
      can(:manage, User, :id => user.id)
    end
  end
end

我不确定如果你传递:id => nil的行为是什么,这是客户案例中发生的事情,但无论如何,如果你不希望访客访问列表查看,您根本不应该为该用户致电can :manage, User

一般来说,我发现分配user ||= User.new会使这种能力更难以推理。

答案 1 :(得分:0)

嘿,显然这应该有效,但是一些重构可以帮助你找到问题:

require 'spec_helper'
require 'cancan/matchers'

describe Ability do
  before(:each) { @user = User.create }

  describe 'guest user' do
    before(:each) { @ability = Ability.new(nil) }
    subject { @ability } # take advantage of subject

    it "should not be an admin user" do
      @user.should_not be_admin
      @user.should be_guest
    end

    it "should_not show other user" do
      should_not be_able_to(:read, @user)
    end

    it "should_not create other user" do
      should_not be_able_to(:create, User)
    end

    it "should_not update other user" do
      should_not be_able_to(:update, @user)
    end

    it "should_not destroy other user" do
      should_not be_able_to(:destroy, @user)
    end
  end
end

请注意,我也删除了此示例@ability.should_not be_able_to(:read, User)

希望它对你有所帮助。

答案 2 :(得分:0)

我有回答自己问题的坏习惯,但我给@jpemberthy和@Austin Taylor提供道具,指出我正确的方向。首先(这是化妆品),我将其添加到我的用户模型中:

class User
  ...
  def self.create_guest
    self.new
  end

  def guest?
    uninitialized?
  end
end

并相应地清理了我的能力模型:

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.create_guest
    if (user.admin?)
      <admin abilities here>
    elsif (user.guest?)
      <guest abilities here>
    else
      <regular user abilities here>
    end
  end
end

但真正的解决方法是在我的RSpec测试中。由于用户对电子邮件和密码字段进行了验证,因此我的原始代码为:

before(:each) do
  @user = User.create
end

失败了,因此创建了一个未初始化的@user。由于:id字段为nil,因此Ability子句:

can(:manage, User, :id => user.id)

与访客用户一起成功,因为nil == nil(如果这是有道理的)。添加必要的字段以满足用户验证(几乎)一切正常。

道德:就像@jpemberthy在他的代码中建议的那样,总是包含一个测试,以确保您的用户对象具有他们应该拥有的权限! (我还有另外一个关于CanCan的问题,希望比这个更少的斩首,出现在你附近的StackOverflow话题中......)