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,45 @@
1
+ module Clearance
2
+ module App
3
+ module Controllers
4
+ module UsersController
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 :redirect_to_root, :only => [:new, :create], :if => :signed_in?
12
+ filter_parameter_logging :password
13
+ end
14
+ end
15
+
16
+ module Actions
17
+ def new
18
+ @user = User.new(params[:user])
19
+ end
20
+
21
+ def create
22
+ @user = User.new params[:user]
23
+ if @user.save
24
+ ClearanceMailer.deliver_confirmation @user
25
+ flash[:notice] = "You will receive an email within the next few minutes. " <<
26
+ "It contains instructions for confirming your account."
27
+ redirect_to url_after_create
28
+ else
29
+ render :action => "new"
30
+ end
31
+ end
32
+ end
33
+
34
+ module PrivateMethods
35
+ private
36
+
37
+ def url_after_create
38
+ new_session_url
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,23 @@
1
+ module Clearance
2
+ module App
3
+ module Models
4
+ module ClearanceMailer
5
+
6
+ def change_password(user)
7
+ from DO_NOT_REPLY
8
+ recipients user.email
9
+ subject "Change your password"
10
+ body :user => user
11
+ end
12
+
13
+ def confirmation(user)
14
+ from DO_NOT_REPLY
15
+ recipients user.email
16
+ subject "Account confirmation"
17
+ body :user => user
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,118 @@
1
+ require 'digest/sha1'
2
+
3
+ module Clearance
4
+ module App
5
+ module Models
6
+ module User
7
+
8
+ def self.included(model)
9
+ model.extend ClassMethods
10
+ model.send(:include, InstanceMethods)
11
+
12
+ model.class_eval do
13
+ attr_accessible :email, :password, :password_confirmation
14
+ attr_accessor :password, :password_confirmation
15
+
16
+ validates_presence_of :email
17
+ validates_presence_of :password, :if => :password_required?
18
+ validates_confirmation_of :password, :if => :password_required?
19
+ validates_uniqueness_of :email, :case_sensitive => false
20
+ validates_format_of :email, :with => %r{.+@.+\..+}
21
+
22
+ before_save :initialize_salt, :encrypt_password, :initialize_token
23
+ end
24
+ end
25
+
26
+ module InstanceMethods
27
+ def authenticated?(password)
28
+ encrypted_password == encrypt(password)
29
+ end
30
+
31
+ def encrypt(string)
32
+ generate_hash("--#{salt}--#{string}--")
33
+ end
34
+
35
+ def remember?
36
+ token_expires_at && Time.now.utc < token_expires_at
37
+ end
38
+
39
+ def remember_me!
40
+ remember_me_until! 2.weeks.from_now.utc
41
+ end
42
+
43
+ def forget_me!
44
+ clear_token
45
+ save(false)
46
+ end
47
+
48
+ def confirm_email!
49
+ self.email_confirmed = true
50
+ self.token = nil
51
+ save(false)
52
+ end
53
+
54
+ def forgot_password!
55
+ generate_token
56
+ save(false)
57
+ end
58
+
59
+ def update_password(new_password, new_password_confirmation)
60
+ self.password = new_password
61
+ self.password_confirmation = new_password_confirmation
62
+ clear_token if valid?
63
+ save
64
+ end
65
+
66
+ protected
67
+
68
+ def generate_hash(string)
69
+ Digest::SHA1.hexdigest(string)
70
+ end
71
+
72
+ def initialize_salt
73
+ if new_record?
74
+ self.salt = generate_hash("--#{Time.now.utc.to_s}--#{password}--")
75
+ end
76
+ end
77
+
78
+ def encrypt_password
79
+ return if password.blank?
80
+ self.encrypted_password = encrypt(password)
81
+ end
82
+
83
+ def generate_token
84
+ self.token = encrypt("--#{Time.now.utc.to_s}--#{password}--")
85
+ self.token_expires_at = nil
86
+ end
87
+
88
+ def clear_token
89
+ self.token = nil
90
+ self.token_expires_at = nil
91
+ end
92
+
93
+ def initialize_token
94
+ generate_token if new_record?
95
+ end
96
+
97
+ def password_required?
98
+ encrypted_password.blank? || !password.blank?
99
+ end
100
+
101
+ def remember_me_until!(time)
102
+ self.token_expires_at = time
103
+ self.token = encrypt("--#{token_expires_at}--#{password}--")
104
+ save(false)
105
+ end
106
+ end
107
+
108
+ module ClassMethods
109
+ def authenticate(email, password)
110
+ return nil unless user = find_by_email(email)
111
+ return user if user.authenticated?(password)
112
+ end
113
+ end
114
+
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,4 @@
1
+ module ActionController
2
+ class Forbidden < StandardError
3
+ end
4
+ end
@@ -0,0 +1 @@
1
+ ActionController::Base.rescue_responses.update('ActionController::Forbidden' => :forbidden)
@@ -0,0 +1,72 @@
1
+ module Clearance
2
+ module Test
3
+ module Functional
4
+ module ConfirmationsControllerTest
5
+
6
+ def self.included(controller_test)
7
+ controller_test.class_eval do
8
+
9
+ should_filter_params :token
10
+
11
+ context "a user whose email has not been confirmed" do
12
+ setup { @user = Factory(:user) }
13
+
14
+ should "have a token" do
15
+ assert_not_nil @user.token
16
+ assert_not_equal "", @user.token
17
+ end
18
+
19
+ context "on GET to #new with correct id and token" do
20
+ setup do
21
+ get :new, :user_id => @user.to_param, :token => @user.token
22
+ end
23
+
24
+ should_set_the_flash_to /confirmed email/i
25
+ should_set_the_flash_to /signed in/i
26
+ should_be_signed_in_and_email_confirmed_as { @user }
27
+ should_redirect_to_url_after_create
28
+ end
29
+
30
+ context "with an incorrect token" do
31
+ setup do
32
+ @bad_token = "bad token"
33
+ assert_not_equal @bad_token, @user.token
34
+ end
35
+
36
+ should_forbid "on GET to #new with incorrect token" do
37
+ get :new, :user_id => @user.to_param, :token => @bad_token
38
+ end
39
+ end
40
+
41
+ should_forbid "on GET to #new with blank token" do
42
+ get :new, :user_id => @user.to_param, :token => ""
43
+ end
44
+
45
+ should_forbid "on GET to #new with no token" do
46
+ get :new, :user_id => @user.to_param
47
+ end
48
+ end
49
+
50
+ context "a user with email confirmed" do
51
+ setup { @user = Factory(:email_confirmed_user) }
52
+
53
+ should_forbid "on GET to #new with correct id" do
54
+ get :new, :user_id => @user.to_param
55
+ end
56
+ end
57
+
58
+ context "no users" do
59
+ setup { assert_equal 0, User.count }
60
+
61
+ should_forbid "on GET to #new with nonexistent id and token" do
62
+ get :new, :user_id => '123', :token => '123'
63
+ end
64
+ end
65
+
66
+ end
67
+ end
68
+
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,180 @@
1
+ module Clearance
2
+ module Test
3
+ module Functional
4
+ module PasswordsControllerTest
5
+
6
+ def self.included(controller_test)
7
+ controller_test.class_eval do
8
+
9
+ should_route :get, '/users/1/password/edit',
10
+ :action => 'edit', :user_id => '1'
11
+
12
+ context "a signed up user" do
13
+ setup do
14
+ @user = Factory(:user)
15
+ end
16
+
17
+ context "on GET to #new" do
18
+ setup { get :new, :user_id => @user.to_param }
19
+
20
+ should_respond_with :success
21
+ should_render_template "new"
22
+ end
23
+
24
+ context "on POST to #create" do
25
+ context "with correct email address" do
26
+ setup do
27
+ ActionMailer::Base.deliveries.clear
28
+ post :create, :password => { :email => @user.email }
29
+ end
30
+
31
+ should "generate a token for the change your password email" do
32
+ assert_not_nil @user.reload.token
33
+ end
34
+
35
+ should "send the change your password email" do
36
+ assert_sent_email do |email|
37
+ email.subject =~ /change your password/i
38
+ end
39
+ end
40
+
41
+ should_set_the_flash_to /password/i
42
+ should_redirect_to_url_after_create
43
+ end
44
+
45
+ context "with incorrect email address" do
46
+ setup do
47
+ email = "user1@example.com"
48
+ assert ! User.exists?(['email = ?', email])
49
+ ActionMailer::Base.deliveries.clear
50
+ assert_equal @user.token, @user.reload.token
51
+
52
+ post :create, :password => { :email => email }
53
+ end
54
+
55
+ should "not generate a token for the change your password email" do
56
+ assert_equal @user.token, @user.reload.token
57
+ end
58
+
59
+ should "not send a password reminder email" do
60
+ assert ActionMailer::Base.deliveries.empty?
61
+ end
62
+
63
+ should "set a :notice flash" do
64
+ assert_not_nil flash.now[:notice]
65
+ end
66
+
67
+ should_render_template :new
68
+ end
69
+ end
70
+ end
71
+
72
+ context "a signed up user and forgotten password" do
73
+ setup do
74
+ @user = Factory(:user)
75
+ @user.forgot_password!
76
+ end
77
+
78
+ context "on GET to #edit with correct id and token" do
79
+ setup do
80
+ get :edit, :user_id => @user.to_param, :token => @user.token
81
+ end
82
+
83
+ should "find the user" do
84
+ assert_equal @user, assigns(:user)
85
+ end
86
+
87
+ should_respond_with :success
88
+ should_render_template "edit"
89
+ should_display_a_password_update_form
90
+ end
91
+
92
+ should_forbid "on GET to #edit with correct id but blank token" do
93
+ get :edit, :user_id => @user.to_param, :token => ""
94
+ end
95
+
96
+ should_forbid "on GET to #edit with correct id but no token" do
97
+ get :edit, :user_id => @user.to_param
98
+ end
99
+
100
+ context "on PUT to #update with matching password and password confirmation" do
101
+ setup do
102
+ new_password = "new_password"
103
+ @encrypted_new_password = @user.encrypt(new_password)
104
+ assert_not_equal @encrypted_new_password, @user.encrypted_password
105
+
106
+ put(:update,
107
+ :user_id => @user,
108
+ :token => @user.token,
109
+ :user => {
110
+ :password => new_password,
111
+ :password_confirmation => new_password
112
+ })
113
+ @user.reload
114
+ end
115
+
116
+ should "update password" do
117
+ assert_equal @encrypted_new_password, @user.encrypted_password
118
+ end
119
+
120
+ should "clear token" do
121
+ assert_nil @user.token
122
+ end
123
+
124
+ should_be_signed_in_as { @user }
125
+ should_redirect_to_url_after_update
126
+ end
127
+
128
+ context "on PUT to #update with password but blank password confirmation" do
129
+ setup do
130
+ new_password = "new_password"
131
+ @encrypted_new_password = @user.encrypt(new_password)
132
+
133
+ put(:update,
134
+ :user_id => @user.to_param,
135
+ :token => @user.token,
136
+ :user => {
137
+ :password => new_password,
138
+ :password_confirmation => ''
139
+ })
140
+ @user.reload
141
+ end
142
+
143
+ should "not update password" do
144
+ assert_not_equal @encrypted_new_password, @user.encrypted_password
145
+ end
146
+
147
+ should "not clear token" do
148
+ assert_not_nil @user.token
149
+ end
150
+
151
+ should_not_be_signed_in
152
+ should_respond_with :success
153
+ should_render_template :edit
154
+
155
+ should_display_a_password_update_form
156
+ end
157
+
158
+ should_forbid "on PUT to #update with id but no token" do
159
+ put :update, :user_id => @user.to_param, :token => ""
160
+ end
161
+ end
162
+
163
+ context "given two users and user one signs in" do
164
+ setup do
165
+ @user_one = Factory(:user)
166
+ @user_two = Factory(:user)
167
+ sign_in_as @user_one
168
+ end
169
+
170
+ should_forbid "when user one tries to change user two's password on GET with no token" do
171
+ get :edit, :user_id => @user_two.to_param
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ end
178
+ end
179
+ end
180
+ end