How to insert an already encrypted password on devise model? - ruby-on-rails

I am a newbie in rails. I would like to ask if it's possible to insert an encrypted password(encrypted using devise default encryption) to devise model?
e.g:
User.csv
id,name,encrypted_password
1,dude,$2a$10$0.xEu5LvDbnmGVIqgIab8ej5o2b3eKa8KLevsg5bxIX0SHSOl5gye
I want to read this csv file and then insert the data to User model.
But I realized that you can't insert the already encrypted password normally on encrypted_password of User model. I'm thinking to decrypt the password but they say that it is bad for the system's security.
Update: It can now insert an encrypted password but the inserted password is different to the one on the csv since the devise automatically encrypts the password before inserting it to the database. Is there a way for devise model to detect if the password is already encrypted before inserting it to the database?

yes, you can set the encrypted_password value directly:
u = User.find(1)
u.update_attribute(:encrypted_password, "$2a$10$0.xEu5LvDbnmGVIqgIab8ej5o2b3eKa8KLevsg5bxIX0SHSOl5gye")
But you should make sure that you're not setting 'password', if you do this won't work as it'll encrypt it again, so
u = User.new
u.password = 'foo'
u.password_confirmation = 'foo'
u.encrypted_password = "$2a$10$0.xEu5LvDbnmGVIqgIab8ej5o2b3eKa8KLevsg5bxIX0SHSOl5gye" # this line will be ignored
u.save
u = User.new
u.encrypted_password = "$2a$10$0.xEu5LvDbnmGVIqgIab8ej5o2b3eKa8KLevsg5bxIX0SHSOl5gye" # this line will now work
u.save

Related

Update devise user password using a custom method

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.

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.

Data migration problems when you explicitly set ID's (Rails + Postgres)

I'm writing a script for a big migration and have come across a major issue.
# Import users
user_data.each do |data|
u = User.new
u.id = data.id
u.email = data.email
# more user attributes set...
u.save!
end
# required to prevent Postgres from trying to use now taken user ids
ActiveRecord::Base.connection.execute "ALTER SEQUENCE users_id_seq RESTART WITH #{User.last.id+1};"
So first we read user data from a data source, and set it's id manually. We need to preserve ids since we are migrating associated data as well.
Then later on, we need to create more users conditionally from the data of an associated object.
# Create a user for this email if no user with this email exists.
if data.email
user = User.find_by_email(data.email)
if user
o.user = user
else
o.user = User.create!(
first_name: 'Unknown',
last_name: 'Unknown',
email: data.email,
password: generate_temp_password
)
end
end
This fails at User.create! with:
Validation failed: Email has already been taken (ActiveRecord::RecordInvalid)
I've debugged this a bit and can see that User.where(email: data.email).first is nil right before this error is thrown. I suspect this has something to with setting ids beyond the current auto increment value, somehow causing the new records to be invisible in my queries, but visible to Postgres own validations.
So how can a user with a specific email not be present, but still trigger DB validation errors?
Apparently, Devise downcases email addresses. And the offending email had some caps in it. So it missed a case sensitive check, and then failed as a dupe when case insensitive.
Devise outsmarted me it seems.

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

Resources