I am trying to do two things:
1) Change the default "edit user form" - provided with devise - to remove "password" and allow the other fields to be updated without having to enter a password ie remove the default validation for password.
2) Create a separate form for changing password
I have got everything to work, there is only one problem, in the separate form for updating password, I have included a field for current password. When using the form, no validation is made for current password, so I changed
@user.update_attributes(params[:user])
to
@user.update_with_password(params[:user])
This worked, however it raised another issue. Back in the main form with all the other details except password, the form now asks for a "current password". How can I achieve this without a validation for current password being called on the main form?
here is my registrations controller:
def update
@user = User.find(current_user.id)
if @user.update_attributes(params[:user])
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in @user, :bypass => true
redirect_to after_update_path_for(@user)
else
clean_up_passwords(resource)
respond_with_navigational(resource) do
if params[:change_password] # or flash[:change_password]
render :change_password
else
render :edit
end
end
end
end
Thanks!
I have found a solution to the problem (albeit a very messy one):
def update
@user = User.find(current_user.id)
if params[:user][:password].blank?
if @user.update_attributes(params[:user])
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in @user, :bypass => true
redirect_to after_update_path_for(@user)
else
respond_with_navigational(resource) do
render :edit
end
end
else
if @user.update_with_password(params[:user])
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in @user, :bypass => true
redirect_to after_update_path_for(@user)
else
clean_up_passwords(resource)
respond_with_navigational(resource) do
render :change_password
end
end
end
Can you suggest a better solution?
The accepted answer does not fully address the question. Which, I believe is to have a separate form for user profile attributes (like email, first name, etc) vs. the password. Here's what you need to do for that:
First, leverage the Devise::RegistrationsController for your profile updates.
password
and password_confirmation
fields. Devise ignores these if they are not present in the put.Second, create your own controller to manage the password updates and your own helper to require current_password
, password
, and password_confirmation
on update.
class PasswordsController < ApplicationController
before_filter :authenticate_user!
def edit
@user = current_user
end
def update
@user = User.find(current_user.id)
if @user.update_password_with_password(user_params)
# Sign in the user by passing validation in case their password changed
sign_in @user, :bypass => true
redirect_to edit_password_path, flash: { success: "Successfully updated password" }
else
render "edit"
end
end
private
def user_params
params.require(:user).permit(:current_password, :password, :password_confirmation)
end
end
Here's the helper, update_password_with_password
that will require the new password fields.
class User < ActiveRecord::Base
def update_password_with_password(params, *options)
current_password = params.delete(:current_password)
result = if valid_password?(current_password)
update_attributes(params, *options)
else
self.assign_attributes(params, *options)
self.valid?
self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
false
end
clean_up_passwords
result
end
end