1

I am following this solution: Rails 4 - Allow password change only if current password is correct

But my password updates regardless if I input the correct current password or not. Here is my code:

Employee model:

class Employee < ActiveRecord::Base
    attr_accessor :password, :current_password

    def self.authenticate(user, password)  
        employee = find_by_code(user)
        if employee && employee.password_hash == BCrypt::Engine.hash_secret(password, employee.password_salt)  
            employee  
        end  
    end  

    validates_presence_of :current_password, if: :validate_password?, on: :update
    validate :current_password_is_correct, if: :validate_password?, on: :update

    def current_password_is_correct
        if Employee.authenticate(code, current_password) == false 
            errors.add(:current_password, "Wrong password.")
        end
    end

    def validate_password?
        !password.blank?
    end
end

If I change the current_password_is_correct to this it properly shows the error:

def current_password_is_correct
    if Employee.authenticate(code, current_password) == false || true
        errors.add(:current_password, "Wrong password.")
    end
end

Which makes me think that probably the password is updated before this validation is executed. How can I be sure of this, and if it is so, how can I make it execute in the correct order?

Thanks

Community
  • 1
  • 1
Fermin
  • 473
  • 1
  • 7
  • 19

3 Answers3

1

This method

def self.authenticate(user, password)  
    employee = find_by_code(user)
    if employee && employee.password_hash == BCrypt::Engine.hash_secret(password, employee.password_salt)  
        employee  
    end  
end  

returns nil if it doesn't match the employee. When you test the results of it, here:

def current_password_is_correct
    if Employee.authenticate(code, current_password) == false 
        errors.add(:current_password, "Wrong password.")
    end
end

you specifically test if the result == false. nil does not equal false, and neither does an employee object, so this test will always return false, and never add the errors. I would change this method to:

def current_password_is_correct
  unless Employee.authenticate(code, current_password)
    errors.add(:current_password, "Wrong password.")
  end
end

The "unless" case will be triggered by anything "falsy" which includes false or nil.

Max Williams
  • 32,435
  • 31
  • 130
  • 197
  • Also, refactor your `authenticate` method as per @jonsnow's advice as it's a bit cleaner. Otherwise you're being caught out by the fact that an if block returns nil if it's test doesn't pass. – Max Williams Oct 13 '15 at 10:52
  • Thanks your advice was what I needed – Fermin Oct 14 '15 at 06:18
1

change your method like this, And check the same

def self.authenticate(user, password)  
    employee = find_by_code(user)
    employee && employee.password_hash == BCrypt::Engine.hash_secret(password, employee.password_salt)
end
jon snow
  • 3,062
  • 1
  • 19
  • 31
0

In Devise gem, you can use update_with_password method to ask the current password using st

Its works on rails 4 and 5.

def update_password
    @user = User.find(current_user.id)
    if @user.update_with_password(password_params)
    end 
end

private
def password_params
    params.require(:user).permit(:password, :password_confirmation, :current_password)
end
Marcelo Austria
  • 861
  • 8
  • 16