Skip to content

Commit

Permalink
#51 reset password functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesprior committed Jan 27, 2013
1 parent dc7a237 commit 6401c82
Show file tree
Hide file tree
Showing 15 changed files with 192 additions and 6 deletions.
2 changes: 2 additions & 0 deletions app/assets/javascripts/password_resets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
4 changes: 4 additions & 0 deletions app/assets/stylesheets/password_resets.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/
7 changes: 7 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ def require_user
end
end

def require_no_user
if current_account.present?
redirect_to root_path
end
end


def require_admin
if !true # TODO determine how a user is admin
redirect_to login_path
Expand Down
48 changes: 48 additions & 0 deletions app/controllers/password_resets_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
class PasswordResetsController < ApplicationController
before_filter :require_no_user
before_filter :load_account_using_perishable_token, :only => [:edit, :update]

def new
render
end

def create
@account = Account.find_by_email(params[:email])
if @account
@account.deliver_password_reset_instructions!
flash[:notice] = "Instructions to reset your password have been emailed to you. Please check your email."
redirect_to login_path
else
flash[:notice] = "No account was found with that email address"
render :action => :new
end
end

def edit
render
end

def update
@account.password = params[:account][:password]
@account.password_confirmation = params[:account][:password_confirmation]
if @account.save
flash[:notice] = "Password successfully updated"
redirect_to root_path
else
render :action => :edit
end
end

private

def load_account_using_perishable_token
@account = Account.find_using_perishable_token(params[:id])
unless @account
flash[:notice] = "We're sorry, but we could not locate your account. " +
"If you are having issues try copying and pasting the URL " +
"from your email into your browser or restarting the " +
"reset password process."
redirect_to login_path
end
end
end
2 changes: 2 additions & 0 deletions app/helpers/password_resets_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module PasswordResetsHelper
end
5 changes: 5 additions & 0 deletions app/mailers/notifier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ def send_password username, email, password
@password = password
mail :to => email, :subject => "Plan Created"
end

def password_reset_instructions account
@account = account
mail :to => account.email, :subject => "Plan Password Reset Instructions"
end

end
8 changes: 8 additions & 0 deletions app/models/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def changed_date
c.transition_from_crypto_providers PhpCrypt::CryptoProviders::DES
c.validate_email_field false
c.check_passwords_against_database false
c.perishable_token_valid_for 1.day.to_i #NOTE, the password reset email says it expires in 24 hours.
end

# Trick authlogic into behaving with our column name
Expand All @@ -79,6 +80,11 @@ def crypted_password= hash
def crypted_password
read_attribute :password
end

def deliver_password_reset_instructions!
reset_perishable_token!
Notifier.password_reset_instructions(self).deliver
end

def self.create_from_tentative tentative_account, temp_password
ta = tentative_account
Expand Down Expand Up @@ -111,6 +117,7 @@ def self.create_random_token length=8




# == Schema Information
#
# Table name: accounts
Expand All @@ -137,5 +144,6 @@ def self.create_random_token length=8
# is_admin :boolean default(FALSE)
# persistence_token :string(255)
# password_salt :string(255)
# perishable_token :string(255) default(""), not null
#

11 changes: 6 additions & 5 deletions app/views/layouts/_footer.html.haml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#footer
#justupdated
%div
Do you read
=link_to "TODO",read_plan_path(:id=>" ") #TODO
, who just updated?
-unless @current_account.blank?
#justupdated
%div
Do you read
=link_to "TODO",read_plan_path(:id=>" ") #TODO
, who just updated?

#poweredby
%div
Expand Down
9 changes: 9 additions & 0 deletions app/views/notifier/password_reset_instructions.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
="Hi [#{@account.username}]!"

="Password Reset: #{edit_password_reset_url(@account.perishable_token)}"

Navigate to the above link to reset your password.
The link will expire in 24 hours.

Cheers,
[plans]
11 changes: 11 additions & 0 deletions app/views/password_resets/edit.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
%h1 Change My Password

=form_for @account, :url => password_reset_path, :method => :put do |f|
= f.error_messages
= f.label :password
= f.password_field :password
%br
= f.label :password_confirmation
= f.password_field :password_confirmation
%br
= f.submit "Update my password"
13 changes: 13 additions & 0 deletions app/views/password_resets/new.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
%h1 Forgot Password

- if flash[:notice]
%p.notice
= flash[:notice]

%p Fill out the form below and instructions to reset your password will be emailed to you:

=form_tag password_resets_path do

%label Email:
= text_field_tag "email"
= submit_tag "Reset my password"
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
get :reset_password
end
end

resources :password_resets, :except => [:destroy, :show, :index]

match '/register' => 'accounts#new', :as => :register
match '/login' => 'account_sessions#new', :as => :login
Expand Down
10 changes: 10 additions & 0 deletions db/migrate/20130125033218_add_accounts_perishable_token.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class AddAccountsPerishableToken < ActiveRecord::Migration
def up
add_column :accounts, :perishable_token, :string, :default => "", :null => false
add_index :accounts, :perishable_token
end

def down
remove_column :accounts, :perishable_token
end
end
5 changes: 4 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
Expand All @@ -10,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.

ActiveRecord::Schema.define(:version => 20110905085915) do
ActiveRecord::Schema.define(:version => 20130125033218) do

create_table "accounts", :primary_key => "userid", :force => true do |t|
t.string "username", :limit => 16, :default => "", :null => false
Expand All @@ -34,11 +35,13 @@
t.boolean "is_admin", :default => false
t.string "persistence_token"
t.string "password_salt"
t.string "perishable_token", :default => "", :null => false
end

add_index "accounts", ["changed"], :name => "changed"
add_index "accounts", ["password"], :name => "password"
add_index "accounts", ["password"], :name => "password_2"
add_index "accounts", ["perishable_token"], :name => "index_accounts_on_perishable_token"
add_index "accounts", ["username"], :name => "username", :unique => true
add_index "accounts", ["username"], :name => "username_2"

Expand Down
61 changes: 61 additions & 0 deletions spec/controllers/password_resets_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
require 'spec_helper'

describe PasswordResetsController do
describe "new" do
subject { get :new }
it { response.should be_success }
it { should render_template :new }
end

context "with user" do
before do
@account = Account.create!( :username => "foobar", :password => "foobar", :password_confirmation => "foobar", :email => '[email protected]' )
ActionMailer::Base.deliveries.clear
end

describe "create" do
before { post :create, :email => @account.email }
it { response.should be_redirect }
it "should send an email" do
email = ActionMailer::Base.deliveries.first
email.to[0].should == @account.email
email.body.should =~ /#{@account.reload.perishable_token}/
end
end

describe "failed create" do
before { post :create, :password_reset => { :email => '[email protected]'} }
it { response.should be_success }
it { should render_template :new }
it "should not have sent an email" do
ActionMailer::Base.deliveries.first.should == nil
end
end
end

context "with a reset token" do
before do
@account = Account.create!( :username => "foobar", :password => "foobar", :password_confirmation => "foobar", :email => '[email protected]' )
@account.reset_perishable_token!
end

describe "edit" do
before { get :edit, :id => @account.perishable_token }
it { should render_template :edit }
it { @controller.current_account.should == nil}
end

describe "update" do
before do
@password_was = @account.reload.crypted_password
put :update, :id => @account.perishable_token, :account => {:password => 'newpassword', :password_confirmation => 'newpassword'}
end
it { response.should be_redirect }
it { @controller.current_account.should == @account}
it "should have reset the password" do
@password_was.should_not == @account.reload.crypted_password
end
end
end

end

0 comments on commit 6401c82

Please sign in to comment.