mischa-clearance 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. data/LICENSE +21 -0
  2. data/README.textile +136 -0
  3. data/Rakefile +83 -0
  4. data/TODO.textile +24 -0
  5. data/generators/clearance/USAGE +1 -0
  6. data/generators/clearance/clearance_generator.rb +69 -0
  7. data/generators/clearance/templates/app/controllers/application.rb +5 -0
  8. data/generators/clearance/templates/app/controllers/confirmations_controller.rb +3 -0
  9. data/generators/clearance/templates/app/controllers/passwords_controller.rb +3 -0
  10. data/generators/clearance/templates/app/controllers/sessions_controller.rb +3 -0
  11. data/generators/clearance/templates/app/controllers/users_controller.rb +3 -0
  12. data/generators/clearance/templates/app/models/clearance_mailer.rb +5 -0
  13. data/generators/clearance/templates/app/models/user.rb +3 -0
  14. data/generators/clearance/templates/app/views/clearance_mailer/change_password.html.erb +6 -0
  15. data/generators/clearance/templates/app/views/clearance_mailer/confirmation.html.erb +1 -0
  16. data/generators/clearance/templates/app/views/confirmations/new.html.erb +6 -0
  17. data/generators/clearance/templates/app/views/passwords/edit.html.erb +23 -0
  18. data/generators/clearance/templates/app/views/passwords/new.html.erb +15 -0
  19. data/generators/clearance/templates/app/views/sessions/new.html.erb +26 -0
  20. data/generators/clearance/templates/app/views/users/_form.html.erb +13 -0
  21. data/generators/clearance/templates/app/views/users/edit.html.erb +4 -0
  22. data/generators/clearance/templates/app/views/users/new.html.erb +4 -0
  23. data/generators/clearance/templates/test/factories.rb +9 -0
  24. data/generators/clearance/templates/test/functional/confirmations_controller_test.rb +5 -0
  25. data/generators/clearance/templates/test/functional/passwords_controller_test.rb +5 -0
  26. data/generators/clearance/templates/test/functional/sessions_controller_test.rb +5 -0
  27. data/generators/clearance/templates/test/functional/users_controller_test.rb +5 -0
  28. data/generators/clearance/templates/test/unit/clearance_mailer_test.rb +6 -0
  29. data/generators/clearance/templates/test/unit/user_test.rb +5 -0
  30. data/lib/clearance.rb +15 -0
  31. data/lib/clearance/app/controllers/application_controller.rb +84 -0
  32. data/lib/clearance/app/controllers/confirmations_controller.rb +46 -0
  33. data/lib/clearance/app/controllers/passwords_controller.rb +67 -0
  34. data/lib/clearance/app/controllers/sessions_controller.rb +79 -0
  35. data/lib/clearance/app/controllers/users_controller.rb +47 -0
  36. data/lib/clearance/app/models/clearance_mailer.rb +33 -0
  37. data/lib/clearance/app/models/user.rb +86 -0
  38. data/lib/clearance/test/functional/confirmations_controller_test.rb +85 -0
  39. data/lib/clearance/test/functional/passwords_controller_test.rb +188 -0
  40. data/lib/clearance/test/functional/sessions_controller_test.rb +148 -0
  41. data/lib/clearance/test/functional/users_controller_test.rb +69 -0
  42. data/lib/clearance/test/test_helper.rb +94 -0
  43. data/lib/clearance/test/unit/clearance_mailer_test.rb +63 -0
  44. data/lib/clearance/test/unit/user_test.rb +208 -0
  45. data/lib/clearance/version.rb +7 -0
  46. metadata +127 -0
