19

So I have interesting password validation requirements:

  • When a user signs up, I want them to have to type in password and confirm and be between 6..40 (GOT THIS WORKING 100%)

  • When a user updates their profile, the same validation rules apply (GOT THIS WORKING 100%)

  • When an admin adds a user, they only have to enter the password once and it should be validated (NOT WORKIG)

  • When an admin edits a user and the password field is blank, it shouldn't update the password, if they type something, it should be validated. (PARTIAL WORKING)

    validates :password, :presence => true,
                       :confirmation => true,
                       :length => {:within => 6..40},
                       :unless => :force_submit
    

The only cases I can't cover are when an admin adds a user, it is not validated and when an admin edits a user (and types in a password) it is not validated.

the :force_submit is passed in from the admin form, so the password isn't validated. (So the case of an updating empty password works)

Any ideas/magic?

JJD
  • 50,076
  • 60
  • 203
  • 339
Chris Muench
  • 17,444
  • 70
  • 209
  • 362

4 Answers4

42

Building slightly on the accepted answer, here's the code that I used in a Rails project at work. (Note: We're using devise to handle user authentication, and devise_invitable to create new users.)

PASSWORD_FORMAT = /\A
  (?=.{8,})          # Must contain 8 or more characters
  (?=.*\d)           # Must contain a digit
  (?=.*[a-z])        # Must contain a lower case character
  (?=.*[A-Z])        # Must contain an upper case character
  (?=.*[[:^alnum:]]) # Must contain a symbol
/x

validates :password, 
  presence: true, 
  length: { in: Devise.password_length }, 
  format: { with: PASSWORD_FORMAT }, 
  confirmation: true, 
  on: :create 

validates :password, 
  allow_nil: true, 
  length: { in: Devise.password_length }, 
  format: { with: PASSWORD_FORMAT }, 
  confirmation: true, 
  on: :update
Tom Lord
  • 27,404
  • 4
  • 50
  • 77
  • Perfect! I want to add one more to the password format: Must not contain repeating or sequential characters of 3 or more (ex. 123, abc, zzz, 555). How can I? – Arif Nov 25 '16 at 10:15
  • 2
    @Arif to be honest, my actual recommendation from a security standpoint would be to just use a robust library such as [zxcvbn](https://github.com/envato/zxcvbn-ruby) to check password entropy, rather than trying to re-implement a secure check yourself. – Tom Lord Nov 25 '16 at 10:31
  • 2
    However, if you have a specific, well defined list of "security requirements" (for example, if a client insists that your password policies match their arbitrary standards), then my suggestion would be to write a [custom validator class](http://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations) to handle each scenario explicitly. (Don't neglect to add unit tests!) – Tom Lord Nov 25 '16 at 10:34
  • you could consolidate the 2 validations into one by removing on: :create and replacing it with allow_blank: true in the first validation and then just delete the 2nd validation. allow_blank prevents a user from creating/updating a user with an empty password but allows users to not have to retype it when updating their name, email, etc... – random_user_0891 Aug 10 '22 at 16:59
  • @random_user_0891 *"allow_blank: true in the first validation"* -- Wouldn't that allow creating a user with a `null` password?... You could rely on a database constraint for that, to handle it gracefully at the application level you need a model validation. – Tom Lord Aug 11 '22 at 10:23
  • *"allows users to not have to retype it when updating their name, email, etc..."* --- that's *exactly* what my second validation already achieves. – Tom Lord Aug 11 '22 at 10:26
  • @TomLord the "validates :password, presence: true," part of the model validation will prevent a null password from being created even with allow_blank set to true. I replaced on :create with allow_blank: true and deleted the second validation for on :update and it's working well for creating/updating users in Rails 7. – random_user_0891 Aug 11 '22 at 19:30
  • @random_user_0891 To be clear, you're validating `allow_blank: true` **AND** `presence: true`? That sounds very odd to me, but if it works... I guess you've saved yourself a few lines of code --- I'd definitely want to add tests for it though, because *if* that works it almost feels like an implementation-detail-quirk that's enabling it, which could unexpectedly break on any rails version update?! – Tom Lord Aug 12 '22 at 11:05
33

The below seem to meet my requirements...I am actually now requiring a confirmation for all users.. (It makes the view cleaner). But on an update I am allowing blanks.

  validates :password, :presence => true,
                       :confirmation => true,
                       :length => {:within => 6..40},
                       :on => :create
  validates :password, :confirmation => true,
                       :length => {:within => 6..40},
                       :allow_blank => true,
                       :on => :update
Chris Muench
  • 17,444
  • 70
  • 209
  • 362
  • 1
    I do not understand how this will solve the third requirement of the question. This requirement seems to ask that the admin can leave the password field blank, but edit another field and save the user, without changing the password. The above code seems to allow the user object to be saved, but will change the password to a blank? – Obromios Apr 07 '15 at 23:34
  • The above code does not solve requirement #3, but nor does it behave in the way you describe. All this code does is allows you to UPDATE a user (e.g. change their name, or address, or email, etc) without supplying a password. Although it could be done, I honestly don't think requirement #3 is a good idea anyway - password confirmations are a very simple + effective safety net. – Tom Lord Nov 10 '15 at 14:35
0

this works for blank password on update action:

validates :password, :presence => true, :on => :update,
 :if => lambda{ !password.nil? }

validates :password,
  :confirmation => true,
  :length => { :minimum => 6},
  :if => lambda{ new_record? || !password.nil? }
0

yet another variant

validates_presence_of :password_digest

validates_length_of :password, minimum: 6, if: Proc.new { |user| user.password.present? }
achempion
  • 794
  • 6
  • 17
  • As it stands, this answer is weak. Can you explain what makes this answer different or preferable to other answers? – neontapir Sep 22 '14 at 18:41