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

发布于 2024-11-09 04:05:02 字数 1449 浏览 0 评论 0原文

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

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

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 的文档的部分,他说:

重要提示:如果一个块或散列 条件存在将被忽略 当检查一个类时,它会 返回真。

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

I'm (finally) wiring CanCan / Ability into my app, and I've started by writing the RSpec tests. But they're failing — my Abilities appear to be overly permissive, and I don't understand why.

First, the Ability class. The intention is that non-admin users can manage only themselves. In particular, they cannot look at other users:

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

The RSpec tests:

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

All five of these tests fail. I've read the part of Ryan's documentation where he says:

Important: If a block or hash of
conditions exist they will be ignored
when checking on a class, and it will
return true.

... but at most, that would only explain two of the five failures. So clearly I'm missing something fundamental.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

水染的天色ゝ 2024-11-16 04:05:02

我希望这能起作用:

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 会使该功能更难以推理。

I would expect this to work:

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

I'm not sure what the behavior is defined to be if you pass :id => nil, which is what happens in the guest case, but at any rate, if you don't want the guest to access the list view, you shouldn't call can :manage, User for that user at all.

In general, I find that assigning user ||= User.new to make the ability harder to reason about.

顾挽 2024-11-16 04:05:02

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

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)

希望对您有帮助。

Hey, apparently this should work, but some refactoring would help you to find the issue:

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

Note that also I removed this example @ability.should_not be_able_to(:read, User).

Hope it helps you.

吐个泡泡 2024-11-16 04:05:02

我有自己回答问题的坏习惯,但我感谢@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 主题中......)

I've got this bad habit of answering my own questions, but I give props to @jpemberthy and @Austin Taylor for pointing me in the right direction. First (and this is cosmetic), I added this to my User model:

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

  def guest?
    uninitialized?
  end
end

and cleaned up my Abilities model accordingly:

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

But the real fix was in my RSpec tests. Since User has validations on email and password fields, my original code of:

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

was failing, thus creating an uninitialized @user. Since the :id field was nil, the Ability clause:

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

was succeeding with a guest user because nil == nil (if that makes sense). Adding the required fields to satisfy the User validations made (almost) everything work.

Moral: just as @jpemberthy suggested in his code, always include a test to make sure your user objects have the privileges that they are supposed to! (I still have another question regarding CanCan, hopefully less boneheaded than this one, appearing in a StackOverflow topic near you...)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文