Update devise user password using a custom method - ruby-on-rails

I am using Devise for authentication and trying to change the password of certain users to their date of birth using a method
def set_dob_password id
#user = User.find(id)
#user.update_attribute(password: #user.birth_date)
end
isn't working
. What is the best way of doing this ?

Of course it won't work! Devise stores encrypted passwords only in the DB. If you look at the users table you won't see a 'password' field but 'encrypted_password' column instead.
You first have to encrypt the password.
pw = BCrypt::Password.create(#user.birth_date)
#user.update_attribute(:encrypted_password, pw)
Make sure you have the 'bcrypt' gem first.

#user.update_attributes(password: params[:password], password_confirmation: params[:password_confirmation]). You need to update both password + password confirmation. So in your case replace parmas with user DoB.

Related

How to update password attribute with has_secure_password?

I used has_secure_password for the User model. Now I am trying to use AJAX to update some of the user's attributes, including password. However, it looks like with has_secure_password, the password attribute no longer exists, replaced by password_digest. So when I am trying to do
user[:password] = "The password passed by AJAX"
user.save!
I got:
ActiveModel::MissingAttributeError (can't write unknown attribute
password)
The question is: What is the right way to update a user's password in this situation? Do I need to manually compute the hash and update the password_digest?
EDIT:
I am using Rails 4.2.1
Normally you just use:
user = User.find 1
user.password = 'Test123456789'
user.save
But, it sounds like you have not added a password_digest column to the users table or have not run the migrations.

Password encryption in Rails

I have User model and use Devise gem on it. In my application i have an admin user who can register other users (for example:clients). Then these users can log in by themselves with randomly generated password. (But no one can register by themselves, only admin can register users.)
When I am creating user from other user logged in, i.e admin user creates another user , i want to generate some random password, encrypt it, and save to database.
How to encrypt passwords, so that it will work with Devise authorization. I guess I have to use the same method as Devise?
I want something like that:
EDIT:
def create
#user = User.new(user_params)
# set #user.password to some random encrypted password
#user.save
end
So every created user will get some random password.
The reason i am asking this, is that i think that if my encryption/decription will not match what devise uses users will not be able to log in with their passwords, since when they log in their input is encrypted via devise's encryption.
If you are using Devise and you have :database_authenticable enabled, you don't need what you describe at all.
Devise encrypts automatically when it saves to the database and doesn't decrypt when it reads it back, however when you store it in the password field, it can be plain text, Devise will take care of it for you only when writing (so it will stay plain text until you save).
So in your controller to create new users you can just do the following:
def create
# I assume your form will pass a `params[:password]` in plain text too
#user = User.new(user_params)
#user.password_confirmation = params[:password]
#user.save
end
This should be enough for your purpose, don't need to match devise encryption
Update 1:
To generate a random password in addition, you can do something like:
require 'securerandom'
def create
# I assume your form will pass a `params[:password]` in plain text too
#password = SecureRandom.hex(16)
#user = User.new(user_params.to_h.merge(password: #password, password_confirmation: #password))
#user.save
# Remember to display `#password` in some way to the user
end
You should be having :database_authenticatable as one of the devise modules in your User model.
From Devise, it says
Database Authenticatable: encrypts and stores a password in the
database to validate the authenticity of a user while signing in. The
authentication can be done both through POST requests or HTTP Basic
Authentication.

How to match a new string with the password already encrypted

I am working on a rails 3.2.13 project. I am using devise plugin (devise gem 3.2.2, 1.4.2) for authentication. Using this plugin, how can I validate the current_password field while changing the old password to a new one? Or else, please suggest how I can achieve this by encrypting the given string and matching it with the password already saved without using the devise plugin.
E.g.: One user has encrypted_password like below:
"$2a$10$VrawKYj6zp10XUxbixVzE.7d4QgYjQn9aiuzAuP7fp3PZOLMP5wbu"
while changing the password, if I enter a current_password, it should match the string above (encrypted_password == current_password). How can I validate this?
I believe you need to break your problem down into the following steps:
Determine if the old_password is actually the user's current password.
To do this, you can call:
User.find_by_id([SOME_ID]).valid_password?(old_password)
If this returns true, then you can move on to the next step to begin changing of the password. If it doesn't, then the old_password is incorrect, and you should not allow the changing of password.
The implementation of valid_password? can be found in the Devise gem's /lib/devise/models/database_authenticatable.rb file (at around Line 40). You could use this implementation to roll your own code for validating a password. But, Devise pretty much does it for you if you call valid_password?, so rolling your own seems unnecessary.
If old_password is valid, then verify that new_password matches confirm_new_password.
if (new_password == confirm_new_password)
.
.
.
end
If these match, then set the new password by doing the following:
u = User.find_by_id([SOME ID])
u.password = new_password
u.password_confirmation = confirm_new_password
u.save
You can verify that the password has been changed by:
u.valid_password?(new_password)
Update user with current_password validation:
#user.update_with_password(account_update_params)
# account_update_params - should have :current_password, :password, :password_confirmation
It is default behaviour in Devise::RegistrationsController. If you want update user without password, you should overwrite controller's action
class UsersController < Devise::RegistrationsController
def update_resource(resource, params)
# resource.update_with_password(params)
resource.update_attributes(params)
end
end
Do I understand you right what you want allow users login with encrypted and unencrypted (usual) password?
We have:
user.valid_password?('Password2').should
code on github
So we can overwrite it inside models/user.rb
def valid_password?(password)
encrypted_password == password || super(password)
end

OmniAuth + Identity Forgot Password

I'm developing a Rails 3.0 application and am using OmniAuth + Identity for authentication a registration. I've implemented a User model that's tied to the Identity model through a foreign key and everything is working well. Now, I want to implement a forgot password feature.
Given a user's email, I want to send them an email with a link to reset their password. The email contains a random hex string that's associated with the user.
Now, how do I reset the user's Identity password?
In the Identity database table, it's stored as a password_digest. Can I just overwrite this?
Do this:
#identity = Identity.find(1)
#identity.password = "newpassword"
#identity.password_confirmation = "newpassword"
#identity.save
In a omniauth-identity's issue, wdspkr say:
Once you understand that omniauth-identity is using ActiveModel's
SecurePassword it's really easy to solve this. Instead of setting the
password_digest you just set password and password_confirmation and
update.
So it turns out it's that simple. Just overwrite the existing password_digest in the Identity table. Use the BCrypt library to create the password_digest:
require 'bcrypt'
...
class UsersController < ApplicationController
...
def update
#user = User.find(params[:id])
...
user_identity = Identity.find_by_email(#user.email)
unencrypted_password = params[:user][:password].to_s
password_digest = BCrypt::Password.create(unencrypted_password)
user_identity.password_digest = password_digest;
user_identity.save!
end
end

Rails Devise: Set password reset token and redirect user

In my app for a certain use case I create a new user (programmatically set the password) and send them a confirmation email.
I would like them to be able to change their password immediately after confirming (without having to enter the system generated one which I don't want to send them)
In effect I would like
1) System creates a new user account with generated password.
2) System sends confirmation email.
3) User clicks confirmation and is redirected to enter in their password (effectively send them to a URL like below)
Change my password
Any help / pointers would be great.
A simple way to have just one step for users to confirm email address and set initial password using the link you proposed...
Send one email your app generates, including a reset_password_token, and consider user's possession of that token confirmation of the validity of that email address.
In system account generation code, assuming User model is set up with :recoverable and :database_authenticatable Devise modules...
acct = User.new
acct.password = User.reset_password_token #won't actually be used...
acct.reset_password_token = User.reset_password_token
acct.email = "user#usercompany.com" #assuming users will identify themselves with this field
#set other acct fields you may need
acct.save
Make the devise reset password view a little clearer for users when setting initial password.
views/devise/passwords/edit.html.erb
...
<%= "true" == params[:initial] ? "Set your password" : "Reset your password" %>
...
Generated Email
Hi <%= #user.name %>
An account has been generated for you.
Please visit www.oursite.com/users/password/edit?initial=true&reset_password_token=<%= #user.reset_password_token %> to set your password.
No need to include :confirmable Devise module in your User model, since accounts created by your app won't get accessed without the reset_password_token in the email.
Devise will handle the submit and clear the reset_password_token field.
See devise_gem_folder/lib/devise/models/recoverable.rb and database_authenticatable.rb for details on reset_password_token method and friends.
If you want to use Devise :confirmable module rather than this approach, see the Devise wiki page.
In Rails 4.1, the following modification of Anatortoise House's reply works:
user = User.new
user.password = SecureRandom.hex #some random unguessable string
raw_token, hashed_token = Devise.token_generator.generate(User, :reset_password_token)
user.reset_password_token = hashed_token
user.reset_password_sent_at = Time.now.utc
user.email = 'user#usercompany.com'
user.save!
# Use a mailer you've written, such as:
AccountMailer.set_password_notice(user, raw_token).deliver
The email view has this link:
www.oursite.com/users/password/edit?initial=true&reset_password_token=<%= #raw_token %>
Here is my snippet for mailer preview
class Devise::MailerPreview < ActionMailer::Preview
def reset_password_instructions
user = User.last
token = user.send(:set_reset_password_token)
Devise::Mailer.reset_password_instructions(user, token)
end
end
You can call
user.send(:set_reset_password_token)
It may not be stable, as it's a protected method but it can work for your case. Just cover it with a test.
(tested on Devise v. 3.4)

Resources