Ruby on Rails Password Validation - ruby-on-rails

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?

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

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

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? }

yet another variant
validates_presence_of :password_digest
validates_length_of :password, minimum: 6, if: Proc.new { |user| user.password.present? }

Related

Ruby on rails Update

I have User model, it has some validations and they work on create. But when i call any user from database as #user=User.find(1) #user.valid? it returns false. Could you help me?
class User < ActiveRecord::Base
validates :name, :surname, :username, :phone, :role, :gender, :presence => true
validates :password_confirmation, :email_confirmation, :presence => true
validates :username, :email, :uniqueness => true
validates :verified, :bulletin, :inclusion => { :in => [true, false] }
validates :password,:email, :confirmation => true
....
end
I guess you need to add on: :create param for each validations that only need to be run on create.
For example when you're doing #user.valid? I gess you don't want to check if password_confirmation is present.
So in this case it should be:
validates :password_confirmation, :email_confirmation, :presence => true, :on => :create
Hope it helps :)
There is a special validation for this use case, that the user should provide a confirmation, but the confirmation is not stored in the database
validates :email, confirmation: true, :uniqueness => true
validates :password, confirmation: true, ....
This substitutes the validation for :password_confirmation and :email_confirmation, so you need also to remove them.
See the fine rails guides http://guides.rubyonrails.org/active_record_validations.html#confirmation

Rails custom validation one after another

I have a password field and that validates presence and length and both are working fine. But when I submit the form with blank password field, it displays error messages for both validations.
What I want is if the password is blank then length validator must not checked and display error message for only presence validator. Length validator will only be checked if password is present.
You can use Object#with_options and ActiveRecord::Base#new_record?:
class User < ActiveRecord::Base
with_options :if => :new_record? do |user|
user.validates :password, presence: true, length: { maximum: 20 }
end
end
Look rails conditional validation.
Along with other validations pass this
:allow_blank => true
For example
validates :password, :presence => true, :length => { :maximum => 20, :allow_blank => true }

attr_accessor and password validation on update

