public
Description: acts_as_authenticated Ruby on Rails authentication system
Home | Edit | New

Change Password

Here’s a version that might be a little more “ruby” (or at least more relevant to the contemporary Rails universe of resources, etc.) than what’s below.

In account_controller.rb:

def change_password if current_user.authenticated?(params[:old_password]) current_user.password = params[:password] current_user.password_confirmation = params[:password_confirmation] begin current_user.save! flash[:notice] = ‘Successfully changed your password.’ rescue ActiveRecord::RecordInvalid => e flash[:error] = “Couldn’t change your password: #{e}” end else flash[:error] = “Couldn’t change your password: is that really your current password?” end respond_to do |format| format.html { redirect_to :action => “edit” } end end

in the view:

<% form_for( :user, :url => user_change_password_path( @user.id ), :html => { :method => :put }) do |f| %>

<%= password_field_tag “old_password” >
<
= password_field_tag “password” >
<
= password_field_tag “password_confirmation” >
<
= submit_tag “Change Password”%>
<% end %>

And the relevant routing:

map.user_change_password ‘users/:id/change_password’, :controller => ‘users’, :action => ‘change_password’

—Greg

Please correct if this is “not enough ruby”… :)

Controller (account_controller.rb):

def change_password return unless request.post? if User.authenticate(current_user.login, params[:old_password]) if (params[:password] == params[:password_confirmation]) current_user.password_confirmation = params[:password_confirmation] current_user.password = params[:password] flash[:notice] = current_user.save ? “Password changed” : “Password not changed” else flash[:notice] = “Password mismatch” @old_password = params[:old_password] end else flash[:notice] = “Wrong password” end end

View (change_password.rhtml):

<%= start_form_tag >


<= password_field_tag ‘old_password’, @old_password %>


<%= password_field_tag ‘password’ %>


<%= password_field_tag ‘password_confirmation’ %>

<%= submit_tag ‘Change password’ %>

<%= end_form_tag %>

__
added by Railsroad

I’ve created some tests:

def test_should_allow_password_change post :change_password, { :old_password => ‘test’, :password => ‘newpassword’, :password_confirmation => ‘newpassword’ }, { :user =>1 } assert_equal ‘newpassword’, assigns(:current_user).password assert_equal “Password changed”, flash[:notice] post :logout assert_nil session[:user] post :login, :login => ‘bryan’, :password => ‘newpassword’ assert session[:user] assert_response :redirect assert_redirected_to :controller => ‘contract’, :action => ‘index’ end def test_non_matching_passwords_should_not_change post :login, :login => ‘bryan’, :password => ‘test’ assert session[:user] post :change_password, { :old_password => ‘test’, :password => ‘newpassword’, :password_confirmation => ‘test’ } assert_not_equal ‘newpassword’, assigns(:current_user).password assert_equal “Password mismatch”, flash[:notice] end def test_incorrect_old_password_does_not_change post :login, :login => ‘bryan’, :password => ‘test’ assert session[:user] post :change_password, { :old_password => ‘wrongpassword’, :password => ‘newpassword’, :password_confirmation => ‘newpassword’ } assert_not_equal ‘newpassword’, assigns(:current_user).password assert_equal “Wrong password”, flash[:notice] end

Suggestion by Barry Hess.

I’m pretty sure there is a bug in the account_controller class in the following update_password action code:

flash[:notice] = current_user.save ? “Password changed” : “Password not changed”

On the update password screen, if one enters the correct old password without entering anything for a new and confirmed password, the system does not generate an error. I think this is because of the password_required? stipulation that is checked upon every password validation in the model:

def password_required?
crypted_password.blank? || !password.blank?
end

As you can see, this works fine upon sign-up as the password will be required due to the user’s crypted_password being blank. But when updating one’s password, the crypted_password is not blank and the second part of the or clause resolves to false as well because the password attribute is now blank as well.

I’m not 100% confident where the tweaking should take place, but I did something on the order of:

flash[:notice] = !params[:password].blank? && current_user.save ? “Password changed” : “Password not changed”

The !params[:password].blank? still does not give us an error if both password fields are blank

corrected by replacing line 3 with

if ((params[:password] == params[:password_confirmation]) && params[:password_confirmation] != nil && params[:password_confirmation] != ’’)

I busted this out in user.rb:

def self.update_attributes( params )

  1. handle password changes here
    passed_password = params[ :password ]
    passed_password_confirmation = params[ :password_confirmation ]
if passed_password || passed_password_confirmation
  1. something was passed
    if ( passed_password ‘’ && passed_password_confirmation != ’’ || passed_password != ’’ && passed_password_confirmation ’’ || passed_password != passed_password_confirmation )
  2. one or the other was entered, but not confirmed, or the passwords didn’t match
    self.errors.add_to_base ‘Password and Password Confirmation must match.’
    return false
    else
  3. are we good here? I guess so.
    self.password = passed_password
    super.update_attributes( params )
    end
    else
  4. no password funny business, just update attributes
    super.update_attributes( params )
    end
    end

Seems to do the trick for me. Thoughts? Critiques?

I would suggest using this instead of the != nil and != ’’

if ((params[:password] == params[:password_confirmation]) && !params[:password_confirmation].blank?)

Suggestions by brad.j.miller on May 18th, 2007

This is my modified code, which bases the form on the user model and lets the model validate the input.

Controller (account_controller.rb):

def change_password
return unless request.post?
@user = self.current_user
@user.update_attributes(params[:user])
@user.reset_password
@user.save!
flash[:notice] = “Password has been successfully changed.”
redirect_to(:action => ‘index’)

rescue ActiveRecord::RecordInvalid render :action => ‘change_password’

end

View (change_password.rhtml):

<%= error_messages_for :user %>

<% form_for :user do |f| -%>


<%= f.password_field :password %>


<%= f.password_field :password_confirmation %>

<%= submit_tag ‘Submit’ %>

<% end -%>

Good idea, however i get an error ‘undefined method `reset_password’ for #User blah blah’ i’m new to rails, so any help would be appreciated!!!

Suggestion by Scott on October 9, 2007

I came across the same bug as Barry, but it seemed to me that the real problem had to do with password_required?.

I implemented the change_password method in the controller like Brad did above, then I also modified password_required from this:

def password_required? crypted_password.blank? || !password.blank? end

to this:

def password_required? crypted_password.blank? || !password.nil? end
Last edited by gundestrup, Fri Oct 24 03:53:08 -0700 2008
Home | Edit | New
Versions: