has_secure_password, wont create attributes - ruby-on-rails

I am trying to use has_secure_password and bcrypt (3.1.11) on my Rails app.
Here is my setup on Ubuntu:
I installed bcrypt with no issues and have it in my gem file
I have a password_digest:string for my users migrated in the database
I added has_secure_password to my user model
I am getting no errors, however when I go to create a user, it is asking me for a password_digest attribute instead of password and password_confirmation.
why is this? Shouldn't the has_secure_password automatically change the user to need a password and password_confirmation instead of password_digest?

Nope, you don't store the password and password_confirmation, just the password_digest. You never want to store plain text passwords in your db.
When you create a new user you do
User.create(password: 'something', password_confirmation: 'something', ..other fields..)
and then has_secure_password takes care of updating the password_digest in the database.
It's also a good rule to include some validations in your User model.
validates :password_confirmation, presence: true, on: :create
validates :password, confirmation: true
Also don't forget to white list them in your controller
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
User.create(user_params)

You'll probably need to change the form and/or create template manually to contain a password_confirmation field. Look more closely at the has_secure_password docs maybe?

Related

Disabling validation in a particular case

In my User model I set validations for the password and its confirmation:
validates_presence_of :password, :password_confirmation
I use this for the forms. These attributes are not stored in the database, I am using the authlogic gem.
Now I am trying to make a system to restore forgotten passwords via email. The user inputs his email and the object is retrieved like this:
#user = User.find_by_email(params[:email])
Then, at a certain point in the process the value of a User attribute is set to true meaning that the user forgot his password, so I need to do a #user.save.
And there's the problem. Since password and password_confirmation were never stored, their values are nil. However, the validations come in play and prevent the user from being updated.
Is there any way to skip those particular validations so I can make this work? Or should I do something else? Any help?
You can skip all validations with save(validate: false). You can skip individual validations with the :if or :unless option, for example.
validates_presence_of :password, :password_confirmation, unless: :forgot_password?
def forgot_password?
# return true if the user is in the forgot password process
end
change
validates_presence_of :password, :password_confirmation
to
Assuming your field that is set to true is forgot_password
validates_presence_of :password, :password_confirmation, :unless => Proc.new { |a| a.forgot_password? }
Conditional Validations documentation

Rails Conditional Validations

