Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails validating that password_confirmation is present when password is also present or has been changed

I've got the following User model:

class User < ActiveRecord::Base
  # Users table has the necessary password_digest field
  has_secure_password
  attr_accessible :login_name, :password, :password_confirmation

  validates :login_name, :presence=>true, :uniqueness=>true

  # I run this validation on :create so that user 
  # can edit login_name without having to enter password      
  validates :password,:presence=>true,:length=>{:minimum=>6},:on=>:create

  # this should only run if the password has changed
  validates :password_confirmation, 
            :presence=>true, :if => :password_digest_changed?
end

These validations don't quite do what I was hoping they would. It's possible to do the following:

# rails console
u = User.new :login_name=>"stephen"
u.valid? 
# => false
u.errors 
# => :password_digest=>["can't be blank"], 
# => :password=>["can't be blank", "please enter at least 6 characters"]}

# so far so good, let's give it a password and a valid confirmation
u.password="password"
u.password_confirmation="password"

# at this point the record is valid and does save
u.save
# => true

# but we can now submit a blank password from console
u.password=""
# => true
u.password_confirmation=""
# => true

u.save
# => true
# oh noes

So what I want is the following:

  • password required on create, must be 6 chars in long
  • password_confirmation required on create, must match password
  • user should not have to submit password when updating login_name
  • password can not be deleted on update

Something which is confusing me is that rails throws a no method error if I use password_changed? as opposed to :password_digest_changed? in my password_confirmation validation. I don't understand why.

So does anyone know what I'm doing wrong here?

like image 649
stephenmurdoch Avatar asked Oct 23 '11 00:10

stephenmurdoch


1 Answers

password isn't a column in the database, right? Just an attribute?

So there is no password_changed? method, which would be available if password were a column. Rather you should just check to see if password is set at all.

Something like:

validates :password_confirmation, :presence => true, :if => '!password.nil?'

Although that solves the initial problem you were having, it still won't quite do what you want, as it's only checking presence, and you need it to be present and match password. Something like the following should work (in combination with the above validation).

validates :password, 
          # you only need presence on create
          :presence => { :on => :create },
          # allow_nil for length (presence will handle it on create)
          :length   => { :minimum => 6, :allow_nil => true },
          # and use confirmation to ensure they always match
          :confirmation => true

If you've never seen :confirmation before, it's a standard validation that looks for foo and foo_confirmation and makes sure they're the same.

Note that you still need to check for the presence of password_confirmation

like image 145
numbers1311407 Avatar answered Oct 10 '22 15:10

numbers1311407