gravis-clearance 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/LICENSE +21 -0
  2. data/README.textile +165 -0
  3. data/Rakefile +46 -0
  4. data/TODO.textile +22 -0
  5. data/generators/clearance/USAGE +1 -0
  6. data/generators/clearance/clearance_generator.rb +73 -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 +93 -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 +67 -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 +222 -0
  45. data/lib/clearance/version.rb +7 -0
  46. metadata +120 -0
@@ -0,0 +1,93 @@
1
+ require 'digest/sha1'
2
+
3
+ module Clearance
4
+ module App
5
+ module Models
6
+ module User
7
+
8
+ def self.included(base)
9
+ base.class_eval do
10
+
11
+ attr_accessible :email, :password, :password_confirmation
12
+ attr_accessor :password, :password_confirmation
13
+
14
+ validates_presence_of :email
15
+ validates_presence_of :password, :if => :password_required?
16
+ validates_confirmation_of :password, :if => :password_required?
17
+ validates_uniqueness_of :email
18
+ validates_format_of :email, :with => %r{.+@.+\..+}
19
+
20
+ before_save :initialize_salt, :encrypt_password, :downcase_email
21
+
22
+ extend ClassMethods
23
+ include InstanceMethods
24
+
25
+ protected
26
+
27
+ include ProtectedInstanceMethods
28
+
29
+ end
30
+ end
31
+
32
+ module ClassMethods
33
+ def authenticate(email, password)
34
+ user = find_by_email email.downcase
35
+ user && user.authenticated?(password) ? user : nil
36
+ end
37
+ end
38
+
39
+ module InstanceMethods
40
+ def authenticated?(password)
41
+ crypted_password == encrypt(password)
42
+ end
43
+
44
+ def encrypt(password)
45
+ Digest::SHA1.hexdigest "--#{salt}--#{password}--"
46
+ end
47
+
48
+ def remember_token?
49
+ remember_token_expires_at && Time.now.utc < remember_token_expires_at
50
+ end
51
+
52
+ def remember_me!
53
+ remember_me_until 2.weeks.from_now.utc
54
+ end
55
+
56
+ def remember_me_until(time)
57
+ self.update_attribute :remember_token_expires_at, time
58
+ self.update_attribute :remember_token, encrypt("#{email}--#{remember_token_expires_at}")
59
+ end
60
+
61
+ def forget_me!
62
+ self.update_attribute :remember_token_expires_at, nil
63
+ self.update_attribute :remember_token, nil
64
+ end
65
+
66
+ def confirm!
67
+ self.update_attribute :confirmed, true
68
+ end
69
+ end
70
+
71
+ module ProtectedInstanceMethods
72
+ def initialize_salt
73
+ self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{email}--") if new_record?
74
+ end
75
+
76
+ def encrypt_password
77
+ return if password.blank?
78
+ self.crypted_password = encrypt(password)
79
+ end
80
+
81
+ def password_required?
82
+ crypted_password.blank? || !password.blank?
83
+ end
84
+
85
+ def downcase_email
86
+ self.email.downcase!
87
+ end
88
+ end
89
+
90
+ end
91
+ end
92
+ end
93
+ 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