I am working on a project and need some help on where to begin. I have three pages
Update User
Create User
Admin User Password Change (like a Hard Reset Password for but only the admin can reset the user's password)
Change Password
On Create User first name, last name, username, password, and password confirmation are mandatory.
On Update User just first name, last name and username are mandatory.
On Admin User Password Change and Change Password, just password and password confirmation are mandatory.
How would you go about doing this? I don't think this is possible through models using validates_presence_of with an if because there are too many scenarios. Any help or guidance would be appreciated. Also, I am pretty new to Rails if you can't already tell.
You can pass conditionals to your validations:
validates :password, :confirmation => true, :presence => true
validates :first_name, :last_name, :username, :presence => true
validate :admin_user_password_change?
Of course you'd have to define what the admin_user_password_change? method would be to determine if it is an admin user changing a password.
UPDATE
The admin_user_password_change? method might be something like:
def admin_user_password_change?
unless self.admin? && self.password.present? && self.password_confirmation.present?
self.errors.add(:admin_password_change, "password and password_confirmation are required.")
end
end
As for How would it communicate with the controller?, it wouldn't directly. But if any of the conditions in the method are false (e.g. self.admin? && self.password.present? && self.password_confirmation.present?), an error will be added to the instance of User and the instance won't save in the controller.
Setting some fields to new values doesn't unset other fields; just because you're only updating some fields in one action doesn't mean the other fields will be unset, so long as they start in a consistent state.
Just add your validations. It will work fine.
You can tell to your validation work only on certain cenarios only using:
The create:
validates :first_name, :last_name, :username, presence: true, on: :create
The update:
validates :password, presence: true, on: :update
Take a look at on.
For validation based on context take a look at Context Validations

Updating profiles and passwords

I'm not sure on how to best tackle this issue - I need to validate a user's password when adding a new record and I also need to be able to validate when updating the passoword too. But how can I let a user update just part of their profile and perhaps leaving the password blank.
Note before you suggest allow_blank I am aware of this option but this is not suitable because when a user needs to change their password as a result of losing/forgetting it I don't want to allow user to have a blank password.
validates :password, presence: true, if: lambda { |user| user.password_changed? }
by ActiveModel::Dirty (available on all models by default)
You can pass an if statement to a validation:
`validates_presence_of :password, :if => :should_validate_password?
You should be able to pass a condition in here to catch whether the user is updating their password or not:
Model
def should_validate_password?
updating_password || new_record?
end
Controller
#user.updating_password = true
#user.save
See here for more details: http://railscasts.com/episodes/41-conditional-validations
Updated:
In your case I would create an if statement in the controller that detected whether any new password was being passed in the params. If so I would set #user.updating_password = true which would trigger the validation in the model. If not then #user.updating_password would be nil and the validation wouldn't trigger.
Fully custom way
Usually, #password= and #password_confirmation= are just virtual setters, the true attribute is #hashed_password or something. So, you could do something like this :
class User
attr_accessor :password, :password_confirmation
validate :validates_password
validates_presence_of :hashed_password
private
def validates_password
if password or password_confirmation
if password != password_confirmation
errors.add( :password, 'your message' )
end
# your others validations
self.hashed_password = hash_password
end
end
def hash_password
# your hashing code
end
end
When user is created providing password, virtual attributes password and password_confirmation are set, so if condition is true and validations are enforced.
When password is already set and user does not change it (an edit form without providing passwords), validation will not be enforced, because of if password and password_confirmation.
When password is already set and user change it, password and password_confirmation are set, so validation is triggered.
When you want to reset password, just set in your action hashed_password to nil. The model is now invalid because of validates_presence_of :hashed_password and user has to provide a new one.
Using #has_secure_password
With #has_secure_password, rails will handle most of this, especially :
it creates virtual atttributes
it will trigger validation for confirmation match only if password attributes are provided
it will hash password
So, what you need is just adding your own validations, doing it only when password or password_confirmation are present.
class User
has_secure_password
validate :validates_password
private
def validates_password
if password or password_confirmation
unless <your_test>
errors.add( :password, '<your error message>' )
end
end
end
end
As previously, this will only be triggered if password and password_confirmation are provided, which only happens if user submitted them as form data (as real attribute is password_digest).

Validation for a certain action in ruby on rails

In my current ROR project I am using devise pluing for validation. In my change password form validation, I am using the following code in the user model
validates_presence_of :password, :password_confirmation
But I wants to validate is only for an action. I have a function in my user controller named update_password. I found the that I can assign the action as follows:
validates_presence_of :password, :password_confirmation, :on => :update_password
But its not working. Even if the password and password confirmation fields are empty, the form is submitted. Can anyone help me to solve how to set the validation only for a particular action. Will be a great help
Thanks a lot
You can use :validatable option implemented in devise.
Just add to your model
devise :validatable
And set validation options in your config/initializers/devise.rb file
# ==> Configuration for :validatable
# Range for password length. Default is 6..128.
config.password_length = 6..128
# Email regex used to validate email formats. It simply asserts that
# an one (and only one) # exists in the given string. This is mainly
# to give user feedback and not to assert the e-mail validity.
config.email_regexp = /\A[^#]+#[^#]+\z/
Another way is to use your own regexp validations. You can add to your model
validates :password, :format => { :with => /\A[a-zA-Z]+\z/, :message => "Only letters allowed" }
After that you can call #user.valid? in your controller to check that your user instance is correct.
There are many different ways to validate your model.
You can read more about validation and ActiveRecord callbacks in guides: http://guides.rubyonrails.org/active_record_validations_callbacks.html
If you're using devise, there's no need to check for that. It's handle for you by default.

In Rails 3, how can I skip validation of the password field when I'm not attempting to update the password?

My User model contains :name, :email, and :password fields. All 3 have validations for length. An "update account" web page allows the user to update his name and email address, but not password. When submitted, params[:user] is
{"name"=>"Joe User", "email"=>"user#example.com"}
Note there is no "password" key because the form doesn't contain such an input field.
When I call
#user.update_attributes(params[:user])
the password validation fails. However, since I'm not attempting to update the password, I don't want the password validation to run on this update. I'm confused why the password validation is running when params[:user] doesn't contain a "password" key.
Note that I want to have a separate web page elsewhere that allows the user to update his password. And for that submission, the password validation should run.
Thank you.
My application does something like this
attr_accessor :updating_password
validates_confirmation_of :password, :if => should_validate_password?
def should_validate_password?
updating_password || new_record?
end
so you have to model.updating_password = true for the verification to take place, and you don't have to do this on creation.
Which I found at a good railscast at http://railscasts.com/episodes/41-conditional-validations
In your user model, you could just ignore the password validation if it's not set.
validates_length_of :password, :minimum => N, :unless => lambda {|u| u.password.nil? }
Using update_attributes will not change the value of the password if there is no key for it in the params hash.
Validation doesn't run against the changed fields only. It validates existing values too.
Your validation must be failing because the password field contains some invalid content that's already saved in the database. I'm guessing it's probably because you're hashing it after validation and you're trying to validate the hashed string.
You can use a virtual attribute (an instance variable or method) that you validate with a custom method, and then assign the hash to the stored password field. Have a look at this technique for ideas.
An app that I am working on uses the following:
validates_confirmation_of :password,
:if => Proc.new { |account|
!account.password.blank?
|| !account.password_confirmation.blank?
|| account.new_record? }
Depending on your requirements, you might want to remove the new_record? check
When password is added then only confirmation will be called and presence will call on create action only
**validates_presence_of :password, :on =>:create**
**validates_confirmation_of :password**

Resources