Created
July 5, 2009 21:41
-
-
Save kevinansfield/141137 to your computer and use it in GitHub Desktop.
authlogic forgotten password / password reset
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 30ea36e6455dfca9c1a6bfed23be891b69756a13 Mon Sep 17 00:00:00 2001 | |
From: Kevin Ansfield <[email protected]> | |
Date: Sun, 5 Jul 2009 22:38:26 +0100 | |
Subject: [PATCH] allow users to reset forgotten passwords | |
--- | |
app/controllers/password_reset_controller.rb | 39 +++++++++ | |
app/helpers/password_reset_helper.rb | 2 + | |
app/models/notifier.rb | 12 +++ | |
app/models/user.rb | 5 + | |
app/views/notifier/password_reset_instructions.erb | 10 +++ | |
app/views/password_reset/edit.html.erb | 11 +++ | |
app/views/password_reset/new.html.erb | 8 ++ | |
config/routes.rb | 1 + | |
test/functional/password_reset_controller_test.rb | 82 ++++++++++++++++++++ | |
test/unit/helpers/password_reset_helper_test.rb | 4 + | |
test/unit/notifier_test.rb | 14 ++++ | |
test/unit/user_test.rb | 8 +- | |
12 files changed, 193 insertions(+), 3 deletions(-) | |
create mode 100644 app/controllers/password_reset_controller.rb | |
create mode 100644 app/helpers/password_reset_helper.rb | |
create mode 100644 app/models/notifier.rb | |
create mode 100644 app/views/notifier/password_reset_instructions.erb | |
create mode 100644 app/views/password_reset/edit.html.erb | |
create mode 100644 app/views/password_reset/new.html.erb | |
create mode 100644 test/functional/password_reset_controller_test.rb | |
create mode 100644 test/unit/helpers/password_reset_helper_test.rb | |
create mode 100644 test/unit/notifier_test.rb | |
diff --git a/app/controllers/password_reset_controller.rb b/app/controllers/password_reset_controller.rb | |
new file mode 100644 | |
index 0000000..4136482 | |
--- /dev/null | |
+++ b/app/controllers/password_reset_controller.rb | |
@@ -0,0 +1,39 @@ | |
+class PasswordResetController < ApplicationController | |
+ before_filter :load_user_using_perishable_token, :only => [:edit, :update] | |
+ before_filter :require_no_user | |
+ | |
+ def update | |
+ @user.password = params[:user][:password] | |
+ @user.password_confirmation = params[:user][:password_confirmation] | |
+ if @user.save | |
+ flash[:notice] = "Password successfully updated" | |
+ redirect_to account_url | |
+ else | |
+ render :action => "edit" | |
+ end | |
+ end | |
+ | |
+ def create | |
+ @user = User.find_by_email(params[:email]) | |
+ if @user | |
+ @user.deliver_password_reset_instructions! | |
+ flash[:notice] = "Instructions to reset your password have been emailed to you. Please check your email" | |
+ redirect_to root_url | |
+ else | |
+ flash[:error] = "No user was found with that address" | |
+ render :action => :new | |
+ end | |
+ end | |
+ | |
+ private | |
+ def load_user_using_perishable_token | |
+ @user = User.find_using_perishable_token(params[:id]) | |
+ unless @user | |
+ flash[:error] = "We're sorry, but we could not find your account. " + | |
+ "If you are having issues, try copy and pasting the URL " + | |
+ "from your email into your browser or restarting the reset password process" | |
+ redirect_to root_url | |
+ end | |
+ end | |
+ | |
+end | |
diff --git a/app/helpers/password_reset_helper.rb b/app/helpers/password_reset_helper.rb | |
new file mode 100644 | |
index 0000000..c3a05db | |
--- /dev/null | |
+++ b/app/helpers/password_reset_helper.rb | |
@@ -0,0 +1,2 @@ | |
+module PasswordResetHelper | |
+end | |
diff --git a/app/models/notifier.rb b/app/models/notifier.rb | |
new file mode 100644 | |
index 0000000..70ed78e | |
--- /dev/null | |
+++ b/app/models/notifier.rb | |
@@ -0,0 +1,12 @@ | |
+class Notifier < ActionMailer::Base | |
+ default_url_options[:host] = "localhost:3000" | |
+ | |
+ def password_reset_instructions(user) | |
+ subject "Password Reset Instructions" | |
+ from "[email protected]" | |
+ recipients user.email | |
+ sent_on Time.now | |
+ body :edit_password_reset_url => edit_password_reset_url(user.perishable_token) | |
+ end | |
+ | |
+end | |
diff --git a/app/models/user.rb b/app/models/user.rb | |
index f9b65d0..e98689f 100644 | |
--- a/app/models/user.rb | |
+++ b/app/models/user.rb | |
@@ -2,4 +2,9 @@ class User < ActiveRecord::Base | |
acts_as_authentic do |c| | |
c.perishable_token_valid_for = 24.hours | |
end | |
+ | |
+ def deliver_password_reset_instructions! | |
+ reset_perishable_token! | |
+ Notifier.deliver_password_reset_instructions(self) | |
+ end | |
end | |
diff --git a/app/views/notifier/password_reset_instructions.erb b/app/views/notifier/password_reset_instructions.erb | |
new file mode 100644 | |
index 0000000..75931bc | |
--- /dev/null | |
+++ b/app/views/notifier/password_reset_instructions.erb | |
@@ -0,0 +1,10 @@ | |
+A request to reset your password has been made. | |
+If you did not make this request, simply ignore this email. | |
+If you did make this request just click the link below: | |
+ | |
+<%= @edit_password_reset_url %> | |
+ | |
+If the above URL does not work try copying and pasting it into your browser. | |
+If you continue to have problems please feel free to contact us. | |
+ | |
+Note that the generated URL above will only work for 24 hours for security reasons. | |
\ No newline at end of file | |
diff --git a/app/views/password_reset/edit.html.erb b/app/views/password_reset/edit.html.erb | |
new file mode 100644 | |
index 0000000..b682743 | |
--- /dev/null | |
+++ b/app/views/password_reset/edit.html.erb | |
@@ -0,0 +1,11 @@ | |
+<% form_for @user, :url => password_reset_path, :method => :put do |f| -%> | |
+ <h1>Change my Password</h1> | |
+ <%= f.error_messages %> | |
+ <%= f.label :password %><br /> | |
+ <%= f.password_field :password %><br /> | |
+ <br /> | |
+ <%= f.label :password_confirmation %><br /> | |
+ <%= f.password_field :password_confirmation %><br /> | |
+ <br /> | |
+ <%= f.submit "Update" %> | |
+<% end -%> | |
\ No newline at end of file | |
diff --git a/app/views/password_reset/new.html.erb b/app/views/password_reset/new.html.erb | |
new file mode 100644 | |
index 0000000..0b711da | |
--- /dev/null | |
+++ b/app/views/password_reset/new.html.erb | |
@@ -0,0 +1,8 @@ | |
+<h1>Forgotten Password</h1> | |
+<p>Fill out the form below and instructions to reset your password will be emailed to you.</p> | |
+<% form_tag('/password_reset', {:class => "form"}) do %> | |
+ <%= label_tag :email %><br /> | |
+ <%= text_field_tag :email %><br /> | |
+ <br /> | |
+ <%= submit_tag 'Submit' %> | |
+<% end -%> | |
\ No newline at end of file | |
diff --git a/config/routes.rb b/config/routes.rb | |
index c8ec928..a164ec2 100644 | |
--- a/config/routes.rb | |
+++ b/config/routes.rb | |
@@ -7,6 +7,7 @@ ActionController::Routing::Routes.draw do |map| | |
map.resource :account, :controller => 'users' | |
map.resources :users | |
map.resource :user_session | |
+ map.resources :password_reset | |
map.root :controller => 'user_sessions', :action => 'new' | |
diff --git a/test/functional/password_reset_controller_test.rb b/test/functional/password_reset_controller_test.rb | |
new file mode 100644 | |
index 0000000..05b5306 | |
--- /dev/null | |
+++ b/test/functional/password_reset_controller_test.rb | |
@@ -0,0 +1,82 @@ | |
+require 'test_helper' | |
+ | |
+class PasswordResetControllerTest < ActionController::TestCase | |
+ | |
+ setup :activate_authlogic | |
+ | |
+ test "shows new when logged out" do | |
+ get :new | |
+ assert_response :success | |
+ assert_select "form[action=/password_reset]" do | |
+ assert_select '#email' | |
+ end | |
+ end | |
+ | |
+ test "redirects from new when logged in" do | |
+ UserSession.create(users(:bob)) | |
+ get :new | |
+ assert_redirected_to account_url | |
+ assert_match /must be logged out/, flash[:error] | |
+ end | |
+ | |
+ test "shows edit when logged out" do | |
+ get :edit, :id => users(:bob).perishable_token | |
+ assert_response :success | |
+ assert_select "form[action=/password_reset/#{users(:bob).perishable_token}]" do | |
+ assert_select '#user_password' | |
+ assert_select '#user_password_confirmation' | |
+ end | |
+ end | |
+ | |
+ test "redirected from edit when logged in" do | |
+ UserSession.create(users(:bob)) | |
+ get :edit, :id => users(:bob).perishable_token | |
+ assert_redirected_to account_url | |
+ assert_match /must be logged out/, flash[:error] | |
+ end | |
+ | |
+ test "edit finds user by perishable token" do | |
+ get :edit, :id => users(:bob).perishable_token | |
+ assert assigns(:user) | |
+ assert_equal users(:bob), assigns(:user) | |
+ end | |
+ | |
+ test "edit redirects with error on incorrect perishable token" do | |
+ get :edit, :id => "test" | |
+ assert_nil assigns(:user) | |
+ assert_redirected_to root_url | |
+ assert_match /could not find your account/, flash[:error] | |
+ end | |
+ | |
+ test "create sends password reset instructions for valid user" do | |
+ post :create, :email => users(:bob).email | |
+ assert_redirected_to root_url | |
+ assert_not_nil flash[:notice] | |
+ assert_not_nil assigns(:user) | |
+ end | |
+ | |
+ test "create shows new template if user not found" do | |
+ post :create, :email => "[email protected]" | |
+ assert_response :success | |
+ assert_template 'new' | |
+ assert_not_nil flash[:error] | |
+ end | |
+ | |
+ test "updates password" do | |
+ old_password = "bobowns" | |
+ new_password = "newpassword" | |
+ assert users(:bob).valid_password?(old_password) | |
+ put :update, :id => users(:bob).perishable_token, :user => { :password => new_password, :password_confirmation => new_password } | |
+ assert_redirected_to account_url | |
+ assert_not_nil flash[:notice] | |
+ assert assigns(:user) | |
+ assert !assigns(:user).valid_password?(old_password) | |
+ assert assigns(:user).valid_password?(new_password) | |
+ end | |
+ | |
+ test "update password fails without matched passwords" do | |
+ put :update, :id => users(:bob).perishable_token, :user => { :password => "one", :password_confirmation => "two" } | |
+ assert_response :success | |
+ assert_template 'edit' | |
+ end | |
+end | |
diff --git a/test/unit/helpers/password_reset_helper_test.rb b/test/unit/helpers/password_reset_helper_test.rb | |
new file mode 100644 | |
index 0000000..20b95bc | |
--- /dev/null | |
+++ b/test/unit/helpers/password_reset_helper_test.rb | |
@@ -0,0 +1,4 @@ | |
+require 'test_helper' | |
+ | |
+class PasswordResetHelperTest < ActionView::TestCase | |
+end | |
diff --git a/test/unit/notifier_test.rb b/test/unit/notifier_test.rb | |
new file mode 100644 | |
index 0000000..f208440 | |
--- /dev/null | |
+++ b/test/unit/notifier_test.rb | |
@@ -0,0 +1,14 @@ | |
+require 'test_helper' | |
+ | |
+class NotifierTest < ActionMailer::TestCase | |
+ test "password reset instructions" do | |
+ user = users(:bob) | |
+ | |
+ email = Notifier.deliver_password_reset_instructions(user) | |
+ assert !ActionMailer::Base.deliveries.empty? | |
+ | |
+ assert_equal [user.email], email.to | |
+ assert_equal "Password Reset Instructions", email.subject | |
+ assert_match /password_reset\/#{user.perishable_token}\/edit/, email.body | |
+ end | |
+end | |
diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb | |
index a64d2d3..4c2b65a 100644 | |
--- a/test/unit/user_test.rb | |
+++ b/test/unit/user_test.rb | |
@@ -1,8 +1,10 @@ | |
require 'test_helper' | |
class UserTest < ActiveSupport::TestCase | |
- # Replace this with your real tests. | |
- test "the truth" do | |
- assert true | |
+ test "delivering password reset instructions resets perishable_token" do | |
+ user = users(:bob) | |
+ old_token = user.perishable_token | |
+ user.deliver_password_reset_instructions! | |
+ assert user.perishable_token != old_token | |
end | |
end | |
-- | |
1.6.3.2+GitX |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment