Question

I've added some parameters to a small User class using the Devise gem and am having some trouble with current_password. On my account update form, I receive the error "Can't be blank" when I update an account and type in the current password. The account is subsequently not updated. I suspect it is being sanitized somewhere which deletes the input and then is read as blank. However I am not sure. I have included anything I thought to be relevant below.

User model:

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  attr_accessible :username, :email, :password, :password_confirmation, :remember_me, :about_me

  has_many :microposts
end

Application controller:

class ApplicationController < ActionController::Base
  protect_from_forgery

  protected
  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email)}
    devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email)}
    devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:username, :about_me, :email, :password, :password_confirmation, :current_password)}
  end
end

password controller:

class Users::PasswordsController < Devise::PasswordsController

  def resource_params
    params.require(:user).permit(:email, :password, :password_confirmation, :current_password)
  end
  private :resource_params
end

Registration controller

class Users::RegistrationsController < Devise::RegistrationsController

  before_filter :configure_permitted_parameters

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) do |u|
      u.permit(:username, :about_me, :email, :password, :password_confirmation)
    end
    devise_parameter_sanitizer.for(:account_update) do |u|
     u.permit(:username, :about_me, :email, :password, :password_confirmation, :current_password)
    end
  end

end

routes for devise:

  devise_for :users, :controllers => {:registrations => "users/registrations", :passwords => "users/passwords" }

the view in question (standard devise /registrations/edit form):

<h2>Edit <%= resource_name.to_s.humanize %></h2>

<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
  <%= f.error_notification %>

  <div class="form-inputs">
    <%= f.input :email, required: true, autofocus: true %>
    <%= f.input :username, required: false, autofocus: true %>
    <%= f.input :about_me, required: false, autofocus: true %>
    <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
      <p>Currently waiting confirmation for: <%= resource.unconfirmed_email %></p>
    <% end %>

    <%= f.input :password, autocomplete: "off", hint: "leave it blank if you don't want to change it", required: false %>
    <%= f.input :password_confirmation, required: false %>
    <%= f.input :current_password, hint: "we need your current password to confirm your changes", required: true %>
  </div>

  <div class="form-actions">
    <%= f.button :submit, "Update" %>
  </div>
<% end %>

<h3>Cancel my account</h3>

<p>Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p>

Again, the problem is that when the password to confirm changes is typed in, the error message "can't be blank" appears next to the field. Any ideas? Thank you

Was it helpful?

Solution

This is an issue I've had trouble in the past with, and something that requires a little bit of a work-around. By default, Devise's :registerable module allows a user to change their information, and requires the :password and :password_confirmation parameters to be entered. As I understand it, you're trying to require the user to enter and confirm their :current_password.

In order to implement the behavior you're looking for, you'll have to override Devise's RegistrationsController. I also don't think you'll need the PasswordsController, since Devise's RegistrationsController handles that for you. You'll need to write and implement a method that checks the validity of the user's :current_password, and then redirects them to the right places via the update action. You can write the following private method in your Users::RegistrationController class:

def needs_password?(user, params)
  user.email != params[:user][:email] || 
   params[:user][:password].present? || 
   params[:user][:password_confirmation].present?
end

Then revise your update method in User::RegistrationsController as follows:

class Users::RegistrationsController < Devise::RegistrationsController
  def update
    @user = User.find(current_user.id)

    successfully_updated = if needs_password?(@user, params)
      @user.update_with_password(devise_parameter_sanitizer.sanitize(:account_update))
    else
      # remove the virtual current_password attribute
      # update_without_password doesn't know how to ignore it
      params[:user].delete(:current_password)
      @user.update_without_password(devise_parameter_sanitizer.sanitize(:account_update))
    end

    if successfully_updated
      set_flash_message :notice, :updated
      # Sign in the user bypassing validation in case their password changed
      sign_in @user, :bypass => true
      redirect_to after_update_path_for(@user)
    else
      render "edit"
    end
  end

Hopefully that will help. You can also refer to Devise's documentation on how to do this: https://github.com/plataformatec/devise/wiki/How-To%3a-Allow-users-to-edit-their-account-without-providing-a-password.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top