clearance 0.8.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of clearance might be problematic. Click here for more details.

Files changed (52) hide show
  1. data/CHANGELOG.textile +194 -0
  2. data/LICENSE +21 -0
  3. data/README.textile +123 -0
  4. data/Rakefile +103 -0
  5. data/TODO.textile +6 -0
  6. data/app/controllers/clearance/confirmations_controller.rb +75 -0
  7. data/app/controllers/clearance/passwords_controller.rb +84 -0
  8. data/app/controllers/clearance/sessions_controller.rb +66 -0
  9. data/app/controllers/clearance/users_controller.rb +35 -0
  10. data/app/models/clearance_mailer.rb +23 -0
  11. data/app/views/clearance_mailer/change_password.html.erb +9 -0
  12. data/app/views/clearance_mailer/confirmation.html.erb +5 -0
  13. data/app/views/passwords/edit.html.erb +23 -0
  14. data/app/views/passwords/new.html.erb +15 -0
  15. data/app/views/sessions/new.html.erb +24 -0
  16. data/app/views/users/_form.html.erb +13 -0
  17. data/app/views/users/new.html.erb +6 -0
  18. data/config/clearance_routes.rb +30 -0
  19. data/generators/clearance/USAGE +1 -0
  20. data/generators/clearance/clearance_generator.rb +41 -0
  21. data/generators/clearance/lib/insert_commands.rb +33 -0
  22. data/generators/clearance/lib/rake_commands.rb +22 -0
  23. data/generators/clearance/templates/README +22 -0
  24. data/generators/clearance/templates/factories.rb +13 -0
  25. data/generators/clearance/templates/migrations/create_users.rb +21 -0
  26. data/generators/clearance/templates/migrations/update_users.rb +41 -0
  27. data/generators/clearance/templates/user.rb +3 -0
  28. data/generators/clearance_features/USAGE +1 -0
  29. data/generators/clearance_features/clearance_features_generator.rb +20 -0
  30. data/generators/clearance_features/templates/features/password_reset.feature +33 -0
  31. data/generators/clearance_features/templates/features/sign_in.feature +35 -0
  32. data/generators/clearance_features/templates/features/sign_out.feature +15 -0
  33. data/generators/clearance_features/templates/features/sign_up.feature +45 -0
  34. data/generators/clearance_features/templates/features/step_definitions/clearance_steps.rb +116 -0
  35. data/generators/clearance_features/templates/features/step_definitions/factory_girl_steps.rb +5 -0
  36. data/generators/clearance_features/templates/features/support/paths.rb +22 -0
  37. data/generators/clearance_views/USAGE +0 -0
  38. data/generators/clearance_views/clearance_views_generator.rb +27 -0
  39. data/generators/clearance_views/templates/formtastic/passwords/edit.html.erb +21 -0
  40. data/generators/clearance_views/templates/formtastic/passwords/new.html.erb +15 -0
  41. data/generators/clearance_views/templates/formtastic/sessions/new.html.erb +21 -0
  42. data/generators/clearance_views/templates/formtastic/users/_inputs.html.erb +6 -0
  43. data/generators/clearance_views/templates/formtastic/users/new.html.erb +10 -0
  44. data/lib/clearance.rb +6 -0
  45. data/lib/clearance/authentication.rb +125 -0
  46. data/lib/clearance/extensions/errors.rb +6 -0
  47. data/lib/clearance/extensions/rescue.rb +3 -0
  48. data/lib/clearance/extensions/routes.rb +14 -0
  49. data/lib/clearance/user.rb +199 -0
  50. data/rails/init.rb +1 -0
  51. data/shoulda_macros/clearance.rb +266 -0
  52. metadata +120 -0
