I have a form_for established to edit a resource (here it is a user). In the model, it is specified that some attributes cannot be blank (e.g. password). I have a second form to edit this user as an admin. This form does not require the user password but can be filled to change this user's password. The problem is that the validation fails cause no password is specified (Validation fail: Password cannot be blank).
I'd like to know if there is a way to edit the resource this way, without deleting the password field from parameters when it's blank.
#user.update!(user_params)
You can do something like a param deletetion. In your update method before the save do something like.
if params["user"].has_key? "password"
if params["user"]["password"].empty? and user_is_admin?
params["user"].delete("password")
end
end
Replace user_is_admin? with your own admin checking method.
Related
I have a user model with generate_password action.
When i create a new user, the password is generated automatically and inserted into params like:
"user_params"=>
{"name"=>"name",
"surname"=>"secname",
"password"=>"j6WW9kj6"}
, so user don't need to feel a password field. And as I have a password present validation, rails throw a validation error when I attempt to create new user.
With my understanding, I can say that you are trying to generate a password when the user is created for the first time.
I think for this problem you can use before_create in User model.
before_create :generate_password
The answer is pretty simple. I just should run password_generate method in users_controller, and ActiveRecord does all for me:
#user = User.new user_params
#user.password = #user.generate_password
Thanks.
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.
I have added a custom method to my (Devise) User controller:
def confirm
#user = User.find(params[:id])
#user.skip_confirmation!
respond_to
if #user.save
//display success message
else
// display error message
end
end
end
When I try to confirm a user via that, the save fails and I have two errors in #user.errors: password and password_confirm
Even in the rails console, if I try
#user.skip_confirmation!
#save
I get a rollback and the save fails.
Any ideas would be appreciated..
Additional Info
The problem causing this is the
validates_presence_of :password, :password_confirmation
in my User model. Even though a password and password confirmation was entered by the user on signup, those get hashed together to make the encrypted_password. For example, when I find a user in the console, the user has an encrypted_password, but no password or password_confirmation.
I have tried this
validates_presence_of :password, :password_confirmation, :on => :create
However it doesn't force the user to enter both a password and a confirm on the Change Password screen.
Any idea on how to get around this?
I think you're misunderstanding what skip_confirmation! does.
Devise's confirmable module will send a new user an e-mail with a link back to your app. Until they click that link, they'll be "unconfirmed" and unable to sign in. What skip_confirmation! does is skip this workflow -- the user will be immediately confirmed, and can log in without first having to go through the e-mail confirmation flow.
You're getting the error you're getting because of validations on the user model. In particular, the user you're trying to save doesn't appear to have an existing password, so it's requiring a password and matching password_confirmation attribute.
I suspect that there's a better way to accomplish whatever your purpose is. Why isn't the normal confirmation workflow sufficient? What's this extra controller action trying to accomplish?
I think skip_confirmation! actually does the saving.
What you want to check is probably if #user.persisted?
I'm attempting to display a users password along in his confirmation page sent by the Devise mailer. The confirmation page is the default
Welcome test0#test.com!
You can confirm your account email through the link below:
Confirm my account
However, I wish to have
Welcome test0#test.com!
Your password is currently DASADSADS
You can confirm your account email through the link below:
Confirm my account
How do I access the user object in the view? Do I need to override the mailer controller with a custom one? If so, how do I tell what the methods of the current mailer do (tried looking at documentation but can't find any clues)?
I noticed that #email and #resource are used in the view. Can I use any of these to access the current password in its unhashed form?
Note that I am sending this email manually with user.find(1).send_confirmation_instructions
Although this can be done, I would caution very strongly against doing so. Hashed passwords are specifically used so that the password cannot be recreated easily. Passing the original password back to the user will cause it to be sent back in plain text which sort of defeats the whole purpose. Also, shouldn't the user already know their password (they did type it in twice after all)?!?
To do this, you would need to capture the original (unhashed) password in the registration create action and send the email at that point (passing along the password). You can do this by overriding the sign_up method - you can do this in an initializer:
class Devise::RegistrationsController < DeviseController
def sign_up(resource_name, resource)
sign_in(resource_name, resource)
resource.unhashed_password = resource_params[:password]
resource.send_confirmation_instructions
end
end
Alternatively, you can derive a new controller from Devise::RegistrationsController and put this override code there (the recommended approach - but then again, this whole operation isn't really recommended). You'll need to add the unhashed_password accessor for this to work:
class User < ActiveRecord::Base
attr_accessor :unhashed_password
end
And then you can update your confirmation view (at app/views/devise/mailer/confirmation_instructions.html.erb) to contain this:
<p>Your password is currently <%= #resource.unhashed_password %></p>
Devise save password in encrypted form: You can decrypt it using,
Generate new migration:
$ rails g migration AddLegacyPasswordToUser legacy_password:boolean
invoke active_record
create db/migrate/20120508083355_add_legacy_password_to_users.rb
$ rake db:migrate
Using legacy_password method in following code you can decrypt your password:
class User < ActiveRecord::Base
...
def valid_password?(password)
if self.legacy_password?
# Use Devise's secure_compare to avoid timing attacks
if Devise.secure_compare(self.encrypted_password, User.legacy_password(password))
self.password = password
self.password_confirmation = password
self.legacy_password = false
self.save!
else
return false
end
end
super(password)
end
# Put your legacy password hashing method here
def self.legacy_password(password)
return Digest::MD5.hexdigest("#{password}-salty-herring");
end
end
You can just use request.request_parameters[:user][:password] to get the plain text password on the create or update action.
I have a User model with three different types fields which I would like to be able to update independently from each other
The same page has 3 different forms which act on the same model:
change the user profile image
change the user name / email
change the user password
The reason they are separated:
photo's automatically get uploaded when they are selected (without requiring a password),
name / email can be changed without requiring a password but require submitting a form,
password requires the current password to change it.
Currently in User#update I have a series of if/else branches for logic: if params[:commit] == "Update Password" I update the password, elsif params[:commit] == "Update Info" I update their name / email, etc.
I don't like how long the logic gets, and I don't think its good practice to tie the controller logic into the view (since the logic is based off of params[:commit] text that appears on the submit buttons).
Is there a better way to do this?
To get rid of if..elsif..elsif chain you can split your update action into update_password, update_info, etc and set your form actions accordingly. Of course you will need to update your routes also.
In your controller could you check to see which parameters have been sent and then act appropriately?
if params[:password] && params[:password_confirmation]
#user.password = params[:password]
end
if params[:email]
#user.email = params[:email]
end
if #user.save
etc...
Then you have one route that behaves as expected dependent on what is sent.