Forum: Ruby on Rails How to selectively ignore some model validations?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
710eb9516d466362bc80a2f8d3c31cab?d=identicon&s=25 Chris Hanks (preciousbodilyfluids)
on 2009-04-22 10:56
Hi -

So, I have a user model, with a bunch of validations, one of which is
validates_acceptance_of :terms_of_service. I've decided to keep a
database column tracking this attribute, and it works just as intended
for signups (people have to check the box before they can register).

However, a user may also be created via an admin interface, which has an
entirely separate controller and view. When a user is created via this
interface, I'd like to keep most of the validations intact (ensuring
that the name and email are unique, that the password is confirmed,
etc.) but there are some validations that I'd like to skip and not be
bothered by (for example, the terms of service checkbox).

I'm not sure how to make this happen. I've played with the :if and
:unless options for the validation helpers, but as far as I can tell
they're only able to see the instance variables belonging to the user
I'm validating. It would be great if I could say validates_acceptance_of
:terms_of_service, :unless => { :controller => 'admin/users' } or
something similar, but that doesn't seem to be a possibility.

Does anyone have any ideas as to how I could make this work?

Thanks!
Chris
782b01570270ab10543bc25a258d7ea8?d=identicon&s=25 Dmitry Sokurenko (Guest)
on 2009-04-22 11:44
(Received via mailing list)
Probably the best way is to redesign the app, so validations will be
always required (eg introduce a Signup model associated with user).

But if you are looking for the simpler way then try.

# in user.rb
attr_accessor :signing_up
validates ..., :if => :signing_up

# in users_controller.rb
def create
 User.create(params[:user].merge(:signing_up => true))
end

Or if the you want to skip those validateions for all already created
users, then just use validates ..., :on => :update

Dmitry
710eb9516d466362bc80a2f8d3c31cab?d=identicon&s=25 Chris Hanks (preciousbodilyfluids)
on 2009-04-22 20:37
Hi -

Thanks a lot, I like the simpler way you wrote out.

My question is, though, is that method vulnerable to mass-assignment
attacks? I know that if it were attr_accessible, a user would be able to
pass in a value for :signing_up and avoid having their data validated,
but I don't know whether the same is true for attr_accessor.

Thanks again!
Chris


Dmitry Sokurenko wrote:
> Probably the best way is to redesign the app, so validations will be
> always required (eg introduce a Signup model associated with user).
>
> But if you are looking for the simpler way then try.
>
> # in user.rb
> attr_accessor :signing_up
> validates ..., :if => :signing_up
>
> # in users_controller.rb
> def create
>  User.create(params[:user].merge(:signing_up => true))
> end
>
> Or if the you want to skip those validateions for all already created
> users, then just use validates ..., :on => :update
>
> Dmitry
782b01570270ab10543bc25a258d7ea8?d=identicon&s=25 Dmitry Sokurenko (Guest)
on 2009-04-22 21:25
(Received via mailing list)
It doesn't matter in that case cause even if a user will try to hack
it, the merging of {:signing_up => true} will override hid value.

But to make it protected in all other parts of the app that use the
User model:
User
 attr_protected :signing_up

def create
 @user = User.new(params[:user])
 @user.signing_up = true
 @user.save
end

Dmitry
710eb9516d466362bc80a2f8d3c31cab?d=identicon&s=25 Chris Hanks (preciousbodilyfluids)
on 2009-04-22 21:41
I'm already using attr_accessible (which I gather is a best practice),
and I don't think it and attr_protected work together, do they?

Thanks again
782b01570270ab10543bc25a258d7ea8?d=identicon&s=25 Dmitry Sokurenko (Guest)
on 2009-04-22 21:53
(Received via mailing list)
No, I think they don't work together, so just don't list :signing_up
in the accesible attributes list.
280b78a61a968391b7e07e912be102a8?d=identicon&s=25 Robert Walker (robert4723)
on 2009-04-22 22:11
Dmitry Sokurenko wrote:
> No, I think they don't work together, so just don't list :signing_up
> in the accesible attributes list.

It is true that you cannot use both attr_accessible and attr_protected
in the same model. Rails will throw a runtime error if both are
specified on one model.
710eb9516d466362bc80a2f8d3c31cab?d=identicon&s=25 Chris Hanks (preciousbodilyfluids)
on 2009-04-22 22:16
Ok, I've done some more reading and I think that I have this down now.
Somebody tell me if I'm on the right/wrong path.

attr_accessible lists attributes that are open to mass-assignment. So,
for security reasons, we shouldn't allow anything in attr_accessible
that we wouldn't let the user define themselves.

Active Record automatically creates setter/getter methods for columns in
databases - since my users table has a "name" column, for example, I can
use @user.name in my models/views/controllers and it'll just work.

However, when I want to use a virtual attribute (something that isn't
persisted in the database but that I still want to manipulate in Rails,
like @user.signing_up), ActiveRecord can't do that for me, and I have to
make setter/getter methods for that myself. I can make those with
attr_accessor, but they won't be mass-assignable, and so they won't be
vulnerable to mass-assignment attacks.

Finally, since the whitelist approach to security is better than the
blacklist approach, attr_protected should just be ignored.

Do I have all that right?
782b01570270ab10543bc25a258d7ea8?d=identicon&s=25 Dmitry Sokurenko (Guest)
on 2009-04-23 00:08
(Received via mailing list)
1. Mass assignment doesn't care if the attributes are genreated by
ActiveRecord, defined using attr-accessor, or implemented explicitly.
It just doesn't allow some attributes to be mass assigned, so if
the :secret is protected then
  user.attributes = {:secret => ...}
  user.update_attributes(:secret => ...)
  User.create(:secret => ...)
won't work, but
  user.secret = ...
will work always for protected & for not-protected attributes.

attr_accesseble & attr_protected can be used interchangeably, just use
the one you like more, eg when User has 3 attributes: name, age &
salary, then:
  attr_accessible :name, :age
is the same as
  attr_protected :salary

In both case name & age will be accessible and salary will be
protected.
This topic is locked and can not be replied to.