2

I have the following code in my User model:

attr_protected :email

I'm trying to create a new user object, but I get a mass assignment protected error with the following code.

user = User.new(
    :first_name => signup.first_name,
    :last_name => signup.last_name,
    :email => signup.email,
    :birthday => signup.birthday,
    :encrypted_password => signup.encrypted_password,
    :salt => signup.salt
  )

Does anyone know how I can work around the attr_protected to get this code to work and assign a value to email?

Thank you.

iwasrobbed
  • 46,496
  • 21
  • 150
  • 195
Brian
  • 5,951
  • 14
  • 53
  • 77

4 Answers4

11
user = User.new(
  :first_name => signup.first_name,
  :last_name => signup.last_name,
  :birthday => signup.birthday,
  :encrypted_password => signup.encrypted_password,
  :salt => signup.salt
)
user.email = signup.email
yfeldblum
  • 65,165
  • 12
  • 129
  • 169
2

I literally just wrote a gem tonight to deal with this exact issue. I'm planning on adding it to RubyGems.org later this week after I give a presentation on it. Feel free to checkout the code in the mean time. http://github.com/beerlington/sudo_attributes

Using the gem, your code would change to:

user = User.sudo_new(
  :first_name => signup.first_name,
  :last_name => signup.last_name,
  :email => signup.email,
  :birthday => signup.birthday,
  :encrypted_password => signup.encrypted_password,
  :salt => signup.salt
)

You could also use sudo_create() if you want to save the instance

Peter Brown
  • 50,956
  • 18
  • 113
  • 146
  • DIGG THIS! Should be somewhere inside the core! – William Yeung Nov 04 '10 at 07:26
  • Sorry... nice try... but I realize something more legitimate to do so, use self.send(:attributes=, hash, false) – William Yeung Nov 04 '10 at 07:51
  • @goodwill - If you look at the source for sudo_attributes, this is all it's doing when updating an instance. However, there are three issues with using `self.send(...)`. 1) There's no way to change attributes and save an instance at the same time. Normally you would use `update_attributes`, but it won't work with protected attributes. 2) There's no way to instantiate an object with protected attributes in one line. `sudo_new` and `sudo_create` solve this. 3) self.send(...) isn't very explicit. In a large code base, `sudo_update_attributes` will let developers know exactly what your intent is. – Peter Brown Nov 04 '10 at 11:48
  • I kinda agree the method name is nice and easy to read, but the risk I felt here is I realize the code kinda go one step inside the AR code (not really using the attributes assignment function directly) which I personally prefer if I have to write these wrappers I would use the send(:attr...) inside for better compatibility guarantee. – William Yeung Nov 05 '10 at 04:33
  • Doesn't this defeat the purpose of protecting from mass assignment? attr_protected is there to prevent mass assignment. Why use attr_protected if you want to do mass assignment? – Gautham Jan 12 '12 at 09:01
  • @Gogu great question. I see this as defeating the purpose of attr_protected in the same sense that sudo on a unix system defeats the purpose of not logging in as root. It's there when you need it and should only be used when you know what you're doing. In my application, there are admin sections that are guaranteed to only be accessible by "root" admin users. In this case, we are confident that using sudo is not putting application data at risk. Sure we could have an admin user who is malicious, but it's a risk we are willing to take. Also, the Rails core now includes similar functionality. – Peter Brown Jan 12 '12 at 12:56
1

update_attributes now allows you to override the protection if you know the hash to be safe, say for internal use where the fields you're setting are not coming from user-controllable hash.

user = User.new({
  first_name: signup.first_name,
  last_name: signup.last_name,
  email: signup.email,
  birthday: signup.birthday,
  encrypted_password: signup.encrypted_password,
  salt: signup.salt
}, {without_protection: true})

You may also want to consider roles:

User.new(params, as: :admin)
Steven Soroka
  • 19,404
  • 4
  • 52
  • 40
0

The core of the protection is on self.attributes=, which if you use self.send you can trigger a special hidden parameter at the end called guard_protected_attributes as false.

Example:

self.send(:attributes=, hash, false)

This would skip the protection feature completely. It doesn't work on new, but you can simply create the object first then call the same method and save, not too painful I guess.

William Yeung
  • 10,368
  • 9
  • 36
  • 42