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.
- data/LICENSE +21 -0
- data/README.textile +136 -0
- data/Rakefile +83 -0
- data/TODO.textile +24 -0
- data/generators/clearance/USAGE +1 -0
- data/generators/clearance/clearance_generator.rb +69 -0
- data/generators/clearance/templates/app/controllers/application.rb +5 -0
- data/generators/clearance/templates/app/controllers/confirmations_controller.rb +3 -0
- data/generators/clearance/templates/app/controllers/passwords_controller.rb +3 -0
- data/generators/clearance/templates/app/controllers/sessions_controller.rb +3 -0
- data/generators/clearance/templates/app/controllers/users_controller.rb +3 -0
- data/generators/clearance/templates/app/models/clearance_mailer.rb +5 -0
- data/generators/clearance/templates/app/models/user.rb +3 -0
- data/generators/clearance/templates/app/views/clearance_mailer/change_password.html.erb +6 -0
- data/generators/clearance/templates/app/views/clearance_mailer/confirmation.html.erb +1 -0
- data/generators/clearance/templates/app/views/confirmations/new.html.erb +6 -0
- data/generators/clearance/templates/app/views/passwords/edit.html.erb +23 -0
- data/generators/clearance/templates/app/views/passwords/new.html.erb +15 -0
- data/generators/clearance/templates/app/views/sessions/new.html.erb +26 -0
- data/generators/clearance/templates/app/views/users/_form.html.erb +13 -0
- data/generators/clearance/templates/app/views/users/edit.html.erb +4 -0
- data/generators/clearance/templates/app/views/users/new.html.erb +4 -0
- data/generators/clearance/templates/test/factories.rb +9 -0
- data/generators/clearance/templates/test/functional/confirmations_controller_test.rb +5 -0
- data/generators/clearance/templates/test/functional/passwords_controller_test.rb +5 -0
- data/generators/clearance/templates/test/functional/sessions_controller_test.rb +5 -0
- data/generators/clearance/templates/test/functional/users_controller_test.rb +5 -0
- data/generators/clearance/templates/test/unit/clearance_mailer_test.rb +6 -0
- data/generators/clearance/templates/test/unit/user_test.rb +5 -0
- data/lib/clearance.rb +15 -0
- data/lib/clearance/app/controllers/application_controller.rb +84 -0
- data/lib/clearance/app/controllers/confirmations_controller.rb +46 -0
- data/lib/clearance/app/controllers/passwords_controller.rb +67 -0
- data/lib/clearance/app/controllers/sessions_controller.rb +79 -0
- data/lib/clearance/app/controllers/users_controller.rb +47 -0
- data/lib/clearance/app/models/clearance_mailer.rb +33 -0
- data/lib/clearance/app/models/user.rb +86 -0
- data/lib/clearance/test/functional/confirmations_controller_test.rb +85 -0
- data/lib/clearance/test/functional/passwords_controller_test.rb +188 -0
- data/lib/clearance/test/functional/sessions_controller_test.rb +148 -0
- data/lib/clearance/test/functional/users_controller_test.rb +69 -0
- data/lib/clearance/test/test_helper.rb +94 -0
- data/lib/clearance/test/unit/clearance_mailer_test.rb +63 -0
- data/lib/clearance/test/unit/user_test.rb +208 -0
- data/lib/clearance/version.rb +7 -0
- 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
|