gravis-clearance 0.3.7

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 +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