@@ -0,0 +1,33 @@
1
+ # Mostly pinched from http://github.com/ryanb/nifty-generators/tree/master
2
+
3
+ Rails::Generator::Commands::Base.class_eval do
4
+ def file_contains?(relative_destination, line)
5
+ File.read(destination_path(relative_destination)).include?(line)
6
+ end
7
+ end
8
+
9
+ Rails::Generator::Commands::Create.class_eval do
10
+ def insert_into(file, line)
11
+ logger.insert "#{line} into #{file}"
12
+ unless options[:pretend] || file_contains?(file, line)
13
+ gsub_file file, /^(class|module) .+$/ do |match|
14
+ "#{match}\n #{line}"
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ Rails::Generator::Commands::Destroy.class_eval do
21
+ def insert_into(file, line)
22
+ logger.remove "#{line} from #{file}"
23
+ unless options[:pretend]
24
+ gsub_file file, "\n #{line}", ''
25
+ end
26
+ end
27
+ end
28
+
29
+ Rails::Generator::Commands::List.class_eval do
30
+ def insert_into(file, line)
31
+ logger.insert "#{line} into #{file}"
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ Rails::Generator::Commands::Create.class_eval do
2
+ def rake_db_migrate
3
+ logger.rake "db:migrate"
4
+ unless system("rake db:migrate")
5
+ logger.rake "db:migrate failed. Rolling back"
6
+ command(:destroy).invoke!
7
+ end
8
+ end
9
+ end
10
+
11
+ Rails::Generator::Commands::Destroy.class_eval do
12
+ def rake_db_migrate
13
+ logger.rake "db:rollback"
14
+ system "rake db:rollback"
15
+ end
16
+ end
17
+
18
+ Rails::Generator::Commands::List.class_eval do
19
+ def rake_db_migrate
20
+ logger.rake "db:migrate"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+
2
+ *******************************************************************************
3
+
4
+ Ok, enough fancy automatic stuff. Time for some old school monkey copy-pasting.
5
+
6
+ 1. Define a HOST constant in your environments files.
7
+ In config/environments/test.rb and config/environments/development.rb it can be:
8
+
9
+ HOST = "localhost"
10
+
11
+ In production.rb it must be the actual host your application is deployed to.
12
+ The constant is used by mailers to generate URLs in emails.
13
+
14
+ 2. In config/environment.rb:
15
+
16
+ DO_NOT_REPLY = "donotreply@example.com"
17
+
18
+ 3. Define root_url to *something* in your config/routes.rb:
19
+
20
+ map.root :controller => 'home'
21
+
22
+ *******************************************************************************
@@ -0,0 +1,13 @@
1
+ Factory.sequence :email do |n|
2
+ "user#{n}@example.com"
3
+ end
4
+
5
+ Factory.define :user do |user|
6
+ user.email { Factory.next :email }
7
+ user.password { "password" }
8
+ user.password_confirmation { "password" }
9
+ end
10
+
11
+ Factory.define :email_confirmed_user, :parent => :user do |user|
12
+ user.email_confirmed { true }
13
+ end
@@ -0,0 +1,21 @@
1
+ class ClearanceCreateUsers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table(:users) do |t|
4
+ t.string :email
5
+ t.string :encrypted_password, :limit => 128
6
+ t.string :salt, :limit => 128
7
+ t.string :confirmation_token, :limit => 128
8
+ t.string :remember_token, :limit => 128
9
+ t.boolean :email_confirmed, :default => false, :null => false
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :users, [:id, :confirmation_token]
14
+ add_index :users, :email
15
+ add_index :users, :remember_token
16
+ end
17
+
18
+ def self.down
19
+ drop_table :users
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ class ClearanceUpdateUsers < ActiveRecord::Migration
2
+ def self.up
3
+ <%
4
+ existing_columns = ActiveRecord::Base.connection.columns(:users).collect { |each| each.name }
5
+ columns = [
6
+ [:email, 't.string :email'],
7
+ [:encrypted_password, 't.string :encrypted_password, :limit => 128'],
8
+ [:salt, 't.string :salt, :limit => 128'],
9
+ [:confirmation_token, 't.string :confirmation_token, :limit => 128'],
10
+ [:remember_token, 't.string :remember_token, :limit => 128'],
11
+ [:email_confirmed, 't.boolean :email_confirmed, :default => false, :null => false']
12
+ ].delete_if {|c| existing_columns.include?(c.first.to_s)}
13
+ -%>
14
+ change_table(:users) do |t|
15
+ <% columns.each do |c| -%>
16
+ <%= c.last %>
17
+ <% end -%>
18
+ end
19
+
20
+ <%
21
+ existing_indexes = ActiveRecord::Base.connection.indexes(:users)
22
+ index_names = existing_indexes.collect { |each| each.name }
23
+ new_indexes = [
24
+ [:index_users_on_id_and_confirmation_token, 'add_index :users, [:id, :confirmation_token]'],
25
+ [:index_users_on_email, 'add_index :users, :email'],
26
+ [:index_users_on_remember_token, 'add_index :users, :remember_token']
27
+ ].delete_if { |each| index_names.include?(each.first.to_s) }
28
+ -%>
29
+ <% new_indexes.each do |each| -%>
30
+ <%= each.last %>
31
+ <% end -%>
32
+ end
33
+
34
+ def self.down
35
+ change_table(:users) do |t|
36
+ <% unless columns.empty? -%>
37
+ t.remove <%= columns.collect { |each| ":#{each.first}" }.join(',') %>
38
+ <% end -%>
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ include Clearance::User
3
+ end
@@ -0,0 +1 @@
1
+ script/generate clearance_features
@@ -0,0 +1,20 @@
1
+ class ClearanceFeaturesGenerator < Rails::Generator::Base
2
+
3
+ def manifest
4
+ record do |m|
5
+ m.directory File.join("features", "step_definitions")
6
+ m.directory File.join("features", "support")
7
+
8
+ ["features/step_definitions/clearance_steps.rb",
9
+ "features/step_definitions/factory_girl_steps.rb",
10
+ "features/support/paths.rb",
11
+ "features/sign_in.feature",
12
+ "features/sign_out.feature",
13
+ "features/sign_up.feature",
14
+ "features/password_reset.feature"].each do |file|
15
+ m.file file, file
16
+ end
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,33 @@
1
+ Feature: Password reset
2
+ In order to sign in even if user forgot their password
3
+ A user
4
+ Should be able to reset it
5
+
6
+ Scenario: User is not signed up
7
+ Given no user exists with an email of "email@person.com"
8
+ When I request password reset link to be sent to "email@person.com"
9
+ Then I should see "Unknown email"
10
+
11
+ Scenario: User is signed up and requests password reset
12
+ Given I signed up with "email@person.com/password"
13
+ When I request password reset link to be sent to "email@person.com"
14
+ Then I should see "instructions for changing your password"
15
+ And a password reset message should be sent to "email@person.com"
16
+
17
+ Scenario: User is signed up updated his password and types wrong confirmation
18
+ Given I signed up with "email@person.com/password"
19
+ When I follow the password reset link sent to "email@person.com"
20
+ And I update my password with "newpassword/wrongconfirmation"
21
+ Then I should see error messages
22
+ And I should be signed out
23
+
24
+ Scenario: User is signed up and updates his password
25
+ Given I signed up with "email@person.com/password"
26
+ When I follow the password reset link sent to "email@person.com"
27
+ And I update my password with "newpassword/newpassword"
28
+ Then I should be signed in
29
+ When I sign out
30
+ Then I should be signed out
31
+ And I sign in as "email@person.com/newpassword"
32
+ Then I should be signed in
33
+
@@ -0,0 +1,35 @@
1
+ Feature: Sign in
2
+ In order to get access to protected sections of the site
3
+ A user
4
+ Should be able to sign in
5
+
6
+ Scenario: User is not signed up
7
+ Given no user exists with an email of "email@person.com"
8
+ When I go to the sign in page
9
+ And I sign in as "email@person.com/password"
10
+ Then I should see "Bad email or password"
11
+ And I should be signed out
12
+
13
+ Scenario: User is not confirmed
14
+ Given I signed up with "email@person.com/password"
15
+ When I go to the sign in page
16
+ And I sign in as "email@person.com/password"
17
+ Then I should see "User has not confirmed email"
18
+ And I should be signed out
19
+
20
+ Scenario: User enters wrong password
21
+ Given I am signed up and confirmed as "email@person.com/password"
22
+ When I go to the sign in page
23
+ And I sign in as "email@person.com/wrongpassword"
24
+ Then I should see "Bad email or password"
25
+ And I should be signed out
26
+
27
+ Scenario: User signs in successfully
28
+ Given I am signed up and confirmed as "email@person.com/password"
29
+ When I go to the sign in page
30
+ And I sign in as "email@person.com/password"
31
+ Then I should see "Signed in"
32
+ And I should be signed in
33
+ When I return next time
34
+ Then I should be signed in
35
+
@@ -0,0 +1,15 @@
1
+ Feature: Sign out
2
+ To protect my account from unauthorized access
3
+ A signed in user
4
+ Should be able to sign out
5
+
6
+ Scenario: User signs out
7
+ Given I am signed up and confirmed as "email@person.com/password"
8
+ When I sign in as "email@person.com/password"
9
+ Then I should be signed in
10
+ And I sign out
11
+ Then I should see "Signed out"
12
+ And I should be signed out
13
+ When I return next time
14
+ Then I should be signed out
15
+
@@ -0,0 +1,45 @@
1
+ Feature: Sign up
2
+ In order to get access to protected sections of the site
3
+ A user
4
+ Should be able to sign up
5
+
6
+ Scenario: User signs up with invalid data
7
+ When I go to the sign up page
8
+ And I fill in "Email" with "invalidemail"
9
+ And I fill in "Password" with "password"
10
+ And I fill in "Confirm password" with ""
11
+ And I press "Sign Up"
12
+ Then I should see error messages
13
+
14
+ Scenario: User signs up with valid data
15
+ When I go to the sign up page
16
+ And I fill in "Email" with "email@person.com"
17
+ And I fill in "Password" with "password"
18
+ And I fill in "Confirm password" with "password"
19
+ And I press "Sign Up"
20
+ Then I should see "instructions for confirming"
21
+ And a confirmation message should be sent to "email@person.com"
22
+
23
+ Scenario: User confirms his account
24
+ Given I signed up with "email@person.com/password"
25
+ When I follow the confirmation link sent to "email@person.com"
26
+ Then I should see "Confirmed email and signed in"
27
+ And I should be signed in
28
+
29
+ Scenario: Signed in user clicks confirmation link again
30
+ Given I signed up with "email@person.com/password"
31
+ When I follow the confirmation link sent to "email@person.com"
32
+ Then I should be signed in
33
+ When I follow the confirmation link sent to "email@person.com"
34
+ Then I should see "Confirmed email and signed in"
35
+ And I should be signed in
36
+
37
+ Scenario: Signed out user clicks confirmation link again
38
+ Given I signed up with "email@person.com/password"
39
+ When I follow the confirmation link sent to "email@person.com"
40
+ Then I should be signed in
41
+ When I sign out
42
+ And I follow the confirmation link sent to "email@person.com"
43
+ Then I should see "Already confirmed email. Please sign in."
44
+ And I should be signed out
45
+
@@ -0,0 +1,116 @@
1
+ # General
2
+
3
+ Then /^I should see error messages$/ do
4
+ assert_match /error(s)? prohibited/m, response.body
5
+ end
6
+
7
+ # Database
8
+
9
+ Given /^no user exists with an email of "(.*)"$/ do |email|
10
+ assert_nil User.find_by_email(email)
11
+ end
12
+
13
+ Given /^I signed up with "(.*)\/(.*)"$/ do |email, password|
14
+ user = Factory :user,
15
+ :email => email,
16
+ :password => password,
17
+ :password_confirmation => password
18
+ end
19
+
20
+ Given /^I am signed up and confirmed as "(.*)\/(.*)"$/ do |email, password|
21
+ user = Factory :email_confirmed_user,
22
+ :email => email,
23
+ :password => password,
24
+ :password_confirmation => password
25
+ end
26
+
27
+ # Session
28
+
29
+ Then /^I should be signed in$/ do
30
+ assert controller.signed_in?
31
+ end
32
+
33
+ Then /^I should be signed out$/ do
34
+ assert ! controller.signed_in?
35
+ end
36
+
37
+ When /^session is cleared$/ do
38
+ request.reset_session
39
+ controller.instance_variable_set(:@_current_user, nil)
40
+ end
41
+
42
+ Given /^I have signed in with "(.*)\/(.*)"$/ do |email, password|
43
+ Given %{I am signed up and confirmed as "#{email}/#{password}"}
44
+ And %{I sign in as "#{email}/#{password}"}
45
+ end
46
+
47
+ # Emails
48
+
49
+ Then /^a confirmation message should be sent to "(.*)"$/ do |email|
50
+ user = User.find_by_email(email)
51
+ sent = ActionMailer::Base.deliveries.first
52
+ assert_equal [user.email], sent.to
53
+ assert_match /confirm/i, sent.subject
54
+ assert !user.confirmation_token.blank?
55
+ assert_match /#{user.confirmation_token}/, sent.body
56
+ end
57
+
58
+ When /^I follow the confirmation link sent to "(.*)"$/ do |email|
59
+ user = User.find_by_email(email)
60
+ visit new_user_confirmation_path(:user_id => user,
61
+ :token => user.confirmation_token)
62
+ end
63
+
64
+ Then /^a password reset message should be sent to "(.*)"$/ do |email|
65
+ user = User.find_by_email(email)
66
+ sent = ActionMailer::Base.deliveries.first
67
+ assert_equal [user.email], sent.to
68
+ assert_match /password/i, sent.subject
69
+ assert !user.confirmation_token.blank?
70
+ assert_match /#{user.confirmation_token}/, sent.body
71
+ end
72
+
73
+ When /^I follow the password reset link sent to "(.*)"$/ do |email|
74
+ user = User.find_by_email(email)
75
+ visit edit_user_password_path(:user_id => user,
76
+ :token => user.confirmation_token)
77
+ end
78
+
79
+ When /^I try to change the password of "(.*)" without token$/ do |email|
80
+ user = User.find_by_email(email)
81
+ visit edit_user_password_path(:user_id => user)
82
+ end
83
+
84
+ Then /^I should be forbidden$/ do
85
+ assert_response :forbidden
86
+ end
87
+
88
+ # Actions
89
+
90
+ When /^I sign in as "(.*)\/(.*)"$/ do |email, password|
91
+ When %{I go to the sign in page}
92
+ And %{I fill in "Email" with "#{email}"}
93
+ And %{I fill in "Password" with "#{password}"}
94
+ And %{I press "Sign In"}
95
+ end
96
+
97
+ When /^I sign out$/ do
98
+ visit '/session', :delete
99
+ end
100
+
101
+ When /^I request password reset link to be sent to "(.*)"$/ do |email|
102
+ When %{I go to the password reset request page}
103
+ And %{I fill in "Email address" with "#{email}"}
104
+ And %{I press "Reset password"}
105
+ end
106
+
107
+ When /^I update my password with "(.*)\/(.*)"$/ do |password, confirmation|
108
+ And %{I fill in "Choose password" with "#{password}"}
109
+ And %{I fill in "Confirm password" with "#{confirmation}"}
110
+ And %{I press "Save this password"}
111
+ end
112
+
113
+ When /^I return next time$/ do
114
+ When %{session is cleared}
115
+ And %{I go to the homepage}
116
+ end
@@ -0,0 +1,5 @@
1
+ Factory.factories.each do |name, factory|
2
+ Given /^an? #{name} exists with an? (.*) of "([^"]*)"$/ do |attr, value|
3
+ Factory(name, attr.gsub(' ', '_') => value)
4
+ end
5
+ end