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

Était-ce utile?

La 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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top