In my Rails app, I set up my users table to have a string for :email and :hashed_password. However I want to provide the option to reset the password. Following Railscast 274, I have everything set up, but the actual password isn’t changing to what I reset it as. I thought maybe I’d have to change :password to :hashed_password in my User model where I handle validations but :password works elsewhere so I ruled it out. Can someone help me figure this out?
Here’s my User model:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :email, :password, :password_confirmation
before_save :encrypt_new_password
before_create { generate_token(:auth_token) }
before_validation :downcase_email
has_one :profile, :dependent => :destroy
accepts_nested_attributes_for :profile
validates :email, :uniqueness => true,
:length => { :within => 5..50 },
:format => { :with => /^[^@][\w.-]+@[\w.-]+[.][a-z]{2,4}$/i }
validates :password, :confirmation => true,
:length => { :within => 4..20 },
:presence => true,
:if => :password_required?
def self.authenticate(email, password)
user = find_by_email(email)
return user if user && user.authenticated?(password)
end
def authenticated?(password)
self.hashed_password == encrypt(password)
end
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
def generate_token(column)
begin
self[column] = SecureRandom.hex
end while User.exists?(column => self[column])
end
end
My password_resets_controller:
class PasswordResetsController < ApplicationController
layout "password_reset"
def new
end
def create
user = User.find_by_email(params[:email])
if user
user.send_password_reset
redirect_to new_password_reset_path, :notice => "Check your email for password reset instructions."
else
redirect_to new_password_reset_path, :notice => "Sorry, we couldn't find that email. Please try again."
end
end
def edit
@user = User.find_by_password_reset_token!(params[:id])
session[:user_id] = @user.id
end
def update
@user = User.find_by_password_reset_token!(params[:id])
if @user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert => "Your password reset link has expired."
elsif @user.update_attributes(params[:user])
redirect_to profile_path(@user), :notice => "Great news: Your password has been reset."
else
render :edit
end
end
end
My password_reset form code:
<%= form_for @user, :url => password_reset_path(params[:id]) do |f| %>
<% if @user.errors.any? %>
<div id="error_messages">
<% for message in @user.errors.full_messages %>
<li class="error"><%= message %></li>
<% end %>
</div>
<% end %>
<p class="label"><label for="password">New Password:</label></p>
<p><%= password_field_tag :password, nil, :autofocus => true, :autocomplete => 'off', :placeholder => 'Enter a new password' %></p>
<p class="label"><label for="password_confirmation">Confirm Password:</label></p>
<p><%= password_field_tag :password_confirmation, nil, :autofocus => false, :autocomplete => 'off', :placeholder => 'Reenter your new password' %></p>
<%= submit_tag 'Update Password', :class => 'button orange' %>
<% end %>
My users_controller update action:
def update
@user = current_user
if @user.update_attributes(params[:user])
redirect_to settings_path, :notice => 'Updated user information successfully.'
else
render :action => 'edit'
end
end
My server message when I update my password:
Started POST "/password_resets/56be9c1168637d64eaa42b2551ef9b6c" for 127.0.0.1 at Thu Mar 08 19:36:03 -0500 2012
Processing by PasswordResetsController#update as HTML
Parameters: {"commit"=>"Update Password", "password_confirmation"=>"[FILTERED]", "authenticity_token"=>"N8v69nP9NbaGVGdYwmv6EF8uIp4/Vqld/y+Q2K40CMQ=", "utf8"=>"\342\234\223", "id"=>"56be9c1168637d64eaa42b2551ef9b6c", "password"=>"[FILTERED]"}
It looks like because you’re using
password_field_tag, the parameters are ignored when the user model is updated. Try usingf.password_fieldinstead so that the password params are nested inparams[:user].Alternatively, you could also call
if @user.update_attributes(:password => params[:password], :password_confirmation => params[:password_confirmation])in the PasswordResetsController.