thoughtbot-clearance 0.4.9 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.textile +8 -0
- data/README.textile +5 -184
- data/Rakefile +1 -1
- data/generators/clearance/templates/README +1 -1
- data/generators/clearance_features/clearance_features_generator.rb +2 -0
- data/generators/clearance_features/templates/features/password_reset.feature +1 -9
- data/generators/clearance_features/templates/features/sign_in.feature +7 -7
- data/generators/clearance_features/templates/features/step_definitions/clearance_steps.rb +1 -7
- data/generators/clearance_features/templates/features/step_definitions/factory_girl_steps.rb +5 -0
- data/lib/clearance/app/controllers/passwords_controller.rb +10 -9
- data/lib/clearance/app/controllers/sessions_controller.rb +1 -1
- data/lib/clearance/app/models/user.rb +24 -24
- data/lib/clearance/test/functional/sessions_controller_test.rb +4 -2
- data/lib/clearance/test/unit/user_test.rb +84 -76
- metadata +3 -2
data/CHANGELOG.textile
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
h2. 0.5.0 (2/27/2009)
|
2
|
+
|
3
|
+
* Fixed problem with Cucumber features. (Dan Croak)
|
4
|
+
* Fixed mising HTTP fluency use case. (Dan Croak)
|
5
|
+
* Refactored User#update_password to take just parameters it needs. (Dan
|
6
|
+
Croak)
|
7
|
+
* Refactored User unit tests to be more readable. (Dan Croak)
|
8
|
+
|
1
9
|
h2. 0.4.9 (2/20/2009)
|
2
10
|
|
3
11
|
* Protect passwords & confirmations actions with forbidden filters. (Dan Croak)
|
data/README.textile
CHANGED
@@ -4,6 +4,10 @@ Rails authentication for developers who write tests.
|
|
4
4
|
|
5
5
|
"We have clearance, Clarence.":http://www.youtube.com/v/mNRXJEE3Nz8
|
6
6
|
|
7
|
+
h2. Wiki
|
8
|
+
|
9
|
+
Most information regarding Clearance is on the "Github Wiki":http://wiki.github.com/thoughtbot/clearance.
|
10
|
+
|
7
11
|
h2. Integration with Suspenders
|
8
12
|
|
9
13
|
Clearance is based on the same conventions and tools as "Suspenders":http://github.com/thoughtbot/suspenders If you use it, you already have some configuration mentioned below.
|
@@ -15,7 +19,7 @@ In config/environment.rb:
|
|
15
19
|
config.gem "thoughtbot-clearance",
|
16
20
|
:lib => 'clearance',
|
17
21
|
:source => 'http://gems.github.com',
|
18
|
-
:version => '>= 0.
|
22
|
+
:version => '>= 0.5.0'
|
19
23
|
|
20
24
|
In config/environments/test.rb:
|
21
25
|
|
@@ -45,77 +49,6 @@ A number of files will be created and instructions will be printed.
|
|
45
49
|
|
46
50
|
You may already have some of these files. Don't worry. You'll be asked if you want to overwrite them.
|
47
51
|
|
48
|
-
h2. Modules
|
49
|
-
|
50
|
-
Clearance works by mixing behavior into tests, controllers, and models. For any file that you do not want to overwrite, include the corresponding Clearance module. They are namespaced exactly like the directory structure of a Rails app.
|
51
|
-
|
52
|
-
Application controller example:
|
53
|
-
|
54
|
-
class ApplicationController < ActionController::Base
|
55
|
-
include Clearance::App::Controllers::ApplicationController
|
56
|
-
end
|
57
|
-
|
58
|
-
User model example:
|
59
|
-
|
60
|
-
class User < ActiveRecord::Base
|
61
|
-
include Clearance::App::Models::User
|
62
|
-
end
|
63
|
-
|
64
|
-
User test example:
|
65
|
-
|
66
|
-
class UserTest < Test::Unit::TestCase
|
67
|
-
include Clearance::Test::Unit::UserTest
|
68
|
-
end
|
69
|
-
|
70
|
-
h2. The migration
|
71
|
-
|
72
|
-
The generator will also create a migration to add a "users" table and run it. If the table already exists in the database, the migration will just add fields and indexes that are missing and required by Clearance. If the migration fails, the generator will revert all changes back.
|
73
|
-
|
74
|
-
h2. Routes
|
75
|
-
|
76
|
-
Clearance will add these routes to your routes.rb:
|
77
|
-
|
78
|
-
map.resources :users, :has_one => [:password, :confirmation]
|
79
|
-
map.resource :session
|
80
|
-
map.resources :passwords
|
81
|
-
|
82
|
-
Please note that Clearance depends on root_url, so please make sure that it is defined to *something* in your config/routes.rb:
|
83
|
-
|
84
|
-
map.root :controller => 'users', :action => 'new'
|
85
|
-
|
86
|
-
h2. Environments
|
87
|
-
|
88
|
-
You need to define HOST constant in your environments files. In config/environments/test.rb and config/environments/development.rb it can be:
|
89
|
-
|
90
|
-
HOST = "localhost"
|
91
|
-
|
92
|
-
While in config/environments/production.rb it must be the actual host your application is deployed to because the constant is used by mailers to generate URLs in emails.
|
93
|
-
|
94
|
-
In config/environment.rb:
|
95
|
-
|
96
|
-
DO_NOT_REPLY = "donotreply@example.com"
|
97
|
-
|
98
|
-
h2. The flash
|
99
|
-
|
100
|
-
You will need to display the success, failure, and notice flash messages in your layout. We recommend creating an app/layouts/_flashes.html.erb partial similar to the _flashes partial in Suspenders:
|
101
|
-
|
102
|
-
<pre><code><div id="flash">
|
103
|
-
<% flash.each do |key, value| -%>
|
104
|
-
<div id="flash_<%= key -%>"><%= html_escape(value) %></div>
|
105
|
-
<% end -%>
|
106
|
-
</div></code></pre>
|
107
|
-
|
108
|
-
which is then rendered inside the body tag of your application layout:
|
109
|
-
|
110
|
-
<%= render :partial => 'layouts/flashes' -%>
|
111
|
-
|
112
|
-
h2. Tests
|
113
|
-
|
114
|
-
The tests use "Shoulda":http://thoughtbot.com/projects/shoulda >= 2.9.1 and "Factory Girl":http://thoughtbot.com/projects/factory_girl >= 1.1.5.
|
115
|
-
|
116
|
-
The generator will create a user factory in test/factories/clearance.rb unless
|
117
|
-
you have it defined somewhere else.
|
118
|
-
|
119
52
|
h2. Features
|
120
53
|
|
121
54
|
If you are using Cucumber on your application Clearance comes with a feature generator:
|
@@ -136,118 +69,6 @@ All of the files generated should be new with the exception of the features/supp
|
|
136
69
|
...
|
137
70
|
end
|
138
71
|
|
139
|
-
h2. Usage: basic workflow
|
140
|
-
|
141
|
-
Rails authentication with Clearance uses the standard approach thoughtbot and our clients have agreed upon.
|
142
|
-
|
143
|
-
Users sign up (UsersController) using an email address and a password (User model). They get an email (ClearanceMailer) with a confirmation link to confirm sign up (ConfirmationController).
|
144
|
-
|
145
|
-
Signed up and email confirmed users can sign in and out (SessionsController). If they forget their password, they request an email (ClearanceMailer) containing a link to change it (PasswordsController).
|
146
|
-
|
147
|
-
h2. Usage: actions which require an authenticated user
|
148
|
-
|
149
|
-
To protect your controllers with authentication add:
|
150
|
-
|
151
|
-
class ProtectedController < ApplicationController
|
152
|
-
before_filter :authenticate
|
153
|
-
|
154
|
-
The filter will ensure that only authenticated users can access the controller. If someone who's not signed in tries to access a protected action:
|
155
|
-
|
156
|
-
* the URL is stored in the session,
|
157
|
-
* the user is redirected to sign in page, and
|
158
|
-
* after successful authentication will be be redirected back to that URL.
|
159
|
-
|
160
|
-
h2. Usage: signed_in?, current_user
|
161
|
-
|
162
|
-
Clearance provides two methods that can be used in controllers, helpers, and views to check if current user is authenticated and get the actual user:
|
163
|
-
|
164
|
-
* signed_in?
|
165
|
-
* current_user
|
166
|
-
|
167
|
-
<% if signed_in? -%>
|
168
|
-
Hello, <%= current_user.name %>!
|
169
|
-
<% else -%>
|
170
|
-
Please <%= link_to 'Sign in', new_session_path %>
|
171
|
-
<% end -%>
|
172
|
-
|
173
|
-
h2. Usage: mass assignment
|
174
|
-
|
175
|
-
Please note that all User attributes except email, password and password_confirmation are protected from mass assignment by default. Use attr_accessible to enable it for your custom attributes.
|
176
|
-
|
177
|
-
class User < ActiveRecord::Base
|
178
|
-
include Clearance::App::Models::User
|
179
|
-
attr_accessible :first_name, :last_name
|
180
|
-
end
|
181
|
-
|
182
|
-
h2. Usage: when bad things happen to good users
|
183
|
-
|
184
|
-
Clearance is HTTP fluent. If someone tries to hack the URLs for passwords or confirmations actions, they will be met with a 403 Forbidden HTTP status code. Internally, the Rails app will raise a ApplicationController::Forbidden error. This is a custom error in Clearance.
|
185
|
-
|
186
|
-
Layman's 403 Forbidden definition:
|
187
|
-
|
188
|
-
"The request was a legal request, but the server is refusing to respond to it.
|
189
|
-
Unlike a 401 Unauthorized response, authenticating will make no difference."
|
190
|
-
|
191
|
-
h2. Hooks: return_to parameter
|
192
|
-
|
193
|
-
To specify where to redirect a user (say you want to have a sign in form on every page and redirect the user to the same page) after he/she signs in, you can add a "return_to" parameter to the request (thanks to "Phillippe":http://www.sivarg.com/2009/01/clearance-coming-from-where-your-were.html for the tip):
|
194
|
-
|
195
|
-
<% form_for :session, :url => session_path(:return_to => request.request_uri) do |form| %>
|
196
|
-
|
197
|
-
h2. Hooks: url_after_create, url_after_update, url_after_destroy
|
198
|
-
|
199
|
-
Actions that redirect (create, update, and destroy) in Clearance controllers are customizable. If you want to redirect a user to a specific route after signing in, overwrite the "url_after_create" method in the SessionsController:
|
200
|
-
|
201
|
-
class SessionsController < ApplicationController
|
202
|
-
include Clearance::App::Controllers::SessionsController
|
203
|
-
|
204
|
-
private
|
205
|
-
|
206
|
-
def url_after_create
|
207
|
-
new_blog_post_path
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
There are similar methods in other controllers as well:
|
212
|
-
|
213
|
-
UsersController#url_after_create (sign up)
|
214
|
-
SessionsController#url_after_create (sign in)
|
215
|
-
SessionsController#url_after_destroy (sign out)
|
216
|
-
PasswordsController#url_after_create (password request)
|
217
|
-
PasswordsController#url_after_update (password)
|
218
|
-
ConfirmationsController#url_after_create (confirmation)
|
219
|
-
|
220
|
-
h2. Hooks: sign_user_in
|
221
|
-
|
222
|
-
Say you want to add a last_signed_in_at attribute to your User model. You would want to update it when the User signs in.
|
223
|
-
|
224
|
-
Clearance has a method named sign_user_in that you can overwrite with that logic. Be sure to call sign_in(user) at the end (and write tests!).
|
225
|
-
|
226
|
-
class ApplicationController < ActionController::Base
|
227
|
-
include Clearance::App::Controllers::ApplicationController
|
228
|
-
|
229
|
-
private
|
230
|
-
|
231
|
-
def sign_user_in(user)
|
232
|
-
# store current time to display "last signed in at" message
|
233
|
-
user.update_attribute(:last_signed_in_at, Time.now)
|
234
|
-
sign_in(user)
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
h2. Write your own tests with Clearance's helpers
|
239
|
-
|
240
|
-
sign_in_as, sign_out, should_be_signed_in_as, should_not_be_signed_in, should_deny_access, signed_in_user_context, and more helpers are available in your test suite. Look in vendor/gems/clearance/shoulda_macros for the full list.
|
241
|
-
|
242
|
-
<pre></code>context "when signed in on GET to new" do
|
243
|
-
setup do
|
244
|
-
@user = Factory(:email_confirmed_user)
|
245
|
-
sign_in_as @user
|
246
|
-
get :new
|
247
|
-
end
|
248
|
-
should_be_signed_in_as { @user }
|
249
|
-
end</code></pre>
|
250
|
-
|
251
72
|
h2. Authors
|
252
73
|
|
253
74
|
Clearance was extracted out of "Hoptoad":http://hoptoadapp.com. We merged the authentication code from two of thoughtbot's client's Rails apps. The following people have made significant contributions, suggestions, and generally improved the library. Thank you!
|
data/Rakefile
CHANGED
@@ -51,7 +51,7 @@ task :default => ['test:all', 'test:features']
|
|
51
51
|
|
52
52
|
gem_spec = Gem::Specification.new do |gem_spec|
|
53
53
|
gem_spec.name = "clearance"
|
54
|
-
gem_spec.version = "0.
|
54
|
+
gem_spec.version = "0.5.0"
|
55
55
|
gem_spec.summary = "Rails authentication for developers who write tests."
|
56
56
|
gem_spec.email = "support@thoughtbot.com"
|
57
57
|
gem_spec.homepage = "http://github.com/thoughtbot/clearance"
|
@@ -40,6 +40,6 @@ In config/environment.rb:
|
|
40
40
|
3. Clearance depends on root_url, so please make sure that it is defined to
|
41
41
|
*something* in your config/routes.rb:
|
42
42
|
|
43
|
-
map.root :controller => '
|
43
|
+
map.root :controller => 'home'
|
44
44
|
|
45
45
|
*******************************************************************************
|
@@ -3,8 +3,10 @@ class ClearanceFeaturesGenerator < Rails::Generator::Base
|
|
3
3
|
def manifest
|
4
4
|
record do |m|
|
5
5
|
m.directory File.join("features", "step_definitions")
|
6
|
+
m.directory File.join("features", "support")
|
6
7
|
|
7
8
|
["features/step_definitions/clearance_steps.rb",
|
9
|
+
"features/step_definitions/factory_girl_steps.rb",
|
8
10
|
"features/support/paths.rb",
|
9
11
|
"features/sign_in.feature",
|
10
12
|
"features/sign_out.feature",
|
@@ -4,7 +4,7 @@ Feature: Password reset
|
|
4
4
|
Should be able to reset it
|
5
5
|
|
6
6
|
Scenario: User is not signed up
|
7
|
-
Given
|
7
|
+
Given no user exists with an email of "email@person.com"
|
8
8
|
When I request password reset link to be sent to "email@person.com"
|
9
9
|
Then I should see "Unknown email"
|
10
10
|
|
@@ -29,11 +29,3 @@ Feature: Password reset
|
|
29
29
|
When I sign out
|
30
30
|
And I sign in as "email@person.com/newpassword"
|
31
31
|
Then I should be signed in
|
32
|
-
|
33
|
-
Scenario: User requests password reset without token
|
34
|
-
Given a user exists with an email of "user@one.com"
|
35
|
-
When I try to change the password of "user@one.com" without token
|
36
|
-
Then I should be forbidden
|
37
|
-
|
38
|
-
|
39
|
-
|
@@ -2,35 +2,35 @@ Feature: Sign in
|
|
2
2
|
In order to get access to protected sections of the site
|
3
3
|
A user
|
4
4
|
Should be able to sign in
|
5
|
-
|
6
|
-
|
7
|
-
Given
|
5
|
+
|
6
|
+
Scenario: User is not signed up
|
7
|
+
Given no user exists with an email of "email@person.com"
|
8
8
|
When I go to the sign in page
|
9
9
|
And I sign in as "email@person.com/password"
|
10
10
|
Then I should see "Bad email or password"
|
11
11
|
And I should not be signed in
|
12
|
-
|
12
|
+
|
13
13
|
Scenario: User is not confirmed
|
14
14
|
Given I signed up with "email@person.com/password"
|
15
15
|
When I go to the sign in page
|
16
16
|
And I sign in as "email@person.com/password"
|
17
17
|
Then I should see "User has not confirmed email"
|
18
18
|
And I should not be signed in
|
19
|
-
|
19
|
+
|
20
20
|
Scenario: User enters wrong password
|
21
21
|
Given I am signed up and confirmed as "email@person.com/password"
|
22
22
|
When I go to the sign in page
|
23
23
|
And I sign in as "email@person.com/wrongpassword"
|
24
24
|
Then I should see "Bad email or password"
|
25
25
|
And I should not be signed in
|
26
|
-
|
26
|
+
|
27
27
|
Scenario: User signs in successfully
|
28
28
|
Given I am signed up and confirmed as "email@person.com/password"
|
29
29
|
When I go to the sign in page
|
30
30
|
And I sign in as "email@person.com/password"
|
31
31
|
Then I should see "Signed in successfully"
|
32
32
|
And I should be signed in
|
33
|
-
|
33
|
+
|
34
34
|
Scenario: User signs in and checks "remember me"
|
35
35
|
Given I am signed up and confirmed as "email@person.com/password"
|
36
36
|
When I go to the sign in page
|
@@ -6,13 +6,7 @@ end
|
|
6
6
|
|
7
7
|
# Database
|
8
8
|
|
9
|
-
|
10
|
-
Given /^an? #{name} exists with an? (.*) of "([^"]*)"$/ do |attr, value|
|
11
|
-
Factory(name, attr.gsub(' ', '_') => value)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
Given /^there is no user with "(.*)"$/ do |email|
|
9
|
+
Given /^no user exists with an email of "(.*)"$/ do |email|
|
16
10
|
assert_nil User.find_by_email(email)
|
17
11
|
end
|
18
12
|
|
@@ -6,14 +6,14 @@ module Clearance
|
|
6
6
|
def self.included(controller)
|
7
7
|
controller.send(:include, Actions)
|
8
8
|
controller.send(:include, PrivateMethods)
|
9
|
-
|
9
|
+
|
10
10
|
controller.class_eval do
|
11
11
|
before_filter :forbid_missing_token, :only => [:edit, :update]
|
12
12
|
before_filter :forbid_non_existant_user, :only => [:edit, :update]
|
13
13
|
filter_parameter_logging :password, :password_confirmation
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
module Actions
|
18
18
|
def new
|
19
19
|
end
|
@@ -38,8 +38,9 @@ module Clearance
|
|
38
38
|
|
39
39
|
def update
|
40
40
|
@user = User.find_by_id_and_token(params[:user_id], params[:token])
|
41
|
-
|
42
|
-
if @user.update_password(params[:user]
|
41
|
+
|
42
|
+
if @user.update_password(params[:user][:password],
|
43
|
+
params[:user][:password_confirmation])
|
43
44
|
sign_user_in(@user)
|
44
45
|
redirect_to url_after_update
|
45
46
|
else
|
@@ -47,16 +48,16 @@ module Clearance
|
|
47
48
|
end
|
48
49
|
end
|
49
50
|
end
|
50
|
-
|
51
|
+
|
51
52
|
module PrivateMethods
|
52
53
|
private
|
53
|
-
|
54
|
+
|
54
55
|
def forbid_missing_token
|
55
56
|
if params[:token].blank?
|
56
57
|
raise ActionController::Forbidden, "missing token"
|
57
58
|
end
|
58
59
|
end
|
59
|
-
|
60
|
+
|
60
61
|
def forbid_non_existant_user
|
61
62
|
unless User.find_by_id_and_token(params[:user_id], params[:token])
|
62
63
|
raise ActionController::Forbidden, "non-existant user"
|
@@ -66,12 +67,12 @@ module Clearance
|
|
66
67
|
def url_after_create
|
67
68
|
new_session_url
|
68
69
|
end
|
69
|
-
|
70
|
+
|
70
71
|
def url_after_update
|
71
72
|
root_url
|
72
73
|
end
|
73
74
|
end
|
74
|
-
|
75
|
+
|
75
76
|
end
|
76
77
|
end
|
77
78
|
end
|
@@ -19,7 +19,7 @@ module Clearance
|
|
19
19
|
params[:session][:password])
|
20
20
|
if @user.nil?
|
21
21
|
flash.now[:notice] = "Bad email or password."
|
22
|
-
render :action => :new
|
22
|
+
render :action => :new, :status => :unauthorized
|
23
23
|
else
|
24
24
|
if @user.email_confirmed?
|
25
25
|
remember(@user) if remember?
|
@@ -4,11 +4,11 @@ module Clearance
|
|
4
4
|
module App
|
5
5
|
module Models
|
6
6
|
module User
|
7
|
-
|
7
|
+
|
8
8
|
def self.included(model)
|
9
9
|
model.extend ClassMethods
|
10
10
|
model.send(:include, InstanceMethods)
|
11
|
-
|
11
|
+
|
12
12
|
model.class_eval do
|
13
13
|
attr_accessible :email, :password, :password_confirmation
|
14
14
|
attr_accessor :password, :password_confirmation
|
@@ -22,7 +22,7 @@ module Clearance
|
|
22
22
|
before_save :initialize_salt, :encrypt_password, :initialize_token
|
23
23
|
end
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
module InstanceMethods
|
27
27
|
def authenticated?(password)
|
28
28
|
encrypted_password == encrypt(password)
|
@@ -37,13 +37,7 @@ module Clearance
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def remember_me!
|
40
|
-
remember_me_until 2.weeks.from_now.utc
|
41
|
-
end
|
42
|
-
|
43
|
-
def remember_me_until(time)
|
44
|
-
self.token_expires_at = time
|
45
|
-
self.token = encrypt("--#{token_expires_at}--#{password}--")
|
46
|
-
save(false)
|
40
|
+
remember_me_until! 2.weeks.from_now.utc
|
47
41
|
end
|
48
42
|
|
49
43
|
def forget_me!
|
@@ -62,15 +56,15 @@ module Clearance
|
|
62
56
|
save(false)
|
63
57
|
end
|
64
58
|
|
65
|
-
def update_password(
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
59
|
+
def update_password(pass, pass_confirmation)
|
60
|
+
self.password = pass
|
61
|
+
self.password_confirmation = pass_confirmation
|
62
|
+
clear_token if valid?
|
63
|
+
save
|
70
64
|
end
|
71
|
-
|
65
|
+
|
72
66
|
protected
|
73
|
-
|
67
|
+
|
74
68
|
def generate_hash(string)
|
75
69
|
Digest::SHA1.hexdigest(string)
|
76
70
|
end
|
@@ -85,17 +79,17 @@ module Clearance
|
|
85
79
|
return if password.blank?
|
86
80
|
self.encrypted_password = encrypt(password)
|
87
81
|
end
|
88
|
-
|
82
|
+
|
89
83
|
def generate_token
|
90
84
|
self.token = encrypt("--#{Time.now.utc.to_s}--#{password}--")
|
91
|
-
self.token_expires_at = nil
|
85
|
+
self.token_expires_at = nil
|
92
86
|
end
|
93
|
-
|
87
|
+
|
94
88
|
def clear_token
|
95
89
|
self.token = nil
|
96
|
-
self.token_expires_at = nil
|
90
|
+
self.token_expires_at = nil
|
97
91
|
end
|
98
|
-
|
92
|
+
|
99
93
|
def initialize_token
|
100
94
|
generate_token if new_record?
|
101
95
|
end
|
@@ -103,15 +97,21 @@ module Clearance
|
|
103
97
|
def password_required?
|
104
98
|
encrypted_password.blank? || !password.blank?
|
105
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
106
|
end
|
107
|
-
|
107
|
+
|
108
108
|
module ClassMethods
|
109
109
|
def authenticate(email, password)
|
110
110
|
user = find(:first, :conditions => ['email = ?', email.to_s])
|
111
111
|
user && user.authenticated?(password) ? user : nil
|
112
112
|
end
|
113
113
|
end
|
114
|
-
|
114
|
+
|
115
115
|
end
|
116
116
|
end
|
117
117
|
end
|
@@ -11,7 +11,7 @@ module Clearance
|
|
11
11
|
context "on GET to /sessions/new" do
|
12
12
|
setup { get :new }
|
13
13
|
|
14
|
-
should_respond_with
|
14
|
+
should_respond_with :success
|
15
15
|
should_render_template :new
|
16
16
|
should_not_set_the_flash
|
17
17
|
|
@@ -30,12 +30,12 @@ module Clearance
|
|
30
30
|
end
|
31
31
|
|
32
32
|
should_deny_access(:flash => /User has not confirmed email. Confirmation email will be resent./i)
|
33
|
+
should_respond_with :unauthorized
|
33
34
|
|
34
35
|
should "send the confirmation email" do
|
35
36
|
assert_not_nil email = ActionMailer::Base.deliveries[0]
|
36
37
|
assert_match /account confirmation/i, email.subject
|
37
38
|
end
|
38
|
-
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -65,6 +65,7 @@ module Clearance
|
|
65
65
|
end
|
66
66
|
|
67
67
|
should_set_the_flash_to /bad/i
|
68
|
+
should_respond_with :unauthorized
|
68
69
|
should_render_template :new
|
69
70
|
should_not_be_signed_in
|
70
71
|
end
|
@@ -100,6 +101,7 @@ module Clearance
|
|
100
101
|
end
|
101
102
|
|
102
103
|
should_set_the_flash_to /bad/i
|
104
|
+
should_respond_with :unauthorized
|
103
105
|
should_render_template :new
|
104
106
|
should_return_from_session :user_id, "nil"
|
105
107
|
|
@@ -2,34 +2,38 @@ module Clearance
|
|
2
2
|
module Test
|
3
3
|
module Unit
|
4
4
|
module UserTest
|
5
|
-
|
5
|
+
|
6
6
|
def self.included(unit_test)
|
7
7
|
unit_test.class_eval do
|
8
|
-
|
8
|
+
|
9
9
|
should_not_allow_mass_assignment_of :email_confirmed,
|
10
10
|
:salt, :encrypted_password,
|
11
11
|
:token, :token_expires_at
|
12
|
-
|
12
|
+
|
13
13
|
# signing up
|
14
|
-
|
14
|
+
|
15
15
|
context "When signing up" do
|
16
|
-
should_validate_presence_of
|
17
|
-
should_allow_values_for
|
18
|
-
should_not_allow_values_for
|
19
|
-
should_not_allow_values_for
|
20
|
-
|
21
|
-
|
22
|
-
:
|
23
|
-
|
16
|
+
should_validate_presence_of :email, :password
|
17
|
+
should_allow_values_for :email, "foo@example.com"
|
18
|
+
should_not_allow_values_for :email, "foo"
|
19
|
+
should_not_allow_values_for :email, "example.com"
|
20
|
+
|
21
|
+
should "require password confirmation on create" do
|
22
|
+
user = Factory.build(:user, :password => 'blah',
|
23
|
+
:password_confirmation => 'boogidy')
|
24
|
+
assert !user.save
|
25
|
+
assert user.errors.on(:password)
|
26
|
+
end
|
27
|
+
|
24
28
|
should "initialize salt" do
|
25
29
|
assert_not_nil Factory(:user).salt
|
26
30
|
end
|
27
|
-
|
28
|
-
should "initialize token
|
31
|
+
|
32
|
+
should "initialize token without expiry date" do
|
29
33
|
assert_not_nil Factory(:user).token
|
30
|
-
assert_nil
|
34
|
+
assert_nil Factory(:user).token_expires_at
|
31
35
|
end
|
32
|
-
|
36
|
+
|
33
37
|
context "encrypt password" do
|
34
38
|
setup do
|
35
39
|
@salt = "salt"
|
@@ -42,26 +46,24 @@ module Clearance
|
|
42
46
|
@expected = Digest::SHA1.hexdigest("--#{@salt}--#{@password}--")
|
43
47
|
end
|
44
48
|
|
45
|
-
should "create an encrypted password using
|
46
|
-
assert_equal
|
47
|
-
assert_not_equal @password, @user.encrypted_password
|
49
|
+
should "create an encrypted password using SHA1 encryption" do
|
50
|
+
assert_equal @expected, @user.encrypted_password
|
48
51
|
end
|
49
52
|
end
|
50
|
-
|
53
|
+
|
51
54
|
should "store email in exact case" do
|
52
55
|
user = Factory(:user, :email => "John.Doe@example.com")
|
53
56
|
assert_equal "John.Doe@example.com", user.email
|
54
57
|
end
|
55
58
|
end
|
56
|
-
|
59
|
+
|
57
60
|
context "When multiple users have signed up" do
|
58
61
|
setup { @user = Factory(:user) }
|
59
|
-
|
60
62
|
should_validate_uniqueness_of :email
|
61
63
|
end
|
62
|
-
|
64
|
+
|
63
65
|
# confirming email
|
64
|
-
|
66
|
+
|
65
67
|
context "A user without email confirmation" do
|
66
68
|
setup do
|
67
69
|
@user = Factory(:user)
|
@@ -77,34 +79,34 @@ module Clearance
|
|
77
79
|
should "have confirmed their email" do
|
78
80
|
assert @user.email_confirmed?
|
79
81
|
end
|
80
|
-
|
82
|
+
|
81
83
|
should "reset token" do
|
82
84
|
assert_nil @user.token
|
83
85
|
end
|
84
86
|
end
|
85
87
|
end
|
86
|
-
|
88
|
+
|
87
89
|
# authenticating
|
88
|
-
|
90
|
+
|
89
91
|
context "A user" do
|
90
92
|
setup do
|
91
93
|
@user = Factory(:user)
|
92
94
|
@password = @user.password
|
93
95
|
end
|
94
|
-
|
96
|
+
|
95
97
|
should "authenticate with good credentials" do
|
96
98
|
assert User.authenticate(@user.email, @password)
|
97
99
|
assert @user.authenticated?(@password)
|
98
100
|
end
|
99
|
-
|
101
|
+
|
100
102
|
should "not authenticate with bad credentials" do
|
101
|
-
assert ! User.authenticate(@user.email, '
|
102
|
-
assert ! @user.authenticated?('
|
103
|
+
assert ! User.authenticate(@user.email, 'bad_password')
|
104
|
+
assert ! @user.authenticated?('bad_password')
|
103
105
|
end
|
104
106
|
end
|
105
107
|
|
106
108
|
# remember me
|
107
|
-
|
109
|
+
|
108
110
|
context "When authenticating with remember_me!" do
|
109
111
|
setup do
|
110
112
|
@user = Factory(:email_confirmed_user)
|
@@ -115,9 +117,9 @@ module Clearance
|
|
115
117
|
|
116
118
|
should "set the remember token and expiration date" do
|
117
119
|
assert_not_equal @token, @user.token
|
118
|
-
assert_not_nil
|
120
|
+
assert_not_nil @user.token_expires_at
|
119
121
|
end
|
120
|
-
|
122
|
+
|
121
123
|
should "remember user when token expires in the future" do
|
122
124
|
@user.update_attribute :token_expires_at,
|
123
125
|
2.weeks.from_now.utc
|
@@ -129,14 +131,14 @@ module Clearance
|
|
129
131
|
2.weeks.ago.utc
|
130
132
|
assert ! @user.remember?
|
131
133
|
end
|
132
|
-
|
134
|
+
|
133
135
|
should "not remember user when token expiry date is not set" do
|
134
136
|
@user.update_attribute :token_expires_at, nil
|
135
137
|
assert ! @user.remember?
|
136
|
-
end
|
137
|
-
|
138
|
+
end
|
139
|
+
|
138
140
|
# logging out
|
139
|
-
|
141
|
+
|
140
142
|
context "forget_me!" do
|
141
143
|
setup { @user.forget_me! }
|
142
144
|
|
@@ -150,75 +152,81 @@ module Clearance
|
|
150
152
|
end
|
151
153
|
end
|
152
154
|
end
|
153
|
-
|
155
|
+
|
154
156
|
# updating password
|
155
|
-
|
157
|
+
|
156
158
|
context "An email confirmed user" do
|
157
|
-
setup
|
159
|
+
setup do
|
160
|
+
@user = Factory(:email_confirmed_user)
|
161
|
+
@old_encrypted_password = @user.encrypted_password
|
162
|
+
end
|
158
163
|
|
159
|
-
context "who
|
164
|
+
context "who updates password with confirmation" do
|
160
165
|
setup do
|
161
|
-
@user.
|
162
|
-
@user.password_confirmation = "new_password"
|
163
|
-
@user.save
|
166
|
+
@user.update_password("new_password", "new_password")
|
164
167
|
end
|
165
168
|
|
166
|
-
|
169
|
+
should "change encrypted password" do
|
170
|
+
assert_not_equal @user.encrypted_password,
|
171
|
+
@old_encrypted_password
|
172
|
+
end
|
167
173
|
end
|
168
174
|
end
|
169
|
-
|
175
|
+
|
170
176
|
# recovering forgotten password
|
171
|
-
|
177
|
+
|
172
178
|
context "An email confirmed user" do
|
173
179
|
setup do
|
174
|
-
@user = Factory(:
|
180
|
+
@user = Factory(:email_confirmed_user)
|
181
|
+
@old_encrypted_password = @user.encrypted_password
|
175
182
|
@user.confirm_email!
|
176
183
|
end
|
177
|
-
|
178
|
-
context "who
|
184
|
+
|
185
|
+
context "who requests password reminder" do
|
179
186
|
setup do
|
180
187
|
assert_nil @user.token
|
181
|
-
@user.forgot_password!
|
188
|
+
@user.forgot_password!
|
182
189
|
end
|
183
|
-
|
190
|
+
|
184
191
|
should "generate token" do
|
185
192
|
assert_not_nil @user.token
|
186
193
|
end
|
187
|
-
|
194
|
+
|
188
195
|
context "and then updates password" do
|
189
|
-
context 'with
|
196
|
+
context 'with confirmation' do
|
190
197
|
setup do
|
191
|
-
@user.update_password(
|
192
|
-
:password => "new_password",
|
193
|
-
:password_confirmation => "new_password"
|
194
|
-
)
|
198
|
+
@user.update_password("new_password", "new_password")
|
195
199
|
end
|
196
|
-
|
197
|
-
|
198
|
-
|
200
|
+
|
201
|
+
should "change encrypted password" do
|
202
|
+
assert_not_equal @user.encrypted_password,
|
203
|
+
@old_encrypted_password
|
204
|
+
end
|
205
|
+
|
199
206
|
should "clear token" do
|
200
207
|
assert_nil @user.token
|
201
|
-
end
|
208
|
+
end
|
202
209
|
end
|
203
|
-
|
204
|
-
context '
|
210
|
+
|
211
|
+
context 'without confirmation' do
|
205
212
|
setup do
|
206
|
-
@user.update_password(
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
213
|
+
@user.update_password("new_password", "")
|
214
|
+
end
|
215
|
+
|
216
|
+
should "not change encrypted password" do
|
217
|
+
assert_equal @user.encrypted_password,
|
218
|
+
@old_encrypted_password
|
219
|
+
end
|
220
|
+
|
212
221
|
should "not clear token" do
|
213
222
|
assert_not_nil @user.token
|
214
|
-
end
|
223
|
+
end
|
215
224
|
end
|
216
|
-
end
|
225
|
+
end
|
217
226
|
end
|
218
|
-
|
219
|
-
|
227
|
+
|
220
228
|
end
|
221
|
-
|
229
|
+
|
222
230
|
end
|
223
231
|
end
|
224
232
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thoughtbot-clearance
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- thoughtbot, inc.
|
@@ -21,7 +21,7 @@ autorequire:
|
|
21
21
|
bindir: bin
|
22
22
|
cert_chain: []
|
23
23
|
|
24
|
-
date: 2009-02-
|
24
|
+
date: 2009-02-26 21:00:00 -08:00
|
25
25
|
default_executable:
|
26
26
|
dependencies: []
|
27
27
|
|
@@ -95,6 +95,7 @@ files:
|
|
95
95
|
- generators/clearance_features/templates/features/sign_up.feature
|
96
96
|
- generators/clearance_features/templates/features/step_definitions
|
97
97
|
- generators/clearance_features/templates/features/step_definitions/clearance_steps.rb
|
98
|
+
- generators/clearance_features/templates/features/step_definitions/factory_girl_steps.rb
|
98
99
|
- generators/clearance_features/templates/features/support
|
99
100
|
- generators/clearance_features/templates/features/support/paths.rb
|
100
101
|
- generators/clearance_features/USAGE
|