For reference: Rails 4, bcrypt 3.1.7, and I'm using the simple_form gem
Within my model, I have this code:
class User < ActiveRecord::Base
has_secure_password
validates :email, presence: {message: 'You must provide an email'},
format: { with: /#/, message: 'Please enter a valid email' }
validates :name, presence: {message: 'You must enter your first name'}
# TODO: Make validation message work
validates :password, presence: {message: 'You must enter a password'},
length: {minimum: 8,
message: 'Your password must contain at least 8 characters'}
end
Everything works as expected, except the presence validator for password. How do I go about changing the message to my message?
Check out the docs on has_secure_password here: http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html
As you can see, you can suppress the default validations:
has_secure_password validations: false
This should allow your custom validations for password to be used.
Note that you'll now need to explicitly add a password confirmation validation also. Here's the one that's used in has_secure_password that you could use:
validates_confirmation_of :password, allow_blank: true
So with everything, your model would look like this:
class User < ActiveRecord::Base
has_secure_password validations: false
.
.
.
validates :password, presence: {message: 'You must enter a password'},
length: {minimum: 8,
message: 'Your password must contain at least 8 characters'}
validates_confirmation_of :password, allow_blank: true
end
EDIT
If you want to only change the message and still use the default validations that come with has_secure_password, you can edit your en.yml file:
config/locales/en.yml
en:
activerecord:
errors:
models:
user:
attributes:
password:
blank: "You must enter a password"
Then, for :password, you will only need to add your minimum password validation, as that is not included with has_secure_password:
class User < ActiveRecord::Base
has_secure_password
.
.
.
validates :password, length: {
minimum: 8,
message: 'Your password must contain at least 8 characters'
}
end
Related
In Michael Hartl's tutorial, the following is stated.
class User < ActiveRecord::Base
attr_accessor :remember_token
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
end
In case you’re worried that Listing 9.10 might allow new users to sign up with empty passwords, recall from Section 6.3.3 that has_secure_password includes a separate presence validation that specifically catches nil passwords.
My question is, how is the has_secure_password validation working if it allows the test to pass? I do not understand, clearly the has_secure_password validation is not "catching" this rule to bypass an empty password.
Further, how does rails know not to set and save the empty passwords to the user? please help me.
You can check the documentation here , it explains everything in details,
According to the source code:
def has_secure_password
.....
if options.fetch(:validations, true)
include ActiveModel::Validations
# This ensures the model has a password by checking whether the password_digest
# is present, so that this works with both new and existing records. However,
# when there is an error, the message is added to the password attribute instead
# so that the error message will make sense to the end-user.
validate do |record|
record.errors.add(:password, :blank) unless record.password_digest.present?
end
validates_length_of :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
validates_confirmation_of :password, allow_blank: true
end
..........
end
if you did not call has_secure_password(validations: false) the three types of validations will be added. i think the reason of the passing test is :
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
The :allow_nil option skips the validation when the value being
validated is nil.
To Add
how does rails know not to set and save the empty passwords to the
user?
I think it's because the params[:user][:password] is blank and not nil
There is an AcviteRecord Model named User like this:
class User < ActiveRecord::Base
validates :name, :presence => true
validates :email, :presence => true, :uniqueness => true
validates :plain_password, :presence => true, :confirmation => true
validates :plain_password_confirmation, :presence => true
#...other codes
end
It requires that the update of name and email and the update of password are separated.
When only update name and password, using update or update_attributes will cause password validation which is not needed. But using update_attribute will save name and email without validation.
Are there any ways to update particular fields of model with validation without causing the other fields' validation?
Give it a try, might help
class User < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true, :uniqueness => true
validates :plain_password, length: { in: 4..255, allow_nil: true }, confirmation: true
validates :plain_password_confirmation, presence: true, if: -> (user){ user.plain_password.present? }
# ......
# ......
end
Apart from this you should reconsider about saving plain_password ;)
You can adjust your validations to only run on create. Requiring confirmation ensures changes on edit are applied.
validates :plain_password,
confirmation: true,
presence: {
on: :create },
length: {
minimum: 8,
allow_blank: true }
validates :plain_password_confirmation,
presence: {
on: :create }
I am assuming you are hashing your passwords, so this would accompany code similar to:
attr_accessor :plain_password
before_save :prepare_password
def encrypted_password( bcrypt_computational_cost = Rails.env.test? ? 1 : 10)
BCrypt::Password.create plain_password, cost: bcrypt_computational_cost
end
private #===========================================================================================================
# Sets this users password hash to the encrypted password, if the password is not blank.
def prepare_password
self.password_hash = encrypted_password if plain_password.present?
end
A better way to handle this is to not include the fields in the rest of the edit form, but rather provide a link to "Change my password". This link would direct to a new form (perhaps in a modal window) which will require the new password, confirmation of the new password, and the old password, to prevent account hijacking.
In your case you can use has_secure_password The password presence is only validated on creation.
I have pretty big RoR app.
There is superclass named User.
class User < ActiveRecord::Base
validates :email, presence: true
end
Also I have class Client which inherited by User
class Client < User
with_options(on: [:personal_info]) do |pi|
pi.validates :first_name,
:last_name,
:email,
:mobile_phone,
:landline_phone,
presence: true
pi.validates :primary_email,
email_format: {
message: "doesn't look like an email address"
}
end
end
When I create Client's object I got 2 errors that "Email can't be blank."
How can I disable or skip validates of superclass??
Remove validations in superclass is impossible.
I'm trying to validate a user's password on create and update (custom authentication, no gems). A password should ALWAYS be required on create, but only required on update if they type something in.
models/user.rb
has_secure_password
validates :password, presence: {if: :requires_password?}, length: {minimum: 6}
validates :password_confirmation, presence: {if: :requires_password?}
private
def requires_password?
new_record? || !password.nil?
end
This works as expected, but when creating a new user, the error message "Password can't be blank" is displayed twice.
Is there a simple fix for this, or am I approaching password validation the wrong way?
See the doc of has_secure_password, validation of presence is already included:
Validations for presence of password, confirmation of password (using
a "password_confirmation" attribute) are automatically added. You can
add more validations by hand if need be.
Try this:
validates :password, \
presence: true,
length: {minimum: 6},
confirmation: true,
if: :requires_password?
(I broke it up into multiple lines for legibility; you can still use the one-liner)
You don't need two separate validations - you just need one on password that includes the confirmation validation.
This seems to work for me!
validates :password, length: {minimum: 6}, allow_blank: true
It requires a password on create, but only updates if something is passed in. And because I'm using has_secure_password, the confirmation is handled automatically.
Updated models/user.rb
has_secure_password
validates :password, length: {minimum: 6}, allow_blank: true
Thanks for the help!
My User model has an attribute called :profile_name which is used in routing profile page url's - domain.com/:profile_name . In order to prevent collision with my other views I want to make sure a User can't choose something like "friends" or "feed" as their profile name. How can I set this in validations?
/models/user.rb (currently):
...
validates :email, presence: true, uniqueness: true
validates :profile_name, presence: true,
uniqueness: true,
format: {
with: /^[a-zA-Z0-9_-]+$/,
message: 'Must be formatted correctly.'
}
...
The exclusion validation helper:
validates :profile_name, presence: true,
...,
exclusion: {
in: ["friends", "feed"],
message: "Profile name %{value} is reserved."
}
Use a custom validation method. You'd probably want to separate out the forbidden list, but I kept this extra concise.
class User < ActiveRecord::Base
validates :profile_not_forbidden
protected
def profile_not_forbidden
if ['friends','feed'].include?(profile_name)
errors.add(:profile_name, 'Forbidden profile name.')
end
end
end