jeffrafter-clearance 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/CHANGELOG.textile +75 -0
  2. data/LICENSE +21 -0
  3. data/README.textile +76 -0
  4. data/Rakefile +73 -0
  5. data/generators/clearance/USAGE +1 -0
  6. data/generators/clearance/clearance_generator.rb +96 -0
  7. data/generators/clearance/lib/insert_commands.rb +103 -0
  8. data/generators/clearance/lib/rake_commands.rb +22 -0
  9. data/generators/clearance/templates/README +45 -0
  10. data/generators/clearance/templates/app/controllers/application.rb +5 -0
  11. data/generators/clearance/templates/app/controllers/confirmations_controller.rb +3 -0
  12. data/generators/clearance/templates/app/controllers/passwords_controller.rb +3 -0
  13. data/generators/clearance/templates/app/controllers/sessions_controller.rb +3 -0
  14. data/generators/clearance/templates/app/controllers/users_controller.rb +3 -0
  15. data/generators/clearance/templates/app/models/clearance_mailer.rb +5 -0
  16. data/generators/clearance/templates/app/models/user.rb +3 -0
  17. data/generators/clearance/templates/app/views/clearance_mailer/change_password.html.erb +7 -0
  18. data/generators/clearance/templates/app/views/clearance_mailer/confirmation.html.erb +2 -0
  19. data/generators/clearance/templates/app/views/passwords/edit.html.erb +23 -0
  20. data/generators/clearance/templates/app/views/passwords/new.html.erb +15 -0
  21. data/generators/clearance/templates/app/views/sessions/new.html.erb +28 -0
  22. data/generators/clearance/templates/app/views/users/_form.html.erb +13 -0
  23. data/generators/clearance/templates/app/views/users/edit.html.erb +6 -0
  24. data/generators/clearance/templates/app/views/users/new.html.erb +6 -0
  25. data/generators/clearance/templates/db/migrate/create_users_with_clearance_columns.rb +20 -0
  26. data/generators/clearance/templates/db/migrate/update_users_with_clearance_columns.rb +41 -0
  27. data/generators/clearance/templates/test/factories/clearance.rb +16 -0
  28. data/generators/clearance/templates/test/functional/confirmations_controller_test.rb +5 -0
  29. data/generators/clearance/templates/test/functional/passwords_controller_test.rb +5 -0
  30. data/generators/clearance/templates/test/functional/sessions_controller_test.rb +5 -0
  31. data/generators/clearance/templates/test/functional/users_controller_test.rb +5 -0
  32. data/generators/clearance/templates/test/unit/clearance_mailer_test.rb +6 -0
  33. data/generators/clearance/templates/test/unit/user_test.rb +5 -0
  34. data/generators/clearance_features/USAGE +1 -0
  35. data/generators/clearance_features/clearance_features_generator.rb +20 -0
  36. data/generators/clearance_features/templates/features/password_reset.feature +31 -0
  37. data/generators/clearance_features/templates/features/sign_in.feature +41 -0
  38. data/generators/clearance_features/templates/features/sign_out.feature +22 -0
  39. data/generators/clearance_features/templates/features/sign_up.feature +30 -0
  40. data/generators/clearance_features/templates/features/step_definitions/clearance_steps.rb +110 -0
  41. data/generators/clearance_features/templates/features/step_definitions/factory_girl_steps.rb +5 -0
  42. data/generators/clearance_features/templates/features/support/paths.rb +25 -0
  43. data/lib/clearance.rb +15 -0
  44. data/lib/clearance/app/controllers/application_controller.rb +84 -0
  45. data/lib/clearance/app/controllers/confirmations_controller.rb +63 -0
  46. data/lib/clearance/app/controllers/passwords_controller.rb +79 -0
  47. data/lib/clearance/app/controllers/sessions_controller.rb +74 -0
  48. data/lib/clearance/app/controllers/users_controller.rb +45 -0
  49. data/lib/clearance/app/models/clearance_mailer.rb +23 -0
  50. data/lib/clearance/app/models/user.rb +118 -0
  51. data/lib/clearance/lib/extensions/errors.rb +4 -0
  52. data/lib/clearance/lib/extensions/rescue.rb +1 -0
  53. data/lib/clearance/test/functional/confirmations_controller_test.rb +72 -0
  54. data/lib/clearance/test/functional/passwords_controller_test.rb +180 -0
  55. data/lib/clearance/test/functional/sessions_controller_test.rb +188 -0
  56. data/lib/clearance/test/functional/users_controller_test.rb +60 -0
  57. data/lib/clearance/test/unit/clearance_mailer_test.rb +65 -0
  58. data/lib/clearance/test/unit/user_test.rb +236 -0
  59. data/rails/init.rb +1 -0
  60. data/shoulda_macros/clearance.rb +246 -0
  61. metadata +157 -0
