【问题标题】:Make Rails Class Attribute Inaccessible - Rails Tutorial Chapter 9, Exercise 1使 Rails 类属性不可访问 - Rails 教程第 9 章,练习 1
【发布时间】:2012-05-25 05:06:22
【问题描述】:

我正在阅读 Michael Hartl 的 Rails 教程。我来Chapter 9, Exercise 1。它要求您添加一个测试来验证 User 类的 admin 属性是否不可访问。这是注释掉不相关部分的 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 属性受到保护。我怎样才能让测试通过?

【问题讨论】:

    标签: ruby-on-rails railstutorial.org


    【解决方案1】:

    来自 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
    

    【讨论】:

      【解决方案2】:

      Rails 文档声称 attr_protected

      在此宏中命名的属性不会被大量赋值,例如 new(attributes)、update_attributes(attributes) 或 attributes=(attributes)。

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

      【讨论】:

        【解决方案3】:

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

        @user.update_attrtibutes(:admin => true)
        @user.admin.should be_false
        

        【讨论】:

          【解决方案4】:

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

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

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

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

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

          希望对您有所帮助,this 帮助了我。

          【讨论】:

            【解决方案5】:

            [剧透警告:如果您尝试自己解决 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 操作传递管理员值,我只是从可接受的 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
            

            【讨论】:

              【解决方案6】:

              按照提示,我首先在 app/models/user.rb 中将:admin 添加到attr_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(我检查过,在这种情况下它不起作用)。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-05-02
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多