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.