t56k
t56k

Reputation: 6981

Devise: allow users to reset password only

I've got a pretty standard User model with Devise. Administrators are controlled with an :admin boolean on the model, and users who aren't administrators cannot manage themselves (i.e., only administrators can make changes to users).

What I'd like is to permit users to reset their password if they forget it. They would enter their email address then be emailed a token which would grant them access to change it. I've started a solution but I really don't know how to proceed.

user.rb

class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
  attr_accessor :current_password
  attr_accessible :name, :password, :password_confirmation, :current_password, :email, :remember_me, :ftp, :colour1, :colour2, :logo, :logo2, :address, :url, :disclosure, :general_advice, :facebook, :twitter, :brand_id, :analytics
  attr_protected :admin
  validates_uniqueness_of :email, :ftp
  belongs_to :brand
  has_many :docs
  has_many :orders, :through => :docs
  has_attached_file :logo
  has_attached_file :logo2
end

class Ability
  include CanCan::Ability
  def initialize(user)
    user ||= User.new #guesting
    if user.admin?
      can :manage, :all
    else
      can :manage, :recoverable
    end
  end
end

And here's the method:

def reset
  @idiot = User.where(:email => params[:email]).first
    unless @idiot.nil?
      Notifier.send_reset_notice(@idiot).deliver
      @idiot.send_reset_password_instructions
      redirect_to new_user_session_url
    else
      redirect_to new_user_session_url, :flash => { :error => "That email address matches no user." }
    end
end

The user receives the Devise email but clicking on the link takes the user to the application root and not to a password reset form. I'm not sure how to proceed from here. Any thoughts? Cheers!

UPDATE

Relevant routes:

devise_for :users, :path => "d"
devise_scope :user do
  get '/sign_in' => 'devise/sessions#new'
  get '/sign_out' => 'devise/sessions#destroy'
end

Upvotes: 4

Views: 9737

Answers (1)

rails_id
rails_id

Reputation: 8220

First, create controller for handle Devise::PasswordsController

class PasswordusersController < Devise::PasswordsController 
  prepend_before_filter :require_no_authentication
  append_before_filter :assert_reset_token_passed, :only => :edit

  def new
    super
  end

  def create
    self.resource = resource_class.send_reset_password_instructions(resource_params)

    if successfully_sent?(resource)
      redirect_to root_path, :notice => "Instruction has been send to your email"
    else
      respond_with(resource)
    end
  end

  def edit
    super
  end

  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)
      redirect_to login_path, :notice => "Password has been change"
    else
      respond_with resource
    end
  end

  protected

    def after_sending_reset_password_instructions_path_for(resource_name)
      root_path
    end

    def assert_reset_token_passed
      super
    end

    def unlockable?(resource)
      super
    end

end

Than, run generate view for devise

copy new.html.erb and edit.html.erb from views/devise/passwords to views/passwordusers

on routes.rb you can config such as :

devise_scope :user do
get '/reset_password' => "passowrdusers#new", :as => :reset_password
get '/new_password' => "passwordusers#edit", :as => :new_password
end

edit link on devise/mailer/reset_password_instructions.html.erb

<p><%= link_to 'Change My Password', new_password_path(@resource, :reset_password_token => @resource.reset_password_token) %></p>

finally, on view form login add this

<%= link_to "Forgot Password?", reset_password_url(resource_name) %>

UPDATE

on routes.rb you can config such as :

devise_scope :user do
get '/reset_password' => "passowrdusers#new", :as => :reset_password
get '/new_password' => "passwordusers#edit", :as => :new_password
post '/send_email' => 'passwordusers#create', :as => :create_password
put '/change' =>  'passwordusers#update', :as => :update_password
end

on views/passwordusers/new.html.erb

  <%= form_for("user", :url => create_password_path(resource_name), :html => { :method => :post }) do |f| %>

on views/passwordusers/edit.html.erb

<%= form_for("user", :url => update_password_path(resource_name), :html => { :method => :put }) do |f| %>

Upvotes: 4

Related Questions