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
--
Posted via http://www.ruby-forum.com/.
Dmitry Sokurenko
2009-Apr-22 09:43 UTC
Re: How to selectively ignore some model validations?
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
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-- Posted via http://www.ruby-forum.com/.
Dmitry Sokurenko
2009-Apr-22 19:24 UTC
Re: How to selectively ignore some model validations?
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
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 -- Posted via http://www.ruby-forum.com/.
Dmitry Sokurenko
2009-Apr-22 19:52 UTC
Re: How to selectively ignore some model validations?
No, I think they don''t work together, so just don''t list :signing_up in the accesible attributes list.
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. -- Posted via http://www.ruby-forum.com/.
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? -- Posted via http://www.ruby-forum.com/.
Dmitry Sokurenko
2009-Apr-22 22:07 UTC
Re: How to selectively ignore some model validations?
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.