I'm having a very weird issue with devise when trying to edit a user's password - here is a simplified version of what I'm doing at the moment:
def update
user = User.find(params[:id])
password = params.delete(:password)
password_confirmation = params.delete(:password_confirmation)
if password.present? and password != "" and password == password_confirmation
user.password = password
end
user.update(params)
user.save
render json: user
end
What's really weird is that if I set password to an arbitrary string, by putting
password = "testpassword"
above
user.password = password
it works properly, the password is set to "testpassword", and i can log in using it just fine. But if I try and use the param like in the code above, I cannot log in using the password set in params[:password]. I have tried forcing the encoding of the string, and to use user.update_without_password, but to no avail.
Would anybody have any idea about this? it's driving me bonkers!
here is a dump of the params hash:
{"username"=>"testytest",
"first_name"=>"Test",
"last_name"=>"testy",
"password"=>"password",
"password_confirmation"=>"password"}
To be a bit more precise, trying to login once this has been fired results in me not being able to login using the old password, or the new one. if there was a way to see which password gets saved in that function, I should be able to debug it!
Well, after 3 days of struggle I finally came up with a solution, however it's pretty ugly: what I did was this (I've added a method to the user model to simplify):
def update_password(new_password)
password_forced = ""
new_password.each_byte do |byte|
password_forced << byte.chr
end
self.password = password_forced
self.save
end
Again, inspecting the string coming from the params didn't point to anything that might be different from another ruby string... but anyway, this works!
Related
I have just run into a situation where I had to tack down why my test user could not login in a system test.
It turns out that the password word for the user was nil.
I ran binding.pry after a user is created:
it 'some tests do
user = create(:user)
binding.pry
end
user.password = '12345' # correct
User.last.password = nil # wtf
user.email = 'joe#example.com' #correct
User.last.email = 'joe#example.com' #correct
Does anyone know why passwords are not persisted into the database with FactoryBot?
The reason User.last.password is nil is because the plain text password is encrypted and not accessible. Check your schema.rb file...you should only see an encrypted_password column (I'm assuming you are using Devise).
To check if the User is persisted just check user.persisted?, user.errors, or something of the sort to figure out whats going on.
I have a relatively simple Rails app with a form using the Devise gem. The form does not require username but does require email address. Everything works fine and the page directs the user to the next page when their email address is all lowercase. However, when the user enters an email address with an uppercase character in the string, I receive the following error from Rails:
NoMethodError at /users
undefined method `uuid' for nil:NilClass
RegistrationsController#create
app/controllers/registrations_controller.rb, line 81
On line 81 (email: new_user.email,):
account = Account.create(user: resource)
new_user = User.find_by_email(params[:user][:email])
recurly_account = Recurly::Account.create(
account_code: new_user.uuid,
email: new_user.email,
first_name: new_user.first_name,
last_name: new_user.last_name
)
sign_in new_user
Request Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"ySBGjHGNwiyeBqGh9nv0N5a8fIJO51bi1nIByev6a21Zh+ncMx5d99cTGKbKWpPvdYmGiBfLX9Gya0qmglrBWg==",
"plan"=>"10day-fmf",
"disc"=>"firstmonthfree10day",
"user"=>{"first_name"=>"asdf", "last_name"=>"asdfsadf", "email"=>"Fjsdlkjs#FLFklsd.com", "password"=>"lsdkjlfkjlskjdf"},
"commit"=>"TRY RISK FREE!",
"controller"=>"registrations",
"action"=>"create"}
--note the email address with uppercase letters
I have researched this problem extensively and found that it has something to do with the email address not being downcased properly. Thus, based on my research I added the following code to various files:
# Devise.rb
config.case_insensitive_keys = [:email]
# User.rb
def downcase_email
self.email.downcase!
end
#registrations_controller.rb
def check_email
email = params[:email].downcase
check = User.where(email: email).first
begin
check_recurly = Recurly::Account.find(email)
if check_recurly
recurly_email = "taken"
end
rescue Recurly::Resource::NotFound => e
recurly_email = "available"
end
However, despite all my attempts at downcasing the email addresses inputted by the user, uppercase letters still cause errors! Any help anyone could give me would be much appreciated!
Thanks
Try to add .downcase here:
new_user = User.find_by_email(params[:user][:email].downcase)
I suppose that in database you have stored lowercase email value, but in controller in params[:user][:email] you have "Fjsdlkjs#FLFklsd.com". Thats why User.find_by_email(params[:user][:email]) returns nil, and you cant call uuid method on a nil object.
Although, I already marked the correct answer above, I wanted to mention that a little javascript onchange event also fixed the problem as well!
<%= f.input_field :email, placeholder: "Email", id:"email", onchange: "this.value=this.value.toLowerCase();" %>
Why are you checking manually if the email already exists? Devise normally do that for you. (did you specify the good user table name?
To lower the case in your controller to lower the params you can do
before_action :downcase_email, if: -> { params[:user][:email].present? }
def downcase_email
params[:user][:email].downcase!
end
Btw you could refacto this method check_email into:
def check_email
return unless params[:email].present?
user = User.find_by_email(params[:email].downcase)
return 'taken' if Recurly::Account.find(user)
'available'
end
Something like that. You don't need to use begin/rescue
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.
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
I have an app which connects to an iphone app, which in turn authenticates it's users via http_digest.
I'm using authlogic, and in my schema users of the website are "users" and users of the phone app are "people". So, i have user_sessions and people_sessions. To handle the http_digest auth, i'm using the authenticate_or_request_with_http_digest method like this:
def digest_authenticate_person
authenticate_or_request_with_http_digest do |email, password|
#ldb is just a logging method i have
ldb "email = #{email.inspect}, password = #{password.inspect}"
person = Person.find_by_email(email)
if person
ldb "Authentication successful: Got person with id #{person.id}"
#current_person_session = PersonSession.create(person)
else
ldb "Authentication failed"
#current_person_session = nil
end
return #current_person_session
end
end
I can see in the logs that password is nil: only email is passed through to the inside of the authenticate_or_request_with_http_digest block.
Im testing this with a curl call like so:
curl --digest --user fakename#madeup.xyz:apass "http://localhost:3000/reports.xml"
I'd expect "fakename#madeup.xyz" and "apass" to get passed through to the inside of the block. Once i have the password then i can use a combination of email and password to find (or not) a user, in the normal way. Does anyone know how i can get access to the password as well?
grateful for any advice - max
EDIT - on further googling, i think i'm using this method wrong: i'm supposed to just return the password, or the crypted password. But then how do i compare that against the password passed as part of the http_digest username?
Found the answer: i had a fundamental misunderstanding of how authenticate_or_request_with_http_digest works: after reading the documentation (in the source code of the gem) i realised that the purpose of this method is not to do the authentication, its purpose is to provide the "email:realm:password" string to the browser, let the browser encrypt it, and check the result against it's own calculated (or cached) version of this.
Here's how i set it up:
def current_person
if #current_person
#current_person
else
load_current_person
end
end
#use in before_filter for methods that require an authenticated person (mobile app user)
def require_person
unless current_person
redirect_to root_path
end
end
def load_current_person
#check user agent to see if we're getting the request from the mobile app
if request.env['HTTP_USER_AGENT'] =~ /MobileAppName/
result = digest_authenticate_person
if result == 401
return 401
elsif result == true
#make authlogic session for person
#current_person_session = PersonSession.new(#person_from_digest_auth)
#current_person = #person_from_digest_auth
end
end
end
#this method returns either true or 401
def digest_authenticate_person
authenticate_or_request_with_http_digest(Person::DIGEST_REALM) do |email|
person = Person.find_by_email(email)
#result = nil
if person
#need to send back ha1_password for digest_auth, but also hang on to the person in case we *do* auth them successfully
#person_from_digest_auth = person
#result = person.ha1_password
else
#person_from_digest_auth = nil
#result = false
end
#result
end
end