title: Rails 4 - Allow password change only if current password is correct
body: <p>In my app, users can edit their profile information. On the edit profile form, the user can make changes to all fields (name, title, and more). On this same form are three fields: <code>current_password</code>, <code>password</code>, and <code>password_confirmation</code>. I am using <code>bcrypt</code>'s <code>has_secure_password</code> feature for password authentication. I am NOT using Devise at all.</p>
  
  <p>I want users to only be able to change their password if they have supplied a correct current password. I've got this working before with the following code in the <code>update</code> method of my Users controller:</p>
  
  <pre><code># Check if the user tried changing his/her password and CANNOT be authenticated with the entered current password
  if !the_params[:password].blank? &amp;&amp; !@user.authenticate(the_params[:current_password])
    # Add an error that states the user's current password is incorrect
    @user.errors.add(:base, "Current password is incorrect.")
  else    
    # Try to update the user
    if @user.update_attributes(the_params)
      # Notify the user that his/her profile was updated
      flash.now[:success] = "Your changes have been saved"
    end
  end
  </code></pre>
  
  <p>However, the problem with this approach is that it discards all changes to the user model if just the current password is incorrect. I want to save all changes to the user model but NOT the password change if the current password is incorrect. I've tried splitting up the IF statements like so:</p>
  
  <pre><code># Check if the user tried changing his/her password and CANNOT be authenticated with the entered current password
  if !the_params[:password].blank? &amp;&amp; !@user.authenticate(the_params[:current_password])
    # Add an error that states the user's current password is incorrect
    @user.errors.add(:base, "Current password is incorrect.")
  end
  
  # Try to update the user
  if @user.update_attributes(the_params)
    # Notify the user that his/her profile was updated
    flash.now[:success] = "Your changes have been saved"
  end
  </code></pre>
  
  <p>This doesn't work because the user is able to change his/her password even if the current password is incorrect. When stepping through the code, although the "Current password is incorrect." error is added to <code>@user</code>, after running through the <code>update_attributes</code> method, it seems to ignore this error message.</p>
  
  <p>By the way, the <code>current_password</code> field is a virtual attribute in my User model:</p>
  
  <pre><code>attr_accessor :current_password
  </code></pre>
  
  <p>I've been stuck trying to figure this out for a couple of hours now, so I can really use some help.</p>
  
  <p>Thanks!</p>
  
  <hr>
  
  <h2>Solution</h2>
  
  <p>Thanks to <a href="https://stackoverflow.com/users/544825/papirtiger">papirtiger</a>, I got this working. I changed the code around a little bit from his answer. Below is my code. Note that either code snippet will work just fine.</p>
  
  <p><strong>In the User model (user.rb)</strong></p>
  
  <pre><code>class User &lt; ActiveRecord::Base
    has_secure_password
  
    attr_accessor :current_password
  
    # Validate current password when the user is updated
    validate :current_password_is_correct, on: :update
  
    # Check if the inputted current password is correct when the user tries to update his/her password
    def current_password_is_correct
      # Check if the user tried changing his/her password
      if !password.blank?
        # Get a reference to the user since the "authenticate" method always returns false when calling on itself (for some reason)
        user = User.find_by_id(id)
  
        # Check if the user CANNOT be authenticated with the entered current password
        if (user.authenticate(current_password) == false)
          # Add an error stating that the current password is incorrect
          errors.add(:current_password, "is incorrect.")
        end
      end
    end
  end
  </code></pre>
  
  <p>And the code in my Users controller is now simply:</p>
  
  <pre><code># Try to update the user
  if @user.update_attributes(the_params)
    # Notify the user that his/her profile was updated
    flash.now[:success] = "Your changes have been saved"
  end
  </code></pre>