@@ -0,0 +1,22 @@
1
+ Feature: Sign out
2
+ To protect my account from unauthorized access
3
+ A signed in user
4
+ Should be able to sign out
5
+
6
+ Scenario: User signs out
7
+ Given I am signed up and confirmed as "email@person.com/password"
8
+ When I sign in as "email@person.com/password"
9
+ Then I should be signed in
10
+ And I sign out
11
+ Then I should see "You have been signed out"
12
+ And I should not be signed in
13
+
14
+ Scenario: User who was remembered signs out
15
+ Given I am signed up and confirmed as "email@person.com/password"
16
+ When I sign in with "remember me" as "email@person.com/password"
17
+ Then I should be signed in
18
+ And I sign out
19
+ Then I should see "You have been signed out"
20
+ And I should not be signed in
21
+ When I return next time
22
+ Then I should not be signed in
@@ -0,0 +1,30 @@
1
+ Feature: Sign up
2
+ In order to get access to protected sections of the site
3
+ A user
4
+ Should be able to sign up
5
+
6
+ Scenario: User signs up with invalid data
7
+ When I go to the sign up page
8
+ And I fill in "Email" with "invalidemail"
9
+ And I fill in "Password" with "password"
10
+ And I fill in "Confirm password" with ""
11
+ And I press "Sign Up"
12
+ Then I should see error messages
13
+
14
+ Scenario: User signs up with valid data
15
+ When I go to the sign up page
16
+ And I fill in "Email" with "email@person.com"
17
+ And I fill in "Password" with "password"
18
+ And I fill in "Confirm password" with "password"
19
+ And I press "Sign Up"
20
+ Then I should see "instructions for confirming"
21
+ And a confirmation message should be sent to "email@person.com"
22
+
23
+ Scenario: User confirms his account
24
+ Given I signed up with "email@person.com/password"
25
+ When I follow the confirmation link sent to "email@person.com"
26
+ Then I should see "Confirmed email and signed in"
27
+ And I should be signed in
28
+
29
+
30
+
@@ -0,0 +1,110 @@
1
+ # General
2
+
3
+ Then /^I should see error messages$/ do
4
+ Then %{I should see "error(s)? prohibited"}
5
+ end
6
+
7
+ # Database
8
+
9
+ Given /^no user exists with an email of "(.*)"$/ do |email|
10
+ assert_nil User.find_by_email(email)
11
+ end
12
+
13
+ Given /^I signed up with "(.*)\/(.*)"$/ do |email, password|
14
+ user = Factory :user,
15
+ :email => email,
16
+ :password => password,
17
+ :password_confirmation => password
18
+ end
19
+
20
+ Given /^I am signed up and confirmed as "(.*)\/(.*)"$/ do |email, password|
21
+ user = Factory :email_confirmed_user,
22
+ :email => email,
23
+ :password => password,
24
+ :password_confirmation => password
25
+ end
26
+
27
+ # Session
28
+
29
+ Then /^I should be signed in$/ do
30
+ assert_not_nil request.session[:user_id]
31
+ end
32
+
33
+ Then /^I should not be signed in$/ do
34
+ assert_nil request.session[:user_id]
35
+ end
36
+
37
+ When /^session is cleared$/ do
38
+ request.session[:user_id] = nil
39
+ end
40
+
41
+ # Emails
42
+
43
+ Then /^a confirmation message should be sent to "(.*)"$/ do |email|
44
+ user = User.find_by_email(email)
45
+ sent = ActionMailer::Base.deliveries.first
46
+ assert_equal [user.email], sent.to
47
+ assert_match /confirm/i, sent.subject
48
+ assert !user.token.blank?
49
+ assert_match /#{user.token}/, sent.body
50
+ end
51
+
52
+ When /^I follow the confirmation link sent to "(.*)"$/ do |email|
53
+ user = User.find_by_email(email)
54
+ visit new_user_confirmation_path(:user_id => user, :token => user.token)
55
+ end
56
+
57
+ Then /^a password reset message should be sent to "(.*)"$/ do |email|
58
+ user = User.find_by_email(email)
59
+ sent = ActionMailer::Base.deliveries.first
60
+ assert_equal [user.email], sent.to
61
+ assert_match /password/i, sent.subject
62
+ assert !user.token.blank?
63
+ assert_match /#{user.token}/, sent.body
64
+ end
65
+
66
+ When /^I follow the password reset link sent to "(.*)"$/ do |email|
67
+ user = User.find_by_email(email)
68
+ visit edit_user_password_path(:user_id => user, :token => user.token)
69
+ end
70
+
71
+ When /^I try to change the password of "(.*)" without token$/ do |email|
72
+ user = User.find_by_email(email)
73
+ visit edit_user_password_path(:user_id => user)
74
+ end
75
+
76
+ Then /^I should be forbidden$/ do
77
+ assert_response :forbidden
78
+ end
79
+
80
+
81
+ # Actions
82
+
83
+ When /^I sign in( with "remember me")? as "(.*)\/(.*)"$/ do |remember, email, password|
84
+ When %{I go to the sign in page}
85
+ And %{I fill in "Email" with "#{email}"}
86
+ And %{I fill in "Password" with "#{password}"}
87
+ And %{I check "Remember me"} if remember
88
+ And %{I press "Sign In"}
89
+ end
90
+
91
+ When /^I sign out$/ do
92
+ visit '/session', :delete
93
+ end
94
+
95
+ When /^I request password reset link to be sent to "(.*)"$/ do |email|
96
+ When %{I go to the password reset request page}
97
+ And %{I fill in "Email address" with "#{email}"}
98
+ And %{I press "Reset password"}
99
+ end
100
+
101
+ When /^I update my password with "(.*)\/(.*)"$/ do |password, confirmation|
102
+ And %{I fill in "Choose password" with "#{password}"}
103
+ And %{I fill in "Confirm password" with "#{confirmation}"}
104
+ And %{I press "Save this password"}
105
+ end
106
+
107
+ When /^I return next time$/ do
108
+ When %{session is cleared}
109
+ And %{I go to the homepage}
110
+ end
@@ -0,0 +1,5 @@
1
+ Factory.factories.each do |name, factory|
2
+ Given /^an? #{name} exists with an? (.*) of "([^"]*)"$/ do |attr, value|
3
+ Factory(name, attr.gsub(' ', '_') => value)
4
+ end
5
+ end
@@ -0,0 +1,25 @@
1
+ module NavigationHelpers
2
+ def path_to(page_name)
3
+ case page_name
4
+
5
+ when /the homepage/i
6
+ root_path
7
+ when /the sign up page/i
8
+ new_user_path
9
+ when /the sign in page/i
10
+ new_session_path
11
+ when /the password reset request page/i
12
+ new_password_path
13
+
14
+ # Add more page name => path mappings here
15
+
16
+ else
17
+ raise "Can't find mapping from \"#{page_name}\" to a path."
18
+ end
19
+ end
20
+ end
21
+
22
+ World do |world|
23
+ world.extend NavigationHelpers
24
+ world
25
+ end
@@ -0,0 +1,15 @@
1
+ require 'clearance/lib/extensions/errors'
2
+ require 'clearance/lib/extensions/rescue'
3
+ require 'clearance/app/controllers/application_controller'
4
+ require 'clearance/app/controllers/confirmations_controller'
5
+ require 'clearance/app/controllers/passwords_controller'
6
+ require 'clearance/app/controllers/sessions_controller'
7
+ require 'clearance/app/controllers/users_controller'
8
+ require 'clearance/app/models/clearance_mailer'
9
+ require 'clearance/app/models/user'
10
+ require 'clearance/test/functional/confirmations_controller_test'
11
+ require 'clearance/test/functional/passwords_controller_test'
12
+ require 'clearance/test/functional/sessions_controller_test'
13
+ require 'clearance/test/functional/users_controller_test'
14
+ require 'clearance/test/unit/clearance_mailer_test'
15
+ require 'clearance/test/unit/user_test'
@@ -0,0 +1,84 @@
1
+ module Clearance
2
+ module App
3
+ module Controllers
4
+ module ApplicationController
5
+
6
+ def self.included(controller)
7
+ controller.send(:include, InstanceMethods)
8
+
9
+ controller.class_eval do
10
+ helper_method :current_user
11
+ helper_method :signed_in?
12
+
13
+ hide_action :current_user, :signed_in?
14
+ end
15
+ end
16
+
17
+ module InstanceMethods
18
+ def current_user
19
+ @_current_user ||= (user_from_cookie || user_from_session)
20
+ end
21
+
22
+ def signed_in?
23
+ ! current_user.nil?
24
+ end
25
+
26
+ protected
27
+
28
+ def authenticate
29
+ deny_access unless signed_in?
30
+ end
31
+
32
+ def user_from_session
33
+ if session[:user_id]
34
+ return nil unless user = User.find_by_id(session[:user_id])
35
+ return user if user.email_confirmed?
36
+ end
37
+ end
38
+
39
+ def user_from_cookie
40
+ if token = cookies[:remember_token]
41
+ return nil unless user = User.find_by_token(token)
42
+ return user if user.remember?
43
+ end
44
+ end
45
+
46
+ def sign_user_in(user)
47
+ sign_in(user)
48
+ end
49
+
50
+ def sign_in(user)
51
+ if user
52
+ session[:user_id] = user.id
53
+ end
54
+ end
55
+
56
+ def redirect_back_or(default)
57
+ session[:return_to] ||= params[:return_to]
58
+ if session[:return_to]
59
+ redirect_to(session[:return_to])
60
+ else
61
+ redirect_to(default)
62
+ end
63
+ session[:return_to] = nil
64
+ end
65
+
66
+ def redirect_to_root
67
+ redirect_to root_url
68
+ end
69
+
70
+ def store_location
71
+ session[:return_to] = request.request_uri if request.get?
72
+ end
73
+
74
+ def deny_access(flash_message = nil, opts = {})
75
+ store_location
76
+ flash[:failure] = flash_message if flash_message
77
+ render :template => "/sessions/new", :status => :unauthorized
78
+ end
79
+ end
80
+
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,63 @@
1
+ module Clearance
2
+ module App
3
+ module Controllers
4
+ module ConfirmationsController
5
+
6
+ def self.included(controller)
7
+ controller.send(:include, Actions)
8
+ controller.send(:include, PrivateMethods)
9
+
10
+ controller.class_eval do
11
+ before_filter :forbid_confirmed_user, :only => :new
12
+ before_filter :forbid_missing_token, :only => :new
13
+ before_filter :forbid_non_existant_user, :only => :new
14
+ filter_parameter_logging :token
15
+ end
16
+ end
17
+
18
+ module Actions
19
+ def new
20
+ create
21
+ end
22
+
23
+ def create
24
+ @user = User.find_by_id_and_token(params[:user_id], params[:token])
25
+ @user.confirm_email!
26
+
27
+ sign_user_in(@user)
28
+ flash[:success] = "Confirmed email and signed in."
29
+ redirect_to url_after_create
30
+ end
31
+ end
32
+
33
+ module PrivateMethods
34
+ private
35
+
36
+ def forbid_confirmed_user
37
+ user = User.find_by_id(params[:user_id])
38
+ if user && user.email_confirmed?
39
+ raise ActionController::Forbidden, "confirmed user"
40
+ end
41
+ end
42
+
43
+ def forbid_missing_token
44
+ if params[:token].blank?
45
+ raise ActionController::Forbidden, "missing token"
46
+ end
47
+ end
48
+
49
+ def forbid_non_existant_user
50
+ unless User.find_by_id_and_token(params[:user_id], params[:token])
51
+ raise ActionController::Forbidden, "non-existant user"
52
+ end
53
+ end
54
+
55
+ def url_after_create
56
+ root_url
57
+ end
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,79 @@
1
+ module Clearance
2
+ module App
3
+ module Controllers
4
+ module PasswordsController
5
+
6
+ def self.included(controller)
7
+ controller.send(:include, Actions)
8
+ controller.send(:include, PrivateMethods)
9
+
10
+ controller.class_eval do
11
+ before_filter :forbid_missing_token, :only => [:edit, :update]
12
+ before_filter :forbid_non_existant_user, :only => [:edit, :update]
13
+ filter_parameter_logging :password, :password_confirmation
14
+ end
15
+ end
16
+
17
+ module Actions
18
+ def new
19
+ end
20
+
21
+ def create
22
+ if user = User.find_by_email(params[:password][:email])
23
+ user.forgot_password!
24
+ ClearanceMailer.deliver_change_password user
25
+ flash[:notice] = "You will receive an email within the next few minutes. " <<
26
+ "It contains instructions for changing your password."
27
+ redirect_to url_after_create
28
+ else
29
+ flash.now[:notice] = "Unknown email"
30
+ render :action => :new
31
+ end
32
+ end
33
+
34
+ def edit
35
+ @user = User.find_by_id_and_token(params[:user_id], params[:token])
36
+ end
37
+
38
+ def update
39
+ @user = User.find_by_id_and_token(params[:user_id], params[:token])
40
+
41
+ if @user.update_password(params[:user][:password],
42
+ params[:user][:password_confirmation])
43
+ @user.confirm_email! unless @user.email_confirmed?
44
+ sign_user_in(@user)
45
+ redirect_to url_after_update
46
+ else
47
+ render :action => :edit
48
+ end
49
+ end
50
+ end
51
+
52
+ module PrivateMethods
53
+ private
54
+
55
+ def forbid_missing_token
56
+ if params[:token].blank?
57
+ raise ActionController::Forbidden, "missing token"
58
+ end
59
+ end
60
+
61
+ def forbid_non_existant_user
62
+ unless User.find_by_id_and_token(params[:user_id], params[:token])
63
+ raise ActionController::Forbidden, "non-existant user"
64
+ end
65
+ end
66
+
67
+ def url_after_create
68
+ new_session_url
69
+ end
70
+
71
+ def url_after_update
72
+ root_url
73
+ end
74
+ end
75
+
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,74 @@
1
+ module Clearance
2
+ module App
3
+ module Controllers
4
+ module SessionsController
5
+
6
+ def self.included(controller)
7
+ controller.send(:include, Actions)
8
+ controller.send(:include, PrivateMethods)
9
+
10
+ controller.class_eval do
11
+ protect_from_forgery :except => :create
12
+ filter_parameter_logging :password
13
+ end
14
+ end
15
+
16
+ module Actions
17
+ def create
18
+ @user = User.authenticate(params[:session][:email],
19
+ params[:session][:password])
20
+ if @user.nil?
21
+ flash.now[:notice] = "Bad email or password."
22
+ render :action => :new, :status => :unauthorized
23
+ else
24
+ if @user.email_confirmed?
25
+ remember(@user) if remember?
26
+ sign_user_in(@user)
27
+ flash[:notice] = "Signed in successfully."
28
+ redirect_back_or url_after_create
29
+ else
30
+ ClearanceMailer.deliver_confirmation(@user)
31
+ deny_access("User has not confirmed email. Confirmation email will be resent.")
32
+ end
33
+ end
34
+ end
35
+
36
+ def destroy
37
+ forget(current_user)
38
+ reset_session
39
+ flash[:notice] = "You have been signed out."
40
+ redirect_to url_after_destroy
41
+ end
42
+ end
43
+
44
+ module PrivateMethods
45
+ private
46
+
47
+ def remember?
48
+ params[:session] && params[:session][:remember_me] == "1"
49
+ end
50
+
51
+ def remember(user)
52
+ user.remember_me!
53
+ cookies[:remember_token] = { :value => user.token,
54
+ :expires => user.token_expires_at }
55
+ end
56
+
57
+ def forget(user)
58
+ user.forget_me! if user
59
+ cookies.delete :remember_token
60
+ end
61
+
62
+ def url_after_create
63
+ root_url
64
+ end
65
+
66
+ def url_after_destroy
67
+ new_session_url
68
+ end
69
+ end
70
+
71
+ end
72
+ end
73
+ end
74
+ end