benaldred-clearance 0.8.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/CHANGELOG.md +274 -0
  2. data/LICENSE +21 -0
  3. data/README.md +129 -0
  4. data/Rakefile +87 -0
  5. data/VERSION +1 -0
  6. data/app/controllers/clearance/confirmations_controller.rb +76 -0
  7. data/app/controllers/clearance/passwords_controller.rb +85 -0
  8. data/app/controllers/clearance/sessions_controller.rb +67 -0
  9. data/app/controllers/clearance/users_controller.rb +35 -0
  10. data/app/models/clearance_mailer.rb +21 -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/generators/clearance/USAGE +1 -0
  19. data/generators/clearance/clearance_generator.rb +68 -0
  20. data/generators/clearance/lib/insert_commands.rb +33 -0
  21. data/generators/clearance/lib/rake_commands.rb +22 -0
  22. data/generators/clearance/templates/README +24 -0
  23. data/generators/clearance/templates/clearance.rb +3 -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 +19 -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 +122 -0
  35. data/generators/clearance_features/templates/features/support/paths.rb +23 -0
  36. data/generators/clearance_views/USAGE +0 -0
  37. data/generators/clearance_views/clearance_views_generator.rb +27 -0
  38. data/generators/clearance_views/templates/formtastic/passwords/edit.html.erb +21 -0
  39. data/generators/clearance_views/templates/formtastic/passwords/new.html.erb +15 -0
  40. data/generators/clearance_views/templates/formtastic/sessions/new.html.erb +21 -0
  41. data/generators/clearance_views/templates/formtastic/users/_inputs.html.erb +6 -0
  42. data/generators/clearance_views/templates/formtastic/users/new.html.erb +10 -0
  43. data/lib/clearance.rb +7 -0
  44. data/lib/clearance/authentication.rb +131 -0
  45. data/lib/clearance/configuration.rb +26 -0
  46. data/lib/clearance/extensions/errors.rb +6 -0
  47. data/lib/clearance/extensions/rescue.rb +5 -0
  48. data/lib/clearance/routes.rb +49 -0
  49. data/lib/clearance/user.rb +215 -0
  50. data/rails/init.rb +1 -0
  51. data/shoulda_macros/clearance.rb +266 -0
  52. data/test/controllers/confirmations_controller_test.rb +104 -0
  53. data/test/controllers/passwords_controller_test.rb +183 -0
  54. data/test/controllers/sessions_controller_test.rb +146 -0
  55. data/test/controllers/users_controller_test.rb +65 -0
  56. data/test/models/clearance_mailer_test.rb +55 -0
  57. data/test/models/user_test.rb +255 -0
  58. data/test/rails_root/app/controllers/accounts_controller.rb +10 -0
  59. data/test/rails_root/app/controllers/application_controller.rb +6 -0
  60. data/test/rails_root/app/helpers/application_helper.rb +5 -0
  61. data/test/rails_root/app/helpers/confirmations_helper.rb +2 -0
  62. data/test/rails_root/app/helpers/passwords_helper.rb +2 -0
  63. data/test/rails_root/app/models/user.rb +3 -0
  64. data/test/rails_root/config/boot.rb +110 -0
  65. data/test/rails_root/config/environment.rb +17 -0
  66. data/test/rails_root/config/environments/development.rb +19 -0
  67. data/test/rails_root/config/environments/production.rb +1 -0
  68. data/test/rails_root/config/environments/test.rb +36 -0
  69. data/test/rails_root/config/initializers/clearance.rb +3 -0
  70. data/test/rails_root/config/initializers/inflections.rb +10 -0
  71. data/test/rails_root/config/initializers/mime_types.rb +5 -0
  72. data/test/rails_root/config/initializers/requires.rb +13 -0
  73. data/test/rails_root/config/initializers/time_formats.rb +4 -0
  74. data/test/rails_root/config/routes.rb +6 -0
  75. data/test/rails_root/features/step_definitions/clearance_steps.rb +122 -0
  76. data/test/rails_root/features/step_definitions/web_steps.rb +259 -0
  77. data/test/rails_root/features/support/env.rb +47 -0
  78. data/test/rails_root/features/support/paths.rb +23 -0
  79. data/test/rails_root/public/dispatch.rb +10 -0
  80. data/test/rails_root/script/create_project.rb +52 -0
  81. data/test/rails_root/test/factories/clearance.rb +13 -0
  82. data/test/rails_root/test/functional/accounts_controller_test.rb +23 -0
  83. data/test/rails_root/vendor/gems/justinfrench-formtastic-0.2.1/generators/formtastic_stylesheets/formtastic_stylesheets_generator.rb +21 -0
  84. data/test/rails_root/vendor/gems/justinfrench-formtastic-0.2.1/lib/formtastic.rb +1236 -0
  85. data/test/rails_root/vendor/gems/justinfrench-formtastic-0.2.1/lib/justin_french/formtastic.rb +10 -0
  86. data/test/rails_root/vendor/gems/justinfrench-formtastic-0.2.1/rails/init.rb +3 -0
  87. data/test/rails_root/vendor/gems/justinfrench-formtastic-0.2.1/spec/formtastic_spec.rb +2900 -0
  88. data/test/rails_root/vendor/gems/justinfrench-formtastic-0.2.1/spec/test_helper.rb +14 -0
  89. data/test/test_helper.rb +19 -0
  90. metadata +160 -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|.*Routes).*$/ 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,24 @@
