thoughtbot-clearance 0.4.8 → 0.4.9
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.textile +29 -18
- data/README.textile +29 -24
- data/Rakefile +4 -3
- data/generators/clearance/templates/README +1 -1
- data/generators/clearance/templates/app/views/passwords/edit.html.erb +1 -1
- data/generators/clearance/templates/app/views/users/_form.html.erb +1 -1
- data/generators/clearance_features/templates/features/password_reset.feature +9 -2
- data/generators/clearance_features/templates/features/sign_up.feature +2 -2
- data/generators/clearance_features/templates/features/step_definitions/clearance_steps.rb +18 -2
- data/generators/clearance_features/templates/features/support/paths.rb +0 -1
- data/lib/clearance.rb +2 -0
- data/lib/clearance/app/controllers/confirmations_controller.rb +18 -12
- data/lib/clearance/app/controllers/passwords_controller.rb +15 -6
- data/lib/clearance/lib/extensions/errors.rb +4 -0
- data/lib/clearance/lib/extensions/rescue.rb +1 -0
- data/lib/clearance/test/functional/confirmations_controller_test.rb +29 -38
- data/lib/clearance/test/functional/passwords_controller_test.rb +124 -118
- data/shoulda_macros/clearance.rb +10 -24
- metadata +12 -2
data/CHANGELOG.textile
CHANGED
@@ -1,32 +1,43 @@
|
|
1
|
+
h2. 0.4.9 (2/20/2009)
|
2
|
+
|
3
|
+
* Protect passwords & confirmations actions with forbidden filters. (Dan Croak)
|
4
|
+
* Return 403 Forbidden status code in those cases. (Tim Pope)
|
5
|
+
* Test 403 Forbidden status code in Cucumber feature. (Dan Croak, Joe Ferris)
|
6
|
+
* Raise custom ActionController::Forbidden error internally. (Joe Ferris, Mike Burns, Jason Morrison)
|
7
|
+
* Test ActionController::Forbidden error is raised in functional test. (Joe Ferris, Mike Burns, Dan Croak)
|
8
|
+
* [#45] Fixed bug that allowed anyone to edit another user's password (Marcel Görner)
|
9
|
+
* Required Factory Girl >= 1.2.0. (Dan Croak)
|
10
|
+
|
1
11
|
h2. 0.4.8 (2/16/2009)
|
2
12
|
|
3
|
-
*
|
4
|
-
*
|
5
|
-
*
|
6
|
-
*
|
7
|
-
*
|
13
|
+
* Added support paths for Cucumber. (Ben Mabey)
|
14
|
+
* Added documentation for the flash. (Ben Mabey)
|
15
|
+
* Generators require "test_helper" instead of File.join. for rr compatibility. (Joe Ferris)
|
16
|
+
* Removed interpolated email address from flash message to make i18n easier. (Bence Nagy)
|
17
|
+
* Standardized flash messages that refer to email delivery. (Dan Croak)
|
8
18
|
|
9
19
|
h2. 0.4.7 (2/12/2009)
|
10
20
|
|
11
|
-
*
|
21
|
+
* Removed Clearance::Test::TestHelper so there is one less setup step. (Dan Croak)
|
22
|
+
* All test helpers now in shoulda_macros. (Dan Croak)
|
12
23
|
|
13
24
|
h2. 0.4.6 (2/11/2009)
|
14
25
|
|
15
|
-
*
|
16
|
-
*
|
26
|
+
* Made the modules behave like mixins again. (hat-tip Eloy Duran)
|
27
|
+
* Created Actions and PrivateMethods modules on controllers for future RDoc reasons. (Dan Croak, Joe Ferris)
|
17
28
|
|
18
29
|
h2. 0.4.5 (2/9/2009)
|
19
30
|
|
20
|
-
* [#43] Removed email downcasing
|
21
|
-
* [#42] Removed dependency on Mocha.
|
22
|
-
* Required Shoulda >= 2.9.1.
|
23
|
-
* Added password reset feature to clearance_features generator.
|
24
|
-
* Removed unnecessary session[:salt].
|
25
|
-
* [#41] Only store location for session[:return_to] for GET requests.
|
26
|
-
* Audited "sign up" naming convention. "Register" had slipped in a few places.
|
27
|
-
* Switched to SHA1 encryption. Cypher doesn't matter much for email confirmation, password reset. Better to have shorter hashes in the emails for clients who line break on 72 chars.
|
31
|
+
* [#43] Removed email downcasing because local-part is case sensitive per RFC5321. (Dan Croak)
|
32
|
+
* [#42] Removed dependency on Mocha. (Dan Croak)
|
33
|
+
* Required Shoulda >= 2.9.1. (Dan Croak)
|
34
|
+
* Added password reset feature to clearance_features generator. (Eugene Bolshakov, Dan Croak)
|
35
|
+
* Removed unnecessary session[:salt]. (Dan Croak)
|
36
|
+
* [#41] Only store location for session[:return_to] for GET requests. (Dan Croak)
|
37
|
+
* Audited "sign up" naming convention. "Register" had slipped in a few places. (Dan Croak)
|
38
|
+
* Switched to SHA1 encryption. Cypher doesn't matter much for email confirmation, password reset. Better to have shorter hashes in the emails for clients who line break on 72 chars. (Dan Croak)
|
28
39
|
|
29
40
|
h2. 0.4.4 (2/2/2009)
|
30
41
|
|
31
|
-
* Added a generator for Cucumber features
|
32
|
-
* Standarized naming for "Sign up," "Sign in," and "Sign out"
|
42
|
+
* Added a generator for Cucumber features. (Joe Ferris, Dan Croak)
|
43
|
+
* Standarized naming for "Sign up," "Sign in," and "Sign out". (Dan Croak)
|
data/README.textile
CHANGED
@@ -15,7 +15,7 @@ In config/environment.rb:
|
|
15
15
|
config.gem "thoughtbot-clearance",
|
16
16
|
:lib => 'clearance',
|
17
17
|
:source => 'http://gems.github.com',
|
18
|
-
:version => '>= 0.4.
|
18
|
+
:version => '>= 0.4.9'
|
19
19
|
|
20
20
|
In config/environments/test.rb:
|
21
21
|
|
@@ -26,7 +26,7 @@ In config/environments/test.rb:
|
|
26
26
|
config.gem 'thoughtbot-factory_girl',
|
27
27
|
:lib => 'factory_girl',
|
28
28
|
:source => "http://gems.github.com",
|
29
|
-
:version => '>= 1.
|
29
|
+
:version => '>= 1.2.0'
|
30
30
|
|
31
31
|
Then:
|
32
32
|
|
@@ -81,7 +81,7 @@ Clearance will add these routes to your routes.rb:
|
|
81
81
|
|
82
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
83
|
|
84
|
-
map.root :controller => '
|
84
|
+
map.root :controller => 'users', :action => 'new'
|
85
85
|
|
86
86
|
h2. Environments
|
87
87
|
|
@@ -99,11 +99,11 @@ h2. The flash
|
|
99
99
|
|
100
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
101
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
107
|
|
108
108
|
which is then rendered inside the body tag of your application layout:
|
109
109
|
|
@@ -140,7 +140,7 @@ h2. Usage: basic workflow
|
|
140
140
|
|
141
141
|
Rails authentication with Clearance uses the standard approach thoughtbot and our clients have agreed upon.
|
142
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
|
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
144
|
|
145
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
146
|
|
@@ -179,6 +179,15 @@ Please note that all User attributes except email, password and password_confirm
|
|
179
179
|
attr_accessible :first_name, :last_name
|
180
180
|
end
|
181
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
|
+
|
182
191
|
h2. Hooks: return_to parameter
|
183
192
|
|
184
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):
|
@@ -230,21 +239,17 @@ h2. Write your own tests with Clearance's helpers
|
|
230
239
|
|
231
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.
|
232
241
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
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>
|
241
250
|
|
242
251
|
h2. Authors
|
243
252
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
* Jason Morrison
|
248
|
-
* Eugene Bolshakov
|
249
|
-
* Josh Nichols
|
250
|
-
* Mike Breen
|
253
|
+
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!
|
254
|
+
|
255
|
+
Dan Croak, Mike Burns, Jason Morrison, Joe Ferris, Eugene Bolshakov, Josh Nichols, Mike Breen, Marcel Görner, Bence Nagy, Ben Mabey, Eloy Duran, & Tim Pope.
|
data/Rakefile
CHANGED
@@ -13,7 +13,7 @@ namespace :test do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
Cucumber::Rake::Task.new(:features) do |t|
|
16
|
-
t.cucumber_opts = "--format
|
16
|
+
t.cucumber_opts = "--format pretty"
|
17
17
|
t.feature_pattern = 'test/rails_root/features/*.feature'
|
18
18
|
end
|
19
19
|
end
|
@@ -51,13 +51,14 @@ 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.4.
|
54
|
+
gem_spec.version = "0.4.9"
|
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"
|
58
58
|
gem_spec.description = "Simple, complete Rails authentication scheme."
|
59
59
|
gem_spec.authors = ["thoughtbot, inc.", "Dan Croak", "Mike Burns", "Jason Morrison",
|
60
|
-
"Eugene Bolshakov", "Josh Nichols", "Mike Breen"
|
60
|
+
"Eugene Bolshakov", "Josh Nichols", "Mike Breen", "Joe Ferris",
|
61
|
+
"Bence Nagy", "Marcel Görner", "Ben Mabey", "Tim Pope", "Eloy Duran"]
|
61
62
|
gem_spec.files = FileList["[A-Z]*", "{generators,lib,shoulda_macros,rails}/**/*"]
|
62
63
|
end
|
63
64
|
|
@@ -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 => 'users', :action => 'new'
|
44
44
|
|
45
45
|
*******************************************************************************
|
@@ -14,7 +14,7 @@
|
|
14
14
|
<%= form.password_field :password %>
|
15
15
|
</div>
|
16
16
|
<div class="password_field">
|
17
|
-
<%= form.label :password_confirmation, "
|
17
|
+
<%= form.label :password_confirmation, "Confirm password" %>
|
18
18
|
<%= form.password_field :password_confirmation %>
|
19
19
|
</div>
|
20
20
|
<div class="submit_field">
|
@@ -8,6 +8,6 @@
|
|
8
8
|
<%= form.password_field :password %>
|
9
9
|
</div>
|
10
10
|
<div class="password_field">
|
11
|
-
<%= form.label :password_confirmation, "
|
11
|
+
<%= form.label :password_confirmation, "Confirm password" %>
|
12
12
|
<%= form.password_field :password_confirmation %>
|
13
13
|
</div>
|
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
In order to sign in even if
|
1
|
+
Feature: Password reset
|
2
|
+
In order to sign in even if user forgot their password
|
3
3
|
A user
|
4
4
|
Should be able to reset it
|
5
5
|
|
@@ -30,3 +30,10 @@ Fature: Password Reset
|
|
30
30
|
And I sign in as "email@person.com/newpassword"
|
31
31
|
Then I should be signed in
|
32
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
|
+
|
@@ -7,7 +7,7 @@ Feature: Sign up
|
|
7
7
|
When I go to the sign up page
|
8
8
|
And I fill in "Email" with "invalidemail"
|
9
9
|
And I fill in "Password" with "password"
|
10
|
-
And I fill in "
|
10
|
+
And I fill in "Confirm password" with ""
|
11
11
|
And I press "Sign Up"
|
12
12
|
Then I should see error messages
|
13
13
|
|
@@ -15,7 +15,7 @@ Feature: Sign up
|
|
15
15
|
When I go to the sign up page
|
16
16
|
And I fill in "Email" with "email@person.com"
|
17
17
|
And I fill in "Password" with "password"
|
18
|
-
And I fill in "
|
18
|
+
And I fill in "Confirm password" with "password"
|
19
19
|
And I press "Sign Up"
|
20
20
|
Then I should see "instructions for confirming"
|
21
21
|
And a confirmation message should be sent to "email@person.com"
|
@@ -4,7 +4,13 @@ Then /^I should see error messages$/ do
|
|
4
4
|
Then %{I should see "error(s)? prohibited"}
|
5
5
|
end
|
6
6
|
|
7
|
-
#
|
7
|
+
# Database
|
8
|
+
|
9
|
+
Factory.factories.each do |name, factory|
|
10
|
+
Given /^an? #{name} exists with an? (.*) of "([^"]*)"$/ do |attr, value|
|
11
|
+
Factory(name, attr.gsub(' ', '_') => value)
|
12
|
+
end
|
13
|
+
end
|
8
14
|
|
9
15
|
Given /^there is no user with "(.*)"$/ do |email|
|
10
16
|
assert_nil User.find_by_email(email)
|
@@ -68,6 +74,16 @@ When /^I follow the password reset link sent to "(.*)"$/ do |email|
|
|
68
74
|
visit edit_user_password_path(:user_id => user, :token => user.token)
|
69
75
|
end
|
70
76
|
|
77
|
+
When /^I try to change the password of "(.*)" without token$/ do |email|
|
78
|
+
user = User.find_by_email(email)
|
79
|
+
visit edit_user_password_path(:user_id => user)
|
80
|
+
end
|
81
|
+
|
82
|
+
Then /^I should be forbidden$/ do
|
83
|
+
assert_response :forbidden
|
84
|
+
end
|
85
|
+
|
86
|
+
|
71
87
|
# Actions
|
72
88
|
|
73
89
|
When /^I sign in( with "remember me")? as "(.*)\/(.*)"$/ do |remember, email, password|
|
@@ -90,7 +106,7 @@ end
|
|
90
106
|
|
91
107
|
When /^I update my password with "(.*)\/(.*)"$/ do |password, confirmation|
|
92
108
|
And %{I fill in "Choose password" with "#{password}"}
|
93
|
-
And %{I fill in "
|
109
|
+
And %{I fill in "Confirm password" with "#{confirmation}"}
|
94
110
|
And %{I press "Save this password"}
|
95
111
|
end
|
96
112
|
|
data/lib/clearance.rb
CHANGED
@@ -8,8 +8,9 @@ module Clearance
|
|
8
8
|
controller.send(:include, PrivateMethods)
|
9
9
|
|
10
10
|
controller.class_eval do
|
11
|
-
before_filter :
|
12
|
-
before_filter :
|
11
|
+
before_filter :forbid_confirmed_user, :only => :new
|
12
|
+
before_filter :forbid_missing_token, :only => :new
|
13
|
+
before_filter :forbid_non_existant_user, :only => :new
|
13
14
|
filter_parameter_logging :token
|
14
15
|
end
|
15
16
|
end
|
@@ -20,7 +21,9 @@ module Clearance
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def create
|
24
|
+
@user = User.find_by_id_and_token(params[:user_id], params[:token])
|
23
25
|
@user.confirm_email!
|
26
|
+
|
24
27
|
sign_user_in(@user)
|
25
28
|
flash[:success] = "Confirmed email and signed in."
|
26
29
|
redirect_to url_after_create
|
@@ -30,19 +33,22 @@ module Clearance
|
|
30
33
|
module PrivateMethods
|
31
34
|
private
|
32
35
|
|
33
|
-
def
|
34
|
-
|
35
|
-
if
|
36
|
-
|
37
|
-
elsif @user.email_confirmed?
|
38
|
-
redirect_to new_session_url
|
36
|
+
def forbid_confirmed_user
|
37
|
+
user = User.find_by_id(params[:user_id])
|
38
|
+
if user && user.email_confirmed?
|
39
|
+
raise ActionController::Forbidden, "confirmed user"
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
def forbid_missing_token
|
44
|
+
if params[:token].blank?
|
45
|
+
raise ActionController::Forbidden, "missing token"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def forbid_non_existant_user
|
50
|
+
unless User.find_by_id_and_token(params[:user_id], params[:token])
|
51
|
+
raise ActionController::Forbidden, "non-existant user"
|
46
52
|
end
|
47
53
|
end
|
48
54
|
|
@@ -7,8 +7,9 @@ module Clearance
|
|
7
7
|
controller.send(:include, Actions)
|
8
8
|
controller.send(:include, PrivateMethods)
|
9
9
|
|
10
|
-
controller.class_eval do
|
11
|
-
before_filter :
|
10
|
+
controller.class_eval do
|
11
|
+
before_filter :forbid_missing_token, :only => [:edit, :update]
|
12
|
+
before_filter :forbid_non_existant_user, :only => [:edit, :update]
|
12
13
|
filter_parameter_logging :password, :password_confirmation
|
13
14
|
end
|
14
15
|
end
|
@@ -32,9 +33,12 @@ module Clearance
|
|
32
33
|
end
|
33
34
|
|
34
35
|
def edit
|
36
|
+
@user = User.find_by_id_and_token(params[:user_id], params[:token])
|
35
37
|
end
|
36
38
|
|
37
39
|
def update
|
40
|
+
@user = User.find_by_id_and_token(params[:user_id], params[:token])
|
41
|
+
|
38
42
|
if @user.update_password(params[:user])
|
39
43
|
sign_user_in(@user)
|
40
44
|
redirect_to url_after_update
|
@@ -47,10 +51,15 @@ module Clearance
|
|
47
51
|
module PrivateMethods
|
48
52
|
private
|
49
53
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
+
def forbid_missing_token
|
55
|
+
if params[:token].blank?
|
56
|
+
raise ActionController::Forbidden, "missing token"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def forbid_non_existant_user
|
61
|
+
unless User.find_by_id_and_token(params[:user_id], params[:token])
|
62
|
+
raise ActionController::Forbidden, "non-existant user"
|
54
63
|
end
|
55
64
|
end
|
56
65
|
|
@@ -0,0 +1 @@
|
|
1
|
+
ActionController::Base.rescue_responses.update('ActionController::Forbidden' => :forbidden)
|
@@ -8,9 +8,14 @@ module Clearance
|
|
8
8
|
|
9
9
|
should_filter_params :token
|
10
10
|
|
11
|
-
context "
|
11
|
+
context "a user whose email has not been confirmed" do
|
12
12
|
setup { @user = Factory(:user) }
|
13
13
|
|
14
|
+
should "have a token" do
|
15
|
+
assert_not_nil @user.token
|
16
|
+
assert_not_equal "", @user.token
|
17
|
+
end
|
18
|
+
|
14
19
|
context "on GET to #new with correct id and token" do
|
15
20
|
setup do
|
16
21
|
get :new, :user_id => @user.to_param, :token => @user.token
|
@@ -22,53 +27,39 @@ module Clearance
|
|
22
27
|
should_redirect_to_url_after_create
|
23
28
|
end
|
24
29
|
|
25
|
-
context "
|
26
|
-
setup do
|
27
|
-
|
28
|
-
assert_not_equal
|
29
|
-
|
30
|
-
|
30
|
+
context "with an incorrect token" do
|
31
|
+
setup do
|
32
|
+
@bad_token = "bad token"
|
33
|
+
assert_not_equal @bad_token, @user.token
|
34
|
+
end
|
35
|
+
|
36
|
+
should_forbid "on GET to #new with incorrect token" do
|
37
|
+
get :new, :user_id => @user.to_param, :token => @bad_token
|
31
38
|
end
|
32
|
-
|
33
|
-
|
34
|
-
|
39
|
+
end
|
40
|
+
|
41
|
+
should_forbid "on GET to #new with blank token" do
|
42
|
+
get :new, :user_id => @user.to_param, :token => ""
|
43
|
+
end
|
44
|
+
|
45
|
+
should_forbid "on GET to #new with no token" do
|
46
|
+
get :new, :user_id => @user.to_param
|
35
47
|
end
|
36
48
|
end
|
37
49
|
|
38
|
-
context "
|
50
|
+
context "a user with email confirmed" do
|
39
51
|
setup { @user = Factory(:email_confirmed_user) }
|
40
52
|
|
41
|
-
|
42
|
-
|
43
|
-
get :new, :user_id => @user.to_param, :token => @user.token
|
44
|
-
end
|
45
|
-
|
46
|
-
should_not_be_signed_in
|
47
|
-
should_redirect_to 'new_session_url'
|
48
|
-
end
|
49
|
-
|
50
|
-
context "on GET to #new with incorrect token" do
|
51
|
-
setup do
|
52
|
-
token = ""
|
53
|
-
assert_not_equal token, @user.token
|
54
|
-
|
55
|
-
get :new, :user_id => @user.to_param, :token => token
|
56
|
-
end
|
57
|
-
|
58
|
-
should_not_be_signed_in
|
59
|
-
should_redirect_to 'new_session_url'
|
53
|
+
should_forbid "on GET to #new with correct id" do
|
54
|
+
get :new, :user_id => @user.to_param
|
60
55
|
end
|
61
56
|
end
|
62
57
|
|
63
|
-
context "
|
58
|
+
context "no users" do
|
64
59
|
setup { assert_equal 0, User.count }
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
69
|
-
|
70
|
-
should_respond_with :not_found
|
71
|
-
should_render_nothing
|
60
|
+
|
61
|
+
should_forbid "on GET to #new with nonexistent id and token" do
|
62
|
+
get :new, :user_id => '123', :token => '123'
|
72
63
|
end
|
73
64
|
end
|
74
65
|
|
@@ -9,163 +9,169 @@ module Clearance
|
|
9
9
|
should_route :get, '/users/1/password/edit',
|
10
10
|
:action => 'edit', :user_id => '1'
|
11
11
|
|
12
|
-
context "with
|
12
|
+
context "a user with confirmed email" do
|
13
13
|
setup do
|
14
|
-
@user = Factory(:
|
14
|
+
@user = Factory(:email_confirmed_user)
|
15
15
|
@user.confirm_email!
|
16
16
|
end
|
17
17
|
|
18
|
-
context
|
18
|
+
context "on GET to #new" do
|
19
19
|
setup { get :new, :user_id => @user.to_param }
|
20
20
|
|
21
21
|
should_respond_with :success
|
22
22
|
should_render_template "new"
|
23
23
|
end
|
24
24
|
|
25
|
-
context "
|
26
|
-
context "with
|
25
|
+
context "on POST to #create" do
|
26
|
+
context "with correct email address" do
|
27
27
|
setup do
|
28
28
|
ActionMailer::Base.deliveries.clear
|
29
29
|
post :create, :password => { :email => @user.email }
|
30
30
|
end
|
31
31
|
|
32
|
-
|
32
|
+
should "generate a token for the change your password email" do
|
33
|
+
assert_not_nil @user.reload.token
|
34
|
+
end
|
35
|
+
|
36
|
+
should "send the change your password email" do
|
37
|
+
assert_sent_email do |email|
|
38
|
+
email.subject =~ /change your password/i
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
33
42
|
should_set_the_flash_to /password/i
|
34
43
|
should_redirect_to_url_after_create
|
35
44
|
end
|
36
45
|
|
37
|
-
context "with
|
46
|
+
context "with incorrect email address" do
|
38
47
|
setup do
|
39
48
|
email = "user1@example.com"
|
40
49
|
assert ! User.exists?(['email = ?', email])
|
41
50
|
ActionMailer::Base.deliveries.clear
|
51
|
+
assert_nil @user.reload.token
|
42
52
|
|
43
53
|
post :create, :password => { :email => email }
|
44
54
|
end
|
45
55
|
|
46
|
-
|
56
|
+
should "not generate a token for the change your password email" do
|
57
|
+
assert_nil @user.reload.token
|
58
|
+
end
|
59
|
+
|
60
|
+
should "not send a password reminder email" do
|
61
|
+
assert ActionMailer::Base.deliveries.empty?
|
62
|
+
end
|
47
63
|
|
48
64
|
should "set a :notice flash" do
|
49
65
|
assert_not_nil flash.now[:notice]
|
50
66
|
end
|
51
67
|
|
52
|
-
should_render_template
|
68
|
+
should_render_template :new
|
53
69
|
end
|
54
70
|
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "a user with confirmed email and forgotten password" do
|
74
|
+
setup do
|
75
|
+
@user = Factory(:email_confirmed_user)
|
76
|
+
@user.forgot_password!
|
77
|
+
end
|
55
78
|
|
56
|
-
context
|
57
|
-
setup
|
58
|
-
|
59
|
-
context "A GET to #edit" do
|
60
|
-
context "with an existing user's id and token" do
|
61
|
-
setup do
|
62
|
-
get :edit, :user_id => @user.to_param, :token => @user.token
|
63
|
-
end
|
64
|
-
|
65
|
-
should "find the user with the given id and token" do
|
66
|
-
assert_equal @user, assigns(:user)
|
67
|
-
end
|
68
|
-
|
69
|
-
should_respond_with :success
|
70
|
-
should_render_template "edit"
|
71
|
-
should_display_a_password_update_form
|
72
|
-
end
|
73
|
-
|
74
|
-
context "with an existing user's id but not token" do
|
75
|
-
setup do
|
76
|
-
get :edit, :user_id => @user.to_param, :token => ""
|
77
|
-
end
|
78
|
-
|
79
|
-
should_respond_with :not_found
|
80
|
-
should_render_nothing
|
81
|
-
end
|
79
|
+
context "on GET to #edit with correct id and token" do
|
80
|
+
setup do
|
81
|
+
get :edit, :user_id => @user.to_param, :token => @user.token
|
82
82
|
end
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
setup do
|
87
|
-
new_password = "new_password"
|
88
|
-
@encrypted_new_password = @user.encrypt(new_password)
|
89
|
-
assert_not_equal @encrypted_new_password, @user.encrypted_password
|
90
|
-
|
91
|
-
put(:update,
|
92
|
-
:user_id => @user,
|
93
|
-
:token => @user.token,
|
94
|
-
:user => {
|
95
|
-
:password => new_password,
|
96
|
-
:password_confirmation => new_password
|
97
|
-
})
|
98
|
-
@user.reload
|
99
|
-
end
|
100
|
-
|
101
|
-
should "update the user's password" do
|
102
|
-
assert_equal @encrypted_new_password, @user.encrypted_password
|
103
|
-
end
|
104
|
-
|
105
|
-
should "clear the token" do
|
106
|
-
assert_nil @user.token
|
107
|
-
end
|
108
|
-
|
109
|
-
should_be_signed_in_as { @user }
|
110
|
-
should_redirect_to_url_after_update
|
111
|
-
end
|
112
|
-
|
113
|
-
context "with password but blank password confirmation" do
|
114
|
-
setup do
|
115
|
-
new_password = "new_password"
|
116
|
-
@encrypted_new_password = @user.encrypt(new_password)
|
117
|
-
|
118
|
-
put(:update,
|
119
|
-
:user_id => @user.to_param,
|
120
|
-
:token => @user.token,
|
121
|
-
:user => {
|
122
|
-
:password => new_password,
|
123
|
-
:password_confirmation => ''
|
124
|
-
})
|
125
|
-
@user.reload
|
126
|
-
end
|
127
|
-
|
128
|
-
should "not update the user's password" do
|
129
|
-
assert_not_equal @encrypted_new_password, @user.encrypted_password
|
130
|
-
end
|
131
|
-
|
132
|
-
should "not clear the token" do
|
133
|
-
assert_not_nil @user.token
|
134
|
-
end
|
135
|
-
|
136
|
-
should_not_be_signed_in
|
137
|
-
should_respond_with :success
|
138
|
-
should_render_template :edit
|
139
|
-
|
140
|
-
should_display_a_password_update_form
|
141
|
-
end
|
142
|
-
|
143
|
-
context "with an existing user's id but not token" do
|
144
|
-
setup do
|
145
|
-
new_password = "new_password"
|
146
|
-
@encrypted_new_password = @user.encrypt(new_password)
|
147
|
-
put(:update,
|
148
|
-
:user_id => @user.to_param,
|
149
|
-
:token => "",
|
150
|
-
:user => {
|
151
|
-
:password => new_password,
|
152
|
-
:password => new_password
|
153
|
-
})
|
154
|
-
end
|
155
|
-
|
156
|
-
should "not update the user's password" do
|
157
|
-
assert_not_equal @encrypted_new_password, @user.encrypted_password
|
158
|
-
end
|
159
|
-
|
160
|
-
should_not_be_signed_in
|
161
|
-
should_respond_with :not_found
|
162
|
-
should_render_nothing
|
163
|
-
end
|
83
|
+
|
84
|
+
should "find the user" do
|
85
|
+
assert_equal @user, assigns(:user)
|
164
86
|
end
|
165
|
-
|
87
|
+
|
88
|
+
should_respond_with :success
|
89
|
+
should_render_template "edit"
|
90
|
+
should_display_a_password_update_form
|
91
|
+
end
|
92
|
+
|
93
|
+
should_forbid "on GET to #edit with correct id but blank token" do
|
94
|
+
get :edit, :user_id => @user.to_param, :token => ""
|
95
|
+
end
|
96
|
+
|
97
|
+
should_forbid "on GET to #edit with correct id but no token" do
|
98
|
+
get :edit, :user_id => @user.to_param
|
166
99
|
end
|
167
100
|
|
168
|
-
|
101
|
+
context "on PUT to #update with matching password and password confirmation" do
|
102
|
+
setup do
|
103
|
+
new_password = "new_password"
|
104
|
+
@encrypted_new_password = @user.encrypt(new_password)
|
105
|
+
assert_not_equal @encrypted_new_password, @user.encrypted_password
|
106
|
+
|
107
|
+
put(:update,
|
108
|
+
:user_id => @user,
|
109
|
+
:token => @user.token,
|
110
|
+
:user => {
|
111
|
+
:password => new_password,
|
112
|
+
:password_confirmation => new_password
|
113
|
+
})
|
114
|
+
@user.reload
|
115
|
+
end
|
116
|
+
|
117
|
+
should "update password" do
|
118
|
+
assert_equal @encrypted_new_password, @user.encrypted_password
|
119
|
+
end
|
120
|
+
|
121
|
+
should "clear token" do
|
122
|
+
assert_nil @user.token
|
123
|
+
end
|
124
|
+
|
125
|
+
should_be_signed_in_as { @user }
|
126
|
+
should_redirect_to_url_after_update
|
127
|
+
end
|
128
|
+
|
129
|
+
context "on PUT to #update with password but blank password confirmation" do
|
130
|
+
setup do
|
131
|
+
new_password = "new_password"
|
132
|
+
@encrypted_new_password = @user.encrypt(new_password)
|
133
|
+
|
134
|
+
put(:update,
|
135
|
+
:user_id => @user.to_param,
|
136
|
+
:token => @user.token,
|
137
|
+
:user => {
|
138
|
+
:password => new_password,
|
139
|
+
:password_confirmation => ''
|
140
|
+
})
|
141
|
+
@user.reload
|
142
|
+
end
|
143
|
+
|
144
|
+
should "not update password" do
|
145
|
+
assert_not_equal @encrypted_new_password, @user.encrypted_password
|
146
|
+
end
|
147
|
+
|
148
|
+
should "not clear token" do
|
149
|
+
assert_not_nil @user.token
|
150
|
+
end
|
151
|
+
|
152
|
+
should_not_be_signed_in
|
153
|
+
should_respond_with :success
|
154
|
+
should_render_template :edit
|
155
|
+
|
156
|
+
should_display_a_password_update_form
|
157
|
+
end
|
158
|
+
|
159
|
+
should_forbid "on PUT to #update with id but no token" do
|
160
|
+
put :update, :user_id => @user.to_param, :token => ""
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context "given two users and user one signs in" do
|
165
|
+
setup do
|
166
|
+
@user_one = Factory(:user)
|
167
|
+
@user_two = Factory(:user)
|
168
|
+
sign_in_as @user_one
|
169
|
+
end
|
170
|
+
|
171
|
+
should_forbid "when user one tries to change user two's password on GET with no token" do
|
172
|
+
get :edit, :user_id => @user_two.to_param
|
173
|
+
end
|
174
|
+
end
|
169
175
|
end
|
170
176
|
end
|
171
177
|
|
data/shoulda_macros/clearance.rb
CHANGED
@@ -53,6 +53,16 @@ module Clearance
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
+
# HTTP FLUENCY
|
57
|
+
|
58
|
+
def should_forbid(description, &block)
|
59
|
+
should "forbid #{description}" do
|
60
|
+
assert_raises ActionController::Forbidden do
|
61
|
+
instance_eval(&block)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
56
66
|
# CONTEXTS
|
57
67
|
|
58
68
|
def signed_in_user_context(&blk)
|
@@ -140,30 +150,6 @@ module Clearance
|
|
140
150
|
end
|
141
151
|
end
|
142
152
|
|
143
|
-
# EMAILS
|
144
|
-
|
145
|
-
def should_send_the_change_your_password_email
|
146
|
-
should "generate a token for the change your password email" do
|
147
|
-
assert_not_nil @user.reload.token
|
148
|
-
end
|
149
|
-
|
150
|
-
should "send the change your password email" do
|
151
|
-
assert_sent_email do |email|
|
152
|
-
email.subject =~ /change your password/i
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def should_not_send_the_change_your_password_email
|
158
|
-
should "generate a token for the change your password email" do
|
159
|
-
assert_nil @user.reload.token
|
160
|
-
end
|
161
|
-
|
162
|
-
should "not send a password reminder email" do
|
163
|
-
assert ActionMailer::Base.deliveries.empty?
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
153
|
# FORMS
|
168
154
|
|
169
155
|
def should_display_a_password_update_form
|
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.
|
4
|
+
version: 0.4.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- thoughtbot, inc.
|
@@ -11,11 +11,17 @@ authors:
|
|
11
11
|
- Eugene Bolshakov
|
12
12
|
- Josh Nichols
|
13
13
|
- Mike Breen
|
14
|
+
- Joe Ferris
|
15
|
+
- Bence Nagy
|
16
|
+
- "Marcel G\xC3\xB6rner"
|
17
|
+
- Ben Mabey
|
18
|
+
- Tim Pope
|
19
|
+
- Eloy Duran
|
14
20
|
autorequire:
|
15
21
|
bindir: bin
|
16
22
|
cert_chain: []
|
17
23
|
|
18
|
-
date: 2009-02-
|
24
|
+
date: 2009-02-19 21:00:00 -08:00
|
19
25
|
default_executable:
|
20
26
|
dependencies: []
|
21
27
|
|
@@ -103,6 +109,10 @@ files:
|
|
103
109
|
- lib/clearance/app/models
|
104
110
|
- lib/clearance/app/models/clearance_mailer.rb
|
105
111
|
- lib/clearance/app/models/user.rb
|
112
|
+
- lib/clearance/lib
|
113
|
+
- lib/clearance/lib/extensions
|
114
|
+
- lib/clearance/lib/extensions/errors.rb
|
115
|
+
- lib/clearance/lib/extensions/rescue.rb
|
106
116
|
- lib/clearance/test
|
107
117
|
- lib/clearance/test/functional
|
108
118
|
- lib/clearance/test/functional/confirmations_controller_test.rb
|