2

我正在阅读 Michael Hartl 的 Rails 教程。我已经来到第 9 章,练习 1。它要求您添加一个测试以验证该类的admin属性User是否不可访问。这是注释掉无关部分的 User 类:

class User < ActiveRecord::Base
  attr_accessible :name, :email, :password, :password_confirmation
  attr_protected :admin

  # before_save methods
  # validations
  # private methods
end

这是我用来验证该admin属性不可访问的测试。

describe User do
  before do
    @user = User.new( 
                     name: "Example User",
                     email: "user@example.com",
                     password: "foobar123",
                     password_confirmation: "foobar123")
  end

  subject { @user }

  describe "accessible attributes" do
    it "should not allow access to admin" do
      expect do
        @user.admin = true 
      end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
    end
  end
end

测试失败。它表示没有引发错误,尽管该admin属性受到保护。我怎样才能让测试通过?

4

6 回答 6

5

来自 Ruby 文档:

批量分配安全性提供了一个用于保护属性免受最终用户分配的接口。

http://api.rubyonrails.org/classes/ActiveModel/MassAssignmentSecurity/ClassMethods.html

试试这个代码

describe "accesible attributes" do
  it "should not allow access to admin" do
    expect do
      User.new(admin: true) 
    end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
  end
end
于 2012-05-26T20:14:47.500 回答
3

正如 Rails 文档所声称的 attr_protected

在此宏中命名的属性不受大规模赋值的影响,例如 new(attributes)、update_attributes(attributes) 或 attributes=(attributes)。

因此,您可以手动更改字段。'attr_protected' 仅与批量分配有关。

于 2012-05-25T05:16:48.000 回答
1

这仅适用于批量分配,例如从表单提交中设置字段。尝试这样的事情:

@user.update_attrtibutes(:admin => true)
@user.admin.should be_false
于 2012-05-25T05:16:45.777 回答
1

[剧透警告:如果您尝试自己解决 Hartl 书中的练习,我很确定我即将给出答案。尽管已被接受的答案是有趣的信息,但我不相信这是 Hartl 的想法,因为这需要本书未涵盖的知识,并且也不会将其与通过网络操作或使用测试的更新具体联系起来他提供。]

我想你可能会认为这个练习比实际困难得多,如果我做对了的话。首先,你误解了提示:

提示:您的第一步应该是将 admin 添加到 user_params 中允许的参数列表中。

它没有说要更改其在类中的 attr 声明。它说要修改帮助函数 user_params。所以我将它添加到 users_controller.rb 的列表中:

def user_params
  params.require(:user).permit(:name, :email, :password,
                             :password_confirmation, :admin)
end

接下来,我将代码清单 9.48 中的代码复制到 spec/requests/user_pages_spec.rb 中指定的位置:

require 'spec_helper'

describe "User pages" do
  .
  .
  .
  describe "edit" do
    .
    .
    .
    describe "forbidden attributes" do
      let(:params) do
        { user: { admin: true, password: user.password,
                  password_confirmation: user.password } }
      end
      before do
        sign_in user, no_capybara: true
        patch user_path(user), params
      end
      specify { expect(user.reload).not_to be_admin }
    end
  end
end

然后测试失败,表明可以传递一个管理员参数,从而将普通用户更改为管理员,这不是您想要允许的:

$ rspec spec
.....................[edited out dots].................................F

Failures:

  1) User pages edit forbidden attributes 
     Failure/Error: specify { expect(user.reload).not_to be_admin }
       expected admin? to return false, got true
     # ./spec/requests/user_pages_spec.rb:180:in `block (4 levels) in <top (required)>'

Finished in 4.15 seconds
91 examples, 1 failure

Failed examples:

rspec ./spec/requests/user_pages_spec.rb:180 # User pages edit forbidden attributes 

然后,为了避免通过 Web 操作传入 admin 值,我只是从可接受的 user_params 列表中删除了 :admin,撤消了第一个更改:

def user_params
  params.require(:user).permit(:name, :email, :password,
                             :password_confirmation)
end

现在尝试使用新的管理员值修补用户失败......并且测试成功,验证“该admin属性不可通过网络编辑”。

    $ rspec spec
...........................................................................................

Finished in 4.2 seconds
91 examples, 0 failures
于 2014-07-22T00:02:10.580 回答
1

@agaved。这个答案可能来晚了,你可能已经有了答案,但我想回答你的问题,它可能对其他人有帮助。

了解update_attributes与直接分配有何不同的最佳方法
@user.admin = true是尝试在您的控制台中执行此操作。如果您遵循 Hartl 的教程,请尝试以下操作:

@user = User.first
@user.admin?
=> true
@user.admin = false
=> false

直接分配设法将用户属性 admin 的值从 更改为 ,truefalse不会引发批量分配错误。这是因为当您使用不可访问的属性调用update_attributes或创建新用户时会引发批量分配错误。User.new换句话说,当用户尝试更新(attribute_update)或创建User.new(admin: true)具有她无法访问的属性的新用户时,Rails 会引发大量分配错误。在上述情况下,直接分配不使用用户控制器的创建或更新方法。

它们是非常相似的代码片段,因为您可以在上述情况下直接在 IRB 中使用直接分配来强制更改 admin 属性,@user.save!(validate: false)但正如我上面所说,这不使用用户控制器的 create 或 update 方法,因此,它不会抛出错误。

我希望这有帮助,对我有帮助。

于 2013-07-10T03:27:15.860 回答
0

按照提示,我首先在 app/models/user.rb 中添加了:adminattr_accessible红色开头的内容。

然后我添加了测试:

describe "admin attribute" do
    it  "should not be accessible" do
        expect do
            @user.update_attributes(:admin => true)
        end.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
    end
end

符合规格并获得红色。

从 user.rb 中删除:admin我得到一个绿色。到目前为止,一切都很好。

令我困惑的是为什么我应该使用 sintax:

@user.update_attributes(:admin => true)

而不是@user.admin = true(我检查过,在这种情况下它不起作用)。

于 2012-11-16T14:22:24.047 回答