Override PasswordsController#update method

2,348 views
Skip to first unread message

Иван Бишевац

unread,
Aug 28, 2013, 11:48:35 AM8/28/13
to plataforma...@googlegroups.com
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)
      
    unless self.resource.first_security_question_answer == params[resource_name][:first_security_question_answer]
      self.resource.errors.add(:first_security_question_answer, "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?

and...@benjamin.dk

unread,
Sep 7, 2013, 6:41:18 PM9/7/13
to plataforma...@googlegroups.com
What do you mean by its value is fake? is it the string "fake"? it seems you have to override the method to do what you want. But first you need to explain what exactly is the error u getting.

Иван Бишевац

unread,
Sep 8, 2013, 7:19:41 AM9/8/13
to plataforma...@googlegroups.com
I mean "fake" is string in database. It can be anything else different from what I entered on update form.

In meanwhile I worked on it and realized that I don't wan't to reset password before I check if security question is correct. So I have to override reset_password_by_token method (https://github.com/plataformatec/devise/blob/master/lib/devise/models/recoverable.rb#L114).
But when I override it in resource model (in my case user) then it raises error that there is no method token_generator.

I don't know how to override it.


2013/9/8 <and...@benjamin.dk>
What do you mean by its value is fake? is it the string "fake"? it seems you have to override the method to do what you want. But first you need to explain what exactly is the error u getting.

--
 
---
You received this message because you are subscribed to a topic in the Google Groups "Devise" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/plataformatec-devise/HWqOvcKuYfY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to plataformatec-de...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

André Orvalho

unread,
Sep 8, 2013, 11:26:40 AM9/8/13
to plataforma...@googlegroups.com
OK I have a few questions for you:

1. What is the Ruby, rails and devise version you using.

2. What is exactly you want to achieve? step by step like this:

  " 1. I want a user to click link recover password when he wants a new password
    2. then I show a form where he puts the email
    3. then I show another form where he puts the recover question
    4. then he receives and email"

Its Hard to help you when I dont know exactly what you want to do.

3. Can you show me the code you have done in your model for reset_password_by_token and what you do to obtain the error and the stack trace of error: maybe use a gist do it(https://gist.github.com/)

Иван Бишевац

unread,
Sep 8, 2013, 1:48:49 PM9/8/13
to plataforma...@googlegroups.com
1. Here it is:
biske@biske:~$ ruby -v
ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-linux]

biske@biske:~$ rails -v
Rails 4.0.0

biske@biske:~$ gem list devise

*** LOCAL GEMS ***

devise (3.0.3)

2. I already wrote that I want to add some fields on edit password page and want to check if they match values in database. Let's simplify this:

Scenario: Reset password
Given I am registered user
When I go to new password page
And I fill in "user_email" with "f...@bar.com"
And I press "Send me reset password instructions" button
Then "f...@bar.com" should get an email
When I open email
And I press "Change my password" button
Then I should be redirected to "edit password page"
When I fill in "user_email" with "FooBar"
And I fill in "user_email_confirmation" with "FooBar"
And I fill in "user_name" with "TestTest"
And I press "Change my password" button
Then I should see "Your password was changed successfully. You are now signed in." message

3. I override PasswordsController
class PasswordsController < Devise::PasswordsController
  # GET /resource/password/edit?reset_password_token=abcdef
  def edit
    if resource.nil?
      self.resource = resource_class.new
      resource.reset_password_token = params[:reset_password_token]
    end
  end

  # PUT /resource/password
  def update
    self.resource = resource_class.reset_password_by_token(resource_params)

    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
      respond_with resource
    end
  end
end

I want to check if user answered security questions and I think reset_password_by_token should be overriden. I tried it into model:

class User < ActiveRecord::Base
  .....
  .....

def self.reset_password_by_token(attributes={})
          original_token = attributes[:reset_password_token]
          reset_password_token = Devise.token_generator.digest(self, :reset_password_token, original_token)

          recoverable = find_or_initialize_with_error_by(:reset_password_token, reset_password_token)

          if recoverable.persisted?
            if recoverable.reset_password_period_valid?
# custom logic for checking security question
              recoverable.reset_password!(attributes[:password], attributes[:password_confirmation])
            else
              recoverable.errors.add(:reset_password_token, :expired)
            end
          end

          recoverable.reset_password_token = original_token
          recoverable
        end
end

But it raises error:

undefined method `token_generator' for Devise:Module

Here is complete stack trace: https://gist.github.com/biske/6486694


Here is github repository:


2013/9/8 André Orvalho <and...@benjamin.dk>

--

André Orvalho

unread,
Sep 8, 2013, 3:56:23 PM9/8/13
to plataforma...@googlegroups.com
OK,

So I am sorry to ask you so many details but I think I found your mistake. For your version of devise 3.0.3 the code for reset_password_by_token() is the following:

https://gist.github.com/andreorvalho/6487893 as you can see here(lines 129 -139 https://github.com/plataformatec/devise/blob/v3.0/lib/devise/models/recoverable.rb#L124) So I am guessing that really ins't a method called `token_generator' for Devise:Module as you call in your version when overwriting the method.

Everything else seems you doing correctly. let me know if it helped :)

Иван Бишевац

unread,
Sep 9, 2013, 2:37:29 AM9/9/13
to plataforma...@googlegroups.com
Yes, that's it, obviously I missed up version. Now I just have to do comparing in this method.

Thanks.


2013/9/8 André Orvalho <and...@benjamin.dk>

--
Reply all
Reply to author
Forward
0 new messages