Devise update password

1.1k views Asked by At

I want to change edit password process. On edit password page I have one field :foo and I want to check if user correctly entered it.

I created PasswordsController and override update method, where I added code for testing if :foo is same as in database:

def update
    self.resource = resource_class.reset_password_by_token(resource_params)

    # this is code that I added
    unless self.resource.foo == params[resource_name][:foo]
      self.resource.errors.add(:foo, "Foo not correct")
    end

    if resource.errors.empty?
      resource.unlock_access! if unlockable?(resource)
      flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
      set_flash_message(:notice, flash_message) if is_navigational_format?
      sign_in(resource_name, resource)
      respond_with resource, :location => after_resetting_password_path_for(resource)
    else
      binding.pry
      respond_with resource
    end
  end

Suppose I enter "test" in :foo field, but in database it's value is "fake". It will print error on view ("Foo not correct") but it will populate :foo field with value from database ("fake") not with value I entered ("test").

I should redefine reset_password_by_token or use my custom method, but wonder if there is more elegant way to solve this problem?

2

There are 2 answers

0
Nick Veys On

Try using a before_action (may be before_filter, depending on your Rails version):

class PasswordsController < ...

  before_action :check_foo, :only => :update

  ...

  private

    def check_foo
      # get resource
      # check foo
      # redirect if foo is not valid
    end

end

You could also probably change this to just extend the existing RegistrationsController. Just re-use the existing Devise implementation of update without re-implementing it, just adding the filter in front.

To prevent updating the :foo on the object, filter the params hash, removing the :foo field, before passing it to reset_password_by_token.

def remove_foo(hash)
    hash.delete_if { |k, v| k == :foo }
end

...

self.resource = resource_class.reset_password_by_token(remove_foo(resource_params))

Or if you're using Rails 4, or Strong Parameters, just don't permit it.

0
donleyp On

Use a different name for the field on the form (let's say 'bar') and then you'd change your test to:

...
unless self.resource.foo == params[resource_name][:bar]
    self.resource.errors.add(:foo, "Foo not correct")
end
...

Note: in the view you won't be able to use the form builder stuff since it will choke if 'bar' isn't a property of the model object, but you can add any markup to the form directly using html or the rails helper methods like FormTagHelper::text_field_tag.