Can't mass assign protected attributes

2019-02-23 08:57发布

My features file looks at this:

Given there are the following users:
    | email              | password | admin |
    | admin@ticketee.com | password | true  |

And my user model doesn't declare the admin attribute as attr_accessible to prevent mass assignment. Accordingly, I've made changes to the user_steps.rb file to tackle this.

Given /^there are the following users:$/ do |table|
  table.hashes.each do |attributes|
   unconfirmed = attributes.delete("unconfirmed") == "true"
   @user = User.create!(attributes)
   @user.update_attribute("admin", attributes["admin"] == "true")
   @user.confirm! unless unconfirmed
 end
end

Now this is supposed to work according to the book - Rails3 in action. I checked the code on their online repo as well. Running this with cucumber gives the following error:

Can't mass-assign protected attributes: admin (ActiveModel::MassAssignmentSecurity::Error)
  ./features/step_definitions/user_steps.rb:4:in `block (2 levels) in <top (required)>'
  ./features/step_definitions/user_steps.rb:2:in `each'
  ./features/step_definitions/user_steps.rb:2:in `/^there are the following users:$/'
  features/creating_projects.feature:7:in `Given there are the following users:'

Any help would be greatly appreciated. I really cant figure what's wrong here.

Thanks a lot!

5条回答
神经病院院长
2楼-- · 2019-02-23 09:04

I think the most easy way to do this is:

Given /^there are the following users:$/ do |table|
  table.hashes.each do |attributes|
    unconfirmed = attributes.delete("unconfirmed") == "true" # this will delete the attribute unconfirmed from first
    # scenario on the signing_in.feature but it's also make a variable unconfirmed true (2 in one action)

    admin = attributes.delete("admin") == "true"

    @user = User.create!(attributes)

    @user.update_attribute("admin", "true") if admin

    # with the user and password, it will create the user as admin.
    @user.confirm! unless unconfirmed  # this is work for the second scenario check rails 3 in action page 152.
  end
end

This way you'll delete the admin attribute from the attributes so you can create the new user; without the mass-assign attributes error. But for the "test" purpose we keep the admin variable to assign the user.admin attribute if the admin was true. The thing here is that you won't make the admin accessible on the User model to protect your site from a hack, but we're doing this to pass our test only. On your website you have to implement a secure way to update the user after it was created.

查看更多
爷的心禁止访问
3楼-- · 2019-02-23 09:16

Assuming this is based on "Rails 3 in Action", the sample code actually has the same problem. From the log:

WARNING: Can't mass-assign protected attributes: admin
(0.1ms)  SELECT 1 FROM "users" WHERE "users"."email" = 'sfcarroll2@gmail.com' LIMIT 1
User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."confirmation_token" =     'f9zDB57RDXsziBdGpmnW' LIMIT 1

However, in the config/environments/development.rb file, the mass_assignment_sanitizer is not set. By default a new rails 3.2 project will set this. If it is commented out, the code will execute. The default setting is:

config.active_record.mass_assignment_sanitizer = :strict

The sample code then sets the attribute using a private method set_admin. Is this a good coding practice? I'm not sure but it works.

It's a great book, by the way.

查看更多
爱情/是我丢掉的垃圾
4楼-- · 2019-02-23 09:19

In the user model add:

attr_accessible :admin

Update:

The admin attribute can be mass assigned and any hacker can set it easily by sending it with the parameters.

查看更多
狗以群分
5楼-- · 2019-02-23 09:27

I got this to work by changing the step definition:

   Given /^there are the following users:$/ do |table|
      table.hashes.each do |attributes|
      unconfirmed = attributes.delete("unconfirmed") == "true"
      admin = attributes.delete("admin") == "true"
      @user = User.create!(attributes)
      @user.admin = admin
      @user.confirm! unless unconfirmed
      end
    end
查看更多
狗以群分
6楼-- · 2019-02-23 09:27

Why not just use @user.admin = attributes["admin"] == "true"?

You will need to get rid of the admin value from the attributes hash - so the full code would be

admin = attributes.delete("admin") == "true"
...
@user = User.new(attributes)
@user.admin = admin
@user.save!

I changed User.create! to User.new + @user.save because you are setting an attribute but not saving the model after. If @user.confirm! saves the model you won't see any errors, but it's not good practice to rely on side effects from other methods like that. Better to be explicit.

查看更多
登录 后发表回答