1
+
2
+ *******************************************************************************
3
+
4
+ Next:
5
+
6
+ 1. Configure default url options for the mailer to generate URLs in emails.
7
+ In production.rb it must be the actual host your application is deployed to.
8
+ In config/environments/test.rb and config/environments/development.rb:
9
+
10
+ config.action_mailer.default_url_options = { :host => 'localhost:3000' }
11
+
12
+ 2. Migrate:
13
+
14
+ rake db:migrate
15
+
16
+ 4. Make sure you're displaying flashes somewhere. For instance, in a layout:
17
+
18
+ <div id="flash">
19
+ <% flash.each do |key, value| -%>
20
+ <div id="flash_<%= key %>"><%=h value %></div>
21
+ <% end -%>
22
+ </div>
23
+
24
+ *******************************************************************************
@@ -0,0 +1,3 @@
1
+ Clearance.configure do |config|
2
+ config.mailer_sender = 'donotreply@example.com'
3
+ end
@@ -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<%= schema_version_constant %> < 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,19 @@
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/support/paths.rb",
10
+ "features/sign_in.feature",
11
+ "features/sign_out.feature",
12
+ "features/sign_up.feature",
13
+ "features/password_reset.feature"].each do |file|
14
+ m.file file, file
15
+ end
16
+ end
17
+ end
18
+
19
+ 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,122 @@
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
+ assert !user.confirmation_token.blank?
52
+ assert !ActionMailer::Base.deliveries.empty?
53
+ result = ActionMailer::Base.deliveries.any? do |email|
54
+ email.to == [user.email] &&
55
+ email.subject =~ /confirm/i &&
56
+ email.body =~ /#{user.confirmation_token}/
57
+ end
58
+ assert result
59
+ end
60
+
61
+ When /^I follow the confirmation link sent to "(.*)"$/ do |email|
62
+ user = User.find_by_email(email)
63
+ visit new_user_confirmation_path(:user_id => user,
64
+ :token => user.confirmation_token)
65
+ end
66
+
67
+ Then /^a password reset message should be sent to "(.*)"$/ do |email|
68
+ user = User.find_by_email(email)
69
+ assert !user.confirmation_token.blank?
70
+ assert !ActionMailer::Base.deliveries.empty?
71
+ result = ActionMailer::Base.deliveries.any? do |email|
72
+ email.to == [user.email] &&
73
+ email.subject =~ /password/i &&
74
+ email.body =~ /#{user.confirmation_token}/
75
+ end
76
+ assert result
77
+ end
78
+
79
+ When /^I follow the password reset link sent to "(.*)"$/ do |email|
80
+ user = User.find_by_email(email)
81
+ visit edit_user_password_path(:user_id => user,
82
+ :token => user.confirmation_token)
83
+ end
84
+
85
+ When /^I try to change the password of "(.*)" without token$/ do |email|
86
+ user = User.find_by_email(email)
87
+ visit edit_user_password_path(:user_id => user)
88
+ end
89
+
90
+ Then /^I should be forbidden$/ do
91
+ assert_response :forbidden
92
+ end
93
+
94
+ # Actions
95
+
96
+ When /^I sign in as "(.*)\/(.*)"$/ do |email, password|
97
+ When %{I go to the sign in page}
98
+ And %{I fill in "Email" with "#{email}"}
99
+ And %{I fill in "Password" with "#{password}"}
100
+ And %{I press "Sign in"}
101
+ end
102
+
103
+ When /^I sign out$/ do
104
+ visit '/sign_out'
105
+ end
106
+
107
+ When /^I request password reset link to be sent to "(.*)"$/ do |email|
108
+ When %{I go to the password reset request page}
109
+ And %{I fill in "Email address" with "#{email}"}
110
+ And %{I press "Reset password"}
111
+ end
112
+
113
+ When /^I update my password with "(.*)\/(.*)"$/ do |password, confirmation|
114
+ And %{I fill in "Choose password" with "#{password}"}
115
+ And %{I fill in "Confirm password" with "#{confirmation}"}
116
+ And %{I press "Save this password"}
117
+ end
118
+
119
+ When /^I return next time$/ do
120
+ When %{session is cleared}
121
+ And %{I go to the homepage}
122
+ end