Reputation: 4511
There's a ton of related questions:
- Devise: Update Account without password confirmation
- rails: devise update user without password
- Devise 3 (rails 4) can't update user without password
- etc...
But when I tried to work with their answers I never ended up getting what I wanted so I've decided to post this Q&A style.
Situation:
I was working on a Rails app using devise and I wanted a "Manage Profile form." I wanted one form for updating user information, including email and password, but also first_name, last_name, and other non-devise fields. I only wanted to require the password fields (password and password_confirmation) if the user was trying to change their password. I also wanted to build my form with the form_for helper, and whitelist the fields the user is able to update. And I also wanted all the handy error messages from devise (like, "Your password must be at least 8 characters." or whatever they are).
Upvotes: 2
Views: 2882
Reputation: 6712
If you really want to update the password without the current_password then you need to use the user reset_password
method
@user.reset_password(params[:user][:password], params[:user][:password_confirmation])
Here is a complete working solution to the problem that works in all scenarios:
if params.dig(:user, :password).blank?
updated = @user.update_without_password(params[:user].to_unsafe_hash)
else
if params.dig(:user, :current_password).nil?
@user.reset_password(params[:user][:password], params[:user][:password_confirmation])
updated = @user.update_without_password(params[:user].to_unsafe_hash)
else
updated = @user.update_with_password(params[:user].to_unsafe_hash)
end
bypass_sign_in(@user)
end
Upvotes: 0
Reputation: 4511
TL;DR: Strip params
to only be what you want, I call this user_params
. In the action that processes the form check if user_params[:password]
is empty, and if it is try to update your @user
model with @user.update_without_password(user_params)
if it isn't try to update with @user.update(user_params)
. If the applicable update call returns false
@user.errors
holds the explanation.
Here's exactly how I solved this problem:
I defined a resource
in my config/routes
file:
resource :profile
I made a controller for my resource that extends devise's authenticated controller ProfilesController < AuthenticatedController
for managing profiles. Profiles controller contains several methods
including user_params
which mainly filters params
down to a whitelist:
def user_params
accessible = [
:first_name,
:last_name,
:email,
:password,
:password_confirmation
]
params.require(:user).permit(accessible)
end
and update
which does the business of processing the form:
def update
# current_user holds the logged in user
@user = current_user
# IF they've left the password field blank,
# AND the devise update_without_password method returns true
# OR IF a full update of user (including password and password_confirmation) returns true
# THEN re-sign them in to flush their session, and redirect them back to their dashboard, and send a success message.
# ELSE re-present the edit form they were just on (there's a handy catcher
# in the edit view script to render the form errors, you can find them on
# @user.errors)
if (user_params[:password].blank? && @user.update_without_password(user_params)) || @user.update(user_params)
sign_in(@user, bypass: true)
redirect_to '/dashboard', notice: 'Your profile changes have been saved.'
else
render 'edit'
end
end
There's also of course a view script (a haml one in my case - app/views/profiles/edit.html.haml
) that uses form_for
to render the form:
= form_for current_user, as: :user, url: profile_path, html: { class: '' } do |f|
# [f.label, f.text_field, f.password_field, etc...]
My user model also has all the devise-y goodness included:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :receipts
validate :first_name, presence: true
validate :last_name, presence: true
validate :email, presence: true, email: true
end
Upvotes: 2