Recuperar contraseñas con restful_authentication

Continuación del post anterior para recuperar las contraseñas olvidadas. Crear una migration para añadir un campo reset_code al modelo User

$  script/generate migration password_reset_code

class PasswordResetCode < ActiveRecord::Migration
  def self.up
    add_column "users", "password_reset_code", :string, :limit => 40
  end

  def self.down
    remove_column "users", "password_reset_code"
  end
end

Añadir los siguientes métodos a app/models/user.rb

   def forgot_password
     @forgotten_password = true
     self.make_password_reset_code
   end

   def reset_password
     # First update the password_reset_code before setting the
     # reset_password flag to avoid duplicate email notifications.
     update_attributes(:password_reset_code => nil)
     @reset_password = true
   end

   def recently_reset_password?
     @reset_password
   end

   def recently_forgot_password?
     @forgotten_password
   end

  protected

  def make_password_reset_code
     self.password_reset_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
    end

Crear los modelos user_notifier.rb y user_observer.rb

# app/models/user_observer.rb
class UserObserver < ActiveRecord::Observer
 
  def after_save(user)
    UserNotifier.deliver_forgot_password(user) if user.recently_forgot_password?
    UserNotifier.deliver_reset_password(user) if user.recently_reset_password?
  end
end

# app/models/user_notifier.rb
class UserNotifier < ActionMailer::Base

  def forgot_password(user)
    setup_email(user)
    @subject    += 'Contraseña olvidada'
    @body[:url]  = "#{HOST}/passwords/reset_password/#{user.password_reset_code}"
  end

  def reset_password(user)
    setup_email(user)
    @subject    += 'Su contraseña ha sido reseteada.'
  end

  protected
  def setup_email(user)
    @recipients  = "#{user.email}"
    @from        = "notifier@midominio.com"
    @subject     = "[midominio.com] "
    @sent_on     = Time.now
    @body[:user] = user
  end
end

En app/views/user_notifier/ crear los ficheros forgot_password.html.erb y reset_password.html.erb

# app/views/user_notifier/forgot_password.html.erb
<?php
= @user.login
?>
, pinche en el enlace para resetear su contraseña
<?php
= @url
?>
# app/views/user_notifier/reset_password.html.erb
<?php
= @user.login
?>
, su contraseña ha sido reseteada.

En config/environment.rb añadir el observer:

# Activate observers that should always be running
# config.active_record.observers = :cacher, :garbage_collector
config.active_record.observers = :user_observer

Crear un controlador para gestionar las contrseñas olvidadas y los reseteos:

# app/controllers/passwords_controller.rb
class PasswordsController < ApplicationController
  
  skip_filter :login_required
  
  def forgot_password
    return unless request.post?
    if @user = User.find_by_email(params[:email])
      @user.forgot_password
      @user.save
      redirect_to(login_path)
      flash[:notice] = "Hemos enví­ado un enlace para resetear la contraseña a su correo electrónico."
    else
      flash[:error] = "No se ha encontrado ningún usuario con esa dirección de correo electrónico."
    end
  end

  def reset_password
    return unless request.post?
    @user = User.find_by_password_reset_code(params[:id]) if params[:id]
    if @user.nil?
      flash[:error] = "Código inválido."
      return
    end
    return if @user unless params[:password]
    begin
      if (params[:password] == params[:password_confirmation])
        @user.password_confirmation = params[:password_confirmation]
        @user.password = params[:password]             
        @user.reset_password
        @user.save!
        flash[:notice] = "Su contraseña ha sido reseteada, ya puede entrar con la nueva contraseña."
        redirect_back_or_default(login_path)                 
      else
        flash[:error] = "Las contraseñas no coinciden."
      end
   
      rescue ActiveRecord::RecordInvalid
        flash[:error] = "Por favor introduzca información válida."
      end
    end

end

En app/views/passwords/

# app/views/passwords/forgot_password.html.erb
<p>¿Olvidó su contraseña? </p>
<% form_tag url_for(:action => 'forgot_password') do %>
    <p><label for="email">Correo electrónico: </label><br />
    <%= text_field_tag :email, "", :size => 50 %></p>
    <p><%= submit_tag 'Recuperar contraseña' %></p>
<% end %>

# app/views/passwords/reset_password.html.erb
<p>Resetear contraseña</p>
<% form_tag url_for(:action => "reset_password") do %>
    <p><label for="password">Contraseña: </label><br />
    <%= password_field_tag :password %></p>
    <p><label for=":password_confirmation">Confirmar contraseña</label><br />
    <%= password_field_tag :password_confirmation %></p>
    <p><%= submit_tag "Resetear contraseña" %></p>   
<% end %>

Comentarios

Gracias por el codigo, me hacia falta la manera de recuperar contraseñas con el plugin. Me ahorraron un trabajito :D Saludos.

Thank you! Your code save me a few hours of work!

Felicitaciones, gracias por esta publicación. Sin embargo tengo una pregunta, si el usuario olvido su contraseña de acceso apenas quiera entrar en la pantalla de login deberia estar esta opción, pregunto ¿Comó haria para que entre directamente solo preguntando el login y luego a la opción ¿olvidó la contraseña? porque lo intento pero no me direcciona a la opcion forgot_password en el log visualizo lo siguiente:

Processing EmpleadosController#show (for 127.0.0.1 at 2009-11-19 15:31:27) [GET] Parameters: {"action"=>"show", "id"=>"forgot_password", "controller"=>"empleados"} Redirected to http://localhost:3008/sesiones/new Filter chain halted as [:login_required] rendered_or_redirected. Completed in 1ms (DB: 1) | 302 Found [http://localhost/empleados/forgot_password] SQL (0.3ms) SET client_min_messages TO 'panic' SQL (0.3ms) SET client_min_messages TO 'notice'

Processing SesionesController#new (for 127.0.0.1 at 2009-11-19 15:31:27) [GET] Parameters: {"action"=>"new", "controller"=>"sesiones"} Rendering template within layouts/bienvenida Rendering sesiones/new Completed in 5ms (View: 2, DB: 1) | 200 OK [http://localhost/sesiones/new]

Añadir nuevo comentario