@@ -0,0 +1,86 @@
1
+ module Clearance
2
+ module App
3
+ module Models
4
+ module User
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+
9
+ attr_accessible :email, :password, :password_confirmation
10
+ attr_accessor :password, :password_confirmation
11
+
12
+ validates_presence_of :email
13
+ validates_presence_of :password, :if => :password_required?
14
+ validates_confirmation_of :password, :if => :password_required?
15
+ validates_uniqueness_of :email
16
+
17
+ before_save :initialize_salt, :encrypt_password
18
+
19
+ extend ClassMethods
20
+ include InstanceMethods
21
+
22
+ protected
23
+
24
+ include ProtectedInstanceMethods
25
+
26
+ end
27
+ end
28
+
29
+ module ClassMethods
30
+ def authenticate(email, password)
31
+ user = find_by_email email
32
+ user && user.authenticated?(password) ? user : nil
33
+ end
34
+ end
35
+
36
+ module InstanceMethods
37
+ def authenticated?(password)
38
+ crypted_password == encrypt(password)
39
+ end
40
+
41
+ def encrypt(password)
42
+ Digest::SHA1.hexdigest "--#{salt}--#{password}--"
43
+ end
44
+
45
+ def remember_token?
46
+ remember_token_expires_at && Time.now.utc < remember_token_expires_at
47
+ end
48
+
49
+ def remember_me!
50
+ remember_me_until 2.weeks.from_now.utc
51
+ end
52
+
53
+ def remember_me_until(time)
54
+ self.update_attribute :remember_token_expires_at, time
55
+ self.update_attribute :remember_token, encrypt("#{email}--#{remember_token_expires_at}")
56
+ end
57
+
58
+ def forget_me!
59
+ self.update_attribute :remember_token_expires_at, nil
60
+ self.update_attribute :remember_token, nil
61
+ end
62
+
63
+ def confirm!
64
+ self.update_attribute :confirmed, true
65
+ end
66
+ end
67
+
68
+ module ProtectedInstanceMethods
69
+ def initialize_salt
70
+ self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{email}--") if new_record?
71
+ end
72
+
73
+ def encrypt_password
74
+ return if password.blank?
75
+ self.crypted_password = encrypt(password)
76
+ end
77
+
78
+ def password_required?
79
+ crypted_password.blank? || !password.blank?
80
+ end
81
+ end
82
+
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,85 @@
1
+ module Clearance
2
+ module Test
3
+ module Functional
4
+ module ConfirmationsControllerTest
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+
9
+ context 'A GET to #new' do
10
+ context "with the User with the given id's salt" do
11
+ setup do
12
+ @user = Factory :user
13
+ get :new, :user_id => @user.to_param, :salt => @user.salt
14
+ end
15
+
16
+ should 'find the User record with the given id and salt' do
17
+ assert_equal @user, assigns(:user)
18
+ end
19
+
20
+ should_respond_with :success
21
+ should_render_template :new
22
+ end
23
+
24
+ context "without the User with the given id's salt" do
25
+ setup do
26
+ user = Factory :user
27
+ salt = ''
28
+ assert_not_equal salt, user.salt
29
+
30
+ get :new, :user_id => user.to_param, :salt => ''
31
+ end
32
+
33
+ should_respond_with :not_found
34
+
35
+ should 'render nothing' do
36
+ assert @response.body.blank?
37
+ end
38
+ end
39
+ end
40
+
41
+ context 'A POST to #create' do
42
+ context "with the User with the given id's salt" do
43
+ setup do
44
+ @user = Factory :user
45
+ assert ! @user.confirmed?
46
+
47
+ post :create, :user_id => @user, :salt => @user.salt
48
+ @user.reload
49
+ end
50
+
51
+ should 'confirm the User record with the given id' do
52
+ assert @user.confirmed?
53
+ end
54
+
55
+ should 'log the User in' do
56
+ assert_equal @user.id, session[:user_id]
57
+ end
58
+
59
+ should_redirect_to "@controller.send(:url_after_create)"
60
+ end
61
+
62
+ context "without the User with the given id's salt" do
63
+ setup do
64
+ user = Factory :user
65
+ salt = ''
66
+ assert_not_equal salt, user.salt
67
+
68
+ post :create, :user_id => user.id, :salt => salt
69
+ end
70
+
71
+ should_respond_with :not_found
72
+
73
+ should 'render nothing' do
74
+ assert @response.body.blank?
75
+ end
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,188 @@
1
+ module Clearance
2
+ module Test
3
+ module Functional
4
+ module PasswordsControllerTest
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+
9
+ should_route :get, '/users/1/password/edit', :action => 'edit', :user_id => '1'
10
+
11
+ context 'with a user' do
12
+ setup { @user = Factory :user }
13
+
14
+ context 'A GET to #new' do
15
+ setup { get :new, :user_id => @user.to_param }
16
+
17
+ should_respond_with :success
18
+ should_render_template 'new'
19
+ end
20
+
21
+ context 'A POST to #create' do
22
+ context "with an existing user's email address" do
23
+ setup do
24
+ ActionMailer::Base.deliveries.clear
25
+
26
+ post :create, :password => { :email => @user.email }
27
+ @email = ActionMailer::Base.deliveries[0]
28
+ end
29
+
30
+ should 'send an email to the user to edit their password' do
31
+ assert @email.subject =~ /change your password/i
32
+ end
33
+
34
+ should_redirect_to "@controller.send(:url_after_create)"
35
+ end
36
+
37
+ context 'with a non-existing email address' do
38
+ setup do
39
+ email = 'user1@example.com'
40
+ assert ! User.exists?(['email = ?', email])
41
+ ActionMailer::Base.deliveries.clear
42
+
43
+ post :create, :password => { :email => email }
44
+ end
45
+
46
+ should 'not send a password reminder email' do
47
+ assert ActionMailer::Base.deliveries.empty?
48
+ end
49
+
50
+ should 'set a :warning flash' do
51
+ assert_not_nil flash.now[:warning]
52
+ end
53
+
54
+ should_render_template "new"
55
+ end
56
+ end
57
+
58
+ context 'A GET to #edit' do
59
+ context "with an existing user's id and password" do
60
+ setup do
61
+ get :edit,
62
+ :user_id => @user.to_param,
63
+ :password => @user.crypted_password,
64
+ :email => @user.email
65
+ end
66
+
67
+ should 'find the user with the given id and password' do
68
+ assert_equal @user, assigns(:user)
69
+ end
70
+
71
+ should_respond_with :success
72
+ should_render_template "edit"
73
+
74
+ should "have a form for the user's email, password, and password confirm" do
75
+ update_path = ERB::Util.h(user_password_path(@user,
76
+ :password => @user.crypted_password,
77
+ :email => @user.email))
78
+
79
+ assert_select 'form[action=?]', update_path do
80
+ assert_select 'input[name=_method][value=?]', 'put'
81
+ assert_select 'input[name=?]', 'user[password]'
82
+ assert_select 'input[name=?]', 'user[password_confirmation]'
83
+ end
84
+ end
85
+ end
86
+
87
+ context "with an existing user's id but not password" do
88
+ setup do
89
+ get :edit, :user_id => @user.to_param, :password => ''
90
+ end
91
+
92
+ should_respond_with :not_found
93
+
94
+ should 'render an empty response' do
95
+ assert @response.body.blank?
96
+ end
97
+ end
98
+ end
99
+
100
+ context 'A PUT to #update' do
101
+ context "with an existing user's id but not password" do
102
+ setup do
103
+ put :update, :user_id => @user.to_param, :password => ''
104
+ end
105
+
106
+ should "not update the user's password" do
107
+ assert_not_equal @encrypted_new_password, @user.crypted_password
108
+ end
109
+
110
+ should 'not log the user in' do
111
+ assert_nil session[:user_id]
112
+ end
113
+
114
+ should_respond_with :not_found
115
+
116
+ should 'render an empty response' do
117
+ assert @response.body.blank?
118
+ end
119
+ end
120
+
121
+ context 'with a matching password and password confirmation' do
122
+ setup do
123
+ new_password = 'new_password'
124
+ encryption_format = "--#{@user.salt}--#{new_password}--"
125
+ @encrypted_new_password = Digest::SHA1.hexdigest encryption_format
126
+ assert_not_equal @encrypted_new_password, @user.crypted_password
127
+
128
+ put(:update,
129
+ :user_id => @user,
130
+ :email => @user.email,
131
+ :password => @user.crypted_password,
132
+ :user => {
133
+ :password => new_password,
134
+ :password_confirmation => new_password
135
+ })
136
+ @user.reload
137
+ end
138
+
139
+ should "update the user's password" do
140
+ assert_equal @encrypted_new_password, @user.crypted_password
141
+ end
142
+
143
+ should 'log the user in' do
144
+ assert_equal session[:user_id], @user.id
145
+ end
146
+
147
+ should_redirect_to "user_path(@user)"
148
+ end
149
+
150
+ context 'with password but blank password confirmation' do
151
+ setup do
152
+ new_password = 'new_password'
153
+ encryption_format = "--#{@user.salt}--#{new_password}--"
154
+ @encrypted_new_password = Digest::SHA1.hexdigest encryption_format
155
+
156
+ put(:update,
157
+ :user_id => @user.to_param,
158
+ :password => @user.crypted_password,
159
+ :user => {
160
+ :password => new_password,
161
+ :password_confirmation => ''
162
+ })
163
+ @user.reload
164
+ end
165
+
166
+ should "not update the user's password" do
167
+ assert_not_equal @encrypted_new_password, @user.crypted_password
168
+ end
169
+
170
+ should 'not log the user in' do
171
+ assert_nil session[:user_id]
172
+ end
173
+
174
+ should_respond_with :not_found
175
+
176
+ should 'render an empty response' do
177
+ assert @response.body.blank?
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
184
+
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,148 @@
1
+ module Clearance
2
+ module Test
3
+ module Functional
4
+ module SessionsControllerTest
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+ should_filter_params :password
9
+
10
+ context "on GET to /sessions/new" do
11
+ setup { get :new }
12
+
13
+ should_respond_with :success
14
+ should_render_template :new
15
+ should_not_set_the_flash
16
+ should_have_form :action => "session_path",
17
+ :fields => { "session[email]" => :text,
18
+ "session[password]" => :password,
19
+ "session[remember_me]" => :checkbox }
20
+ end
21
+
22
+ context "Given an unconfirmed user" do
23
+ setup do
24
+ @user = Factory(:user, :confirmed => false)
25
+ end
26
+
27
+ context "a POST to #create with good credentials" do
28
+ setup do
29
+ ActionMailer::Base.deliveries.clear
30
+ post :create, :session => {
31
+ :email => @user.email,
32
+ :password => @user.password
33
+ }
34
+ end
35
+
36
+ should_deny_access(:flash => /confirm/i)
37
+
38
+ should "send the confirmation email" do
39
+ assert_not_nil email = ActionMailer::Base.deliveries[0]
40
+ assert_match /account confirmation/i, email.subject
41
+ end
42
+ end
43
+ end
44
+
45
+ context "Given a confirmed user" do
46
+ setup { @user = Factory(:user, :confirmed => true) }
47
+
48
+ context "a POST to #create with good credentials" do
49
+ setup do
50
+ post :create, :session => { :email => @user.email,
51
+ :password => @user.password }
52
+ end
53
+
54
+ should_set_the_flash_to /success/i
55
+ should_redirect_to '@controller.send(:url_after_create)'
56
+ should_return_from_session :user_id, "@user.id"
57
+ end
58
+
59
+ context "a POST to #create with bad credentials" do
60
+ setup do
61
+ post :create, :session => { :email => @user.email,
62
+ :password => "bad value" }
63
+ end
64
+
65
+ should_set_the_flash_to /bad/i
66
+ should_render_template :new
67
+ should_return_from_session :user_id, "nil"
68
+ end
69
+
70
+ context "a POST to #create with good credentials and remember me" do
71
+ setup do
72
+ post :create, :session => { :email => @user.email,
73
+ :password => @user.password, :remember_me => '1' }
74
+ end
75
+
76
+ should_set_the_flash_to /success/i
77
+ should_redirect_to "@controller.send(:url_after_create)"
78
+ should_return_from_session :user_id, "@user.id"
79
+
80
+ should 'set the cookie' do
81
+ assert ! cookies['auth_token'].empty?
82
+ end
83
+
84
+ should 'set the remember me token in users table' do
85
+ assert_not_nil @user.reload.remember_token
86
+ assert_not_nil @user.reload.remember_token_expires_at
87
+ end
88
+ end
89
+
90
+ context "a POST to #create with bad credentials and remember me" do
91
+ setup do
92
+ post :create, :session => { :email => @user.email,
93
+ :password => "bad value", :remember_me => '1' }
94
+ end
95
+
96
+ should_set_the_flash_to /bad/i
97
+ should_render_template :new
98
+ should_return_from_session :user_id, "nil"
99
+
100
+ should 'not create the cookie' do
101
+ assert_nil cookies['auth_token']
102
+ end
103
+
104
+ should 'not set the remember me token in users table' do
105
+ assert_nil @user.reload.remember_token
106
+ assert_nil @user.reload.remember_token_expires_at
107
+ end
108
+ end
109
+ end
110
+
111
+ public_context do
112
+ context "logging out again" do
113
+ setup { delete :destroy }
114
+ should_redirect_to '@controller.send(:url_after_destroy)'
115
+ end
116
+ end
117
+
118
+ logged_in_user_context do
119
+ context "a DELETE to #destroy without a cookie" do
120
+ setup { delete :destroy }
121
+
122
+ should_set_the_flash_to(/logged out/i)
123
+ should_redirect_to '@controller.send(:url_after_destroy)'
124
+ end
125
+
126
+ context 'a DELETE to #destroy with a cookie' do
127
+ setup do
128
+ cookies['auth_token'] = CGI::Cookie.new 'token', 'value'
129
+ delete :destroy
130
+ end
131
+
132
+ should 'delete the cookie' do
133
+ assert cookies['auth_token'].empty?
134
+ end
135
+
136
+ should 'delete the remember me token in users table' do
137
+ assert_nil @user.reload.remember_token
138
+ assert_nil @user.reload.remember_token_expires_at
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ end
146
+ end
147
+ end
148
+ end