I have this code in my user model:
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
attr_accessor :password
before_save :encrypt_password
validates :email, :presence => true,
:uniqueness => { :case_sensitive => false },
:format => { :with => /\A[^#]+#[^#]+\z/ },
:length => 7..128
validates :password, :presence => true,
:confirmation => true,
:length => 6..128
private
def encrypt_password
return unless password
self.encrypted_password = BCrypt::Password.create(password)
end
end
Now in my controller when I'm updating some user fields with
#user.update_attributes(params[:user])
the password field is always validated, even when it is not set in the params hash. I figured that this is happening because of the attr_accesor :password which always sets password = "" on update_attributes.
Now I could simply skip the validation of password if it is an empty string:
validates :password, :presence => true,
:confirmation => true,
:length => 6..128,
:if => "password.present?"
But this doesn't work because it allows a user to set an empty password.
Using update_attribute on the field I'd like to change is not a solution because i need validation on that attribute.
If I pass in the exact parameter with
#user.update_attributes(params[:user][:fieldname])
it doesn't solve the problem because it also triggers password validation.
Isn't there a way to prevent attr_accesor :password from always setting password = "" on update?
New answer
This works for me:
validates :password, :presence => true,
:confirmation => true,
:length => { :minimum => 6 },
:if => :password # only validate if password changed!
If I remember correctly it also took me some time to get this right (a lot of trial and error). I never had the time to find out exactly why this works (in contrast to :if => "password.present?").
Old answer - not really useful for your purpose (see comments)
I get around this problem by using a completely different action for password update (user#update_password). Now it is sufficient to only validate the password field
:on => [:create, :update_password]
(and also only make it accessible to those actions).
Here some more details:
in your routes:
resources :users do
member do
GET :edit_password # for the user#edit_password action
PUT :update_password # for the user#update_passwor action
end
end
in your UsersController:
def edit_password
# could be same content as #edit action, e.g.
#user = User.find(params[:id])
end
def update_password
# code to update password (and only password) here
end
In your edit_password view, you now have a form for only updating the password, very similar to your form in the edit view, but with :method => :put and :url => edit_password_user_path(#user)
The solution I have started using to get round this problem is this:
Start using ActiveModel's built in has_secure_password method.
At console
rails g migration add_password_digest_to_users password_digest:string
rake db:migrate
In your model:
class User < ActiveRecord::Base
has_secure_password
attr_accessible :login_name, :password, :password_confirmation
# secure_password.rb already checks for presence of :password_digest
# so we can assume that a password is present if that validation passes
# and thus, we don't need to explicitly check for presence of password
validates :password,
:length => { :minimum => 6 }, :if => :password_digest_changed?
# secure_password.rb also checks for confirmation of :password
# but we also have to check for presence of :password_confirmation
validates :password_confirmation,
:presence=>true, :if => :password_digest_changed?
end
And finally,
# In `config/locales/en.yml` make sure that errors on
# the password_digest field refer to "Password" as it's more human friendly
en:
hello: "Hello world"
activerecord:
attributes:
user:
password_digest: "Password"
Oh, one more thing: watch the railscast

rails password update validation issue

I have the following validation:
validates :password, :presence => true, :confirmation => true, :length => { :within => 6..40 }, :format => { :with => pass_regex }, :unless => :nopass?
Then, when I try to update without password (nopass? is true) the following errors appear:
There were problems with the following fields:
Password is too short (minimum is 6 characters)
Password is invalid
Notice that the :unless works on :presence and :confirmation but not in :lenght or :format.
How could I fix this?
I've had some strange issues with the :confirmation flag as well, which I never figured out, but that's how I solved the problem in my Rails 3.0.x app:
attr_accessor :password_confirmation
validates :password, :presence => true, :length => {:within => PASSWORD_MIN_LENGTH..PASSWORD_MAX_LENGTH}
validate :password_is_confirmed
def password_is_confirmed
if password_changed? # don't trigger that validation every time we save/update attributes
errors.add(:password_confirmation, "can't be blank") if password_confirmation.blank?
errors.add(:password_confirmation, "doesn't match first password") if password != password_confirmation
end
end
I realise this is not an explanation why your code isn't working, but if you're looking for a quick temporary fix - I hope this will help.
You might use conditional validations
class Person < ActiveRecord::Base
validates :surname, :presence => true, :if => "name.nil?"
end

Undefined method password_changed? Error

I'm trying to set my program so that the password only is validated if it is changed (so a user can edit other information without having to put in their password).
I am currently getting an error that says
NoMethodError in UsersController#create, undefined method `password_changed?' for #<User:0x00000100d1d7a0>
when I try to log in.
Here is my validation code in user.rb:
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :password, :presence =>true, :confirmation => true, :length => { :within => 6..40 }, :if=>:password_changed?
Here is my create method in users_controller.rb:
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
#title = "Sign up"
render 'new'
end
end
Thank you!
Replace with:
:if => lambda {|user| user.password_changed? }
I'd do two different validations:
validates :password, :presence =>true, :confirmation => true, :length => { :within => 6..40 }, :on => :create
validates :password, :confirmation => true, :length => { :within => 6..40 }, :on => :update, :unless => lambda{ |user| user.password.blank? }
I encountered this same problem and after reading this post I implemented the code suggested by apneadiving in his answer but with a slight modification:
I used two different validations, one for create:
validates :password, :presence => true, :confirmation => true, :length => { :within => 6..128 }, :on => :create
and then I used this one for update:
validates :password, :presence => true, :confirmation => true, :length => { :within => 6..128 }, :on => :update, :unless => lambda{ |user| user.password.to_s.empty? }
Originally, I Implemented exactly what apneadiving suggested, but I realized that on updates to the user it wasn't actually validating the presence of a string. As in, it was allowing a user to set their password to " " (A string of whitespace). This is due to the fact that " ".blank? == true, and so if you have it set to validate like this:
:unless => lambda { |user| user.password.blank? }
Then it won't run the validation if a string full of whitespace is submitted by the user is submitted because it reads the string as being blank. This essentially invalidates the effect of validating for presence on the update. The reason I did password.to_s.empty? instead of simply password.empty? is to prevent errors if someone calls update_attributes on the user model and doesn't pass in anything into the password, field, then the password will be nil, and since the ruby nil class doesn't have an empty? method, it will throw an error. Calling .to_s on nil, however will convert it to an empty string, which will return true on a successive .empty? call (thus the validation won't run). So after some trials and tribulation I found that this was the best way to do it.
Ended up here googling this error message, and using
#account.encrypted_password_changed?
in my case yielded what I wanted.
The change to look for, in Rails 4 at least, is password_digest.
#account.password = "my new password"
#account.changes # => {"password_digest"=>["$2a$10$nR./uTAmcO0CmUSd5xOP2OMf8n7/vXuMD6EAgvCIsnoJDMpOzYzsa", "$2a$10$pVM18wPMzkyH5zQBvcf6ruJry22Yn8w7BrJ4U78o08eU/GMIqQUBW"]}
#account.password_digest_changed? # => true

Resources