Permalink
Browse files

Basic flow of reset password done, with tests

  • Loading branch information...
1 parent 7212e9b commit 94a4db7772167996a116811dbd81f4320664372c @konklone konklone committed Mar 30, 2012
View
@@ -32,6 +32,7 @@ end
group :test do
gem 'rack-test', '0.6.1'
+ gem 'rspec-mocks', '2.9.0'
end
gem 'httparty', '0.7.8'
View
@@ -60,6 +60,7 @@ GEM
rack-test (0.6.1)
rack (>= 1.0)
raindrops (0.8.0)
+ rspec-mocks (2.9.0)
ruby-hmac (0.4.0)
ruby-nuggets (0.8.5)
sinatra (1.3.2)
@@ -120,6 +121,7 @@ DEPENDENCIES
postmark (= 0.9.10)
rack (= 1.4.1)
rack-test (= 0.6.1)
+ rspec-mocks (= 2.9.0)
ruby-hmac (= 0.4.0)
sinatra (= 1.3.2)
sinatra-content-for (= 0.2)
View
@@ -77,11 +77,15 @@ def self.with_postmark!(tag, to, subject, body)
end
end
+ # always disable email in test mode
+ # allow development mode to disable email by withholding the from email
def self.email?
- config[:email][:from].present?
+ !Sinatra::Application.test? and config[:email][:from].present?
end
def self.sent_message(method, tag, to, subject, body)
+ return if Sinatra::Application.test?
+
puts
puts "--------------------------------"
puts "[#{tag}][#{method}] Delivered to #{to}:"
View
@@ -52,6 +52,7 @@ def phone_for_sms
validates_presence_of :email, :message => "We need an email address."
validates_uniqueness_of :email, :message => "That email address is already signed up."
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i, :message => "Not a valid email address."
+
validates_confirmation_of :password, :message => "Your passwords did not match."
before_save :encrypt_password
@@ -68,7 +69,10 @@ def encrypt_password
field :reset_token
validates_uniqueness_of :reset_token
- before_create :new_reset_token
+ before_validation :new_reset_token, :on => :create
+
+ # set after a password is reset
+ field :should_change_password, :type => Boolean, :default => false
# taken from authlogic
# https://github.com/binarylogic/authlogic/blob/master/lib/authlogic/random.rb
@@ -81,4 +85,14 @@ def new_reset_token
self.reset_token = friendly_token
end
+ def reset_password
+ new_password = friendly_token
+ self.password = new_password
+ self.password_confirmation = new_password
+ self.should_change_password = true
+
+ # need to return the actual password, so it can be emailed
+ new_password
+ end
+
end
View
@@ -79,10 +79,10 @@
user.new_reset_token
# email the user with a link including the token
- subject = "[Scout] Password reset"
+ subject = "Request to reset your password"
body = erb :"account/mail/reset_password", :layout => false, :locals => {:user => user}
- unless user.save and Email.deliver!("Password Reset", user.email, subject, body)
+ unless user.save and Email.deliver!("Password Reset Request", user.email, subject, body)
flash[:forgot] = "Your account was found, but there was an error actually sending the reset password email. Try again later, or write us and we can try to figure out what happened."
redirect_home and return
end
@@ -93,11 +93,29 @@
get '/account/reset' do
unless params[:reset_token] and user = User.where(:reset_token => params[:reset_token]).first
- flash[:forgot] = "This password reset request is no longer valid."
+ halt 404 and return
+ end
+
+ # reset the password itself, and the token
+ new_password = user.reset_password
+ user.new_reset_token
+ unless user.save
+ flash[:forgot] = "There was an error issuing you a new password. Please contact us for support."
+ redirect_home and return
+ end
+
+ # send the next email with the new password
+
+ subject = "Your password has been reset"
+ body = erb :"account/mail/new_password", :layout => false, :locals => {:new_password => new_password}
+
+ unless Email.deliver!("Password Reset", user.email, subject, body)
+ flash[:forgot] = "There was an error emailing you a new password. Please contact us for support."
redirect_home and return
end
- erb :"account/reset_password", :locals => {:user => user}
+ flash[:forgot] = "Your password has been reset, and a new one has been emailed to you."
+ redirect_home
end
put '/user' do
View
@@ -0,0 +1,129 @@
+ENV['RACK_ENV'] = 'test'
+
+require 'rubygems'
+require 'test/unit'
+
+require 'bundler/setup'
+require 'rack/test'
+require 'scout'
+
+require 'rspec/mocks'
+
+set :environment, :test
+
+class AccountsTest < Test::Unit::TestCase
+ include Rack::Test::Methods
+
+ # Test::Unit hooks
+
+ def setup
+ RSpec::Mocks.setup(self)
+ end
+
+ def verify
+ RSpec::Mocks.space.verify_all
+ end
+
+ def teardown
+ # delete fake user if it was created
+ User.where(:email => "fake@example.com").delete_all
+
+ # remove rspec mocks
+ RSpec::Mocks.space.reset_all
+ end
+
+
+ # Sinatra helpers
+
+ def app
+ Sinatra::Application
+ end
+
+ def login(user)
+ {"rack.session" => {'user_email' => user.email}}
+ end
+
+ def new_user!(options = {})
+ User.create!({:email => "fake@example.com", :password => "test", :password_confirmation => "test"}.merge(options))
+ end
+
+ def redirect_path
+ last_response.headers['Location'].sub(/http:\/\/example.org/, '')
+ end
+
+
+ # actual tests
+
+ def test_start_reset_password_process
+ # post '/subscriptions', :interest => "testing", :subscription_type => "federal_bills"
+ # assert_equal 302, last_response.status
+ user = new_user!
+ old_token = user.reset_token
+
+ Email.should_receive(:deliver!).with("Password Reset Request", user.email, anything, anything)
+
+ post '/login/forgot', :email => user.email
+
+ user.reload
+ assert_not_equal old_token, user.reset_token
+
+ assert_equal 302, last_response.status
+ assert_equal '/', redirect_path
+ end
+
+ def test_start_reset_password_process_with_bad_email
+ Email.should_not_receive(:deliver!)
+ post '/login/forgot', :email => "notvalid@example.com"
+
+ assert_equal 302, last_response.status
+ assert_equal '/', redirect_path
+ end
+
+ def test_visit_reset_password_link
+ user = new_user!
+ reset_token = user.reset_token
+ old_password_hash = user.password_hash
+ assert !user.should_change_password
+
+ Email.should_receive(:deliver!).with("Password Reset", user.email, anything, anything)
+
+ get '/account/reset', :reset_token => reset_token
+ user.reload
+
+ assert_not_equal reset_token, user.reset_token
+ assert_not_equal old_password_hash, user.password_hash
+ assert user.should_change_password
+
+ assert_equal 302, last_response.status
+ assert_equal '/', redirect_path
+ end
+
+ def test_visit_reset_password_link_with_no_token
+ Email.should_not_receive(:deliver!)
+
+ get '/account/reset'
+
+ assert_equal 404, last_response.status
+ end
+
+ def test_visit_reset_password_link_with_invalid_token
+ Email.should_not_receive(:deliver!)
+
+ get '/account/reset', :reset_token => "whatever"
+
+ assert_equal 404, last_response.status
+ end
+
+ # def test_change_password
+ # end
+
+ # def test_change_password_not_logged_in
+ # end
+
+ # def test_change_password_wrong_original_password
+ # end
+
+ # def test_change_password_mismatched_new_passwords
+ # end
+
+end
Binary file not shown.
@@ -0,0 +1,17 @@
+Hi,
+
+We've reset your password. You should login as soon as possible with this new password, and change it to something else.
+
+Your new password:
+<%= new_password %>
+
+
+Visit Scout to log in with your new password:
+http://<%= config[:hostname] %>
+
+
+---------------------
+
+This email is sent from Scout, a service of the Sunlight Foundation (sunlightfoundation.com).
+
+If you received this message in error, you can safely delete it.
@@ -1 +0,0 @@
-Hi, <%= user.email %>.

0 comments on commit 94a4db7

Please sign in to comment.