clearance 1.10.1 → 1.17.0

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 (106) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +20 -8
  4. data/.yardopts +3 -0
  5. data/Appraisals +13 -16
  6. data/Gemfile +7 -5
  7. data/Gemfile.lock +124 -130
  8. data/NEWS.md +171 -2
  9. data/README.md +99 -42
  10. data/app/controllers/clearance/passwords_controller.rb +35 -21
  11. data/app/controllers/clearance/sessions_controller.rb +17 -3
  12. data/app/controllers/clearance/users_controller.rb +10 -4
  13. data/app/mailers/clearance_mailer.rb +2 -3
  14. data/app/views/clearance_mailer/change_password.text.erb +1 -1
  15. data/app/views/layouts/application.html.erb +0 -1
  16. data/bin/setup +6 -2
  17. data/clearance.gemspec +5 -2
  18. data/config/locales/clearance.en.yml +9 -0
  19. data/gemfiles/rails_4.2.gemfile +20 -0
  20. data/gemfiles/rails_5.0.gemfile +21 -0
  21. data/gemfiles/rails_5.1.gemfile +21 -0
  22. data/gemfiles/rails_5.2.gemfile +21 -0
  23. data/lib/clearance/authentication.rb +63 -3
  24. data/lib/clearance/authorization.rb +48 -5
  25. data/lib/clearance/back_door.rb +55 -6
  26. data/lib/clearance/configuration.rb +50 -10
  27. data/lib/clearance/constraints/signed_in.rb +21 -0
  28. data/lib/clearance/constraints/signed_out.rb +12 -0
  29. data/lib/clearance/constraints.rb +12 -0
  30. data/lib/clearance/controller.rb +13 -0
  31. data/lib/clearance/default_sign_in_guard.rb +17 -0
  32. data/lib/clearance/engine.rb +18 -5
  33. data/lib/clearance/password_strategies/bcrypt.rb +16 -21
  34. data/lib/clearance/password_strategies/bcrypt_migration_from_sha1.rb +10 -0
  35. data/lib/clearance/password_strategies/blowfish.rb +10 -1
  36. data/lib/clearance/password_strategies/sha1.rb +9 -0
  37. data/lib/clearance/password_strategies.rb +13 -0
  38. data/lib/clearance/rack_session.rb +13 -0
  39. data/lib/clearance/rspec.rb +15 -4
  40. data/lib/clearance/session.rb +62 -13
  41. data/lib/clearance/session_status.rb +7 -0
  42. data/lib/clearance/sign_in_guard.rb +65 -0
  43. data/lib/clearance/test_unit.rb +3 -3
  44. data/lib/clearance/testing/controller_helpers.rb +57 -0
  45. data/lib/clearance/testing/deny_access_matcher.rb +36 -2
  46. data/lib/clearance/testing/helpers.rb +9 -25
  47. data/lib/clearance/testing/view_helpers.rb +32 -0
  48. data/lib/clearance/token.rb +7 -0
  49. data/lib/clearance/user.rb +183 -4
  50. data/lib/clearance/version.rb +1 -1
  51. data/lib/generators/clearance/install/install_generator.rb +28 -9
  52. data/lib/generators/clearance/install/templates/README +1 -1
  53. data/lib/generators/clearance/install/templates/clearance.rb +1 -0
  54. data/lib/generators/clearance/install/templates/db/migrate/{add_clearance_to_users.rb → add_clearance_to_users.rb.erb} +3 -3
  55. data/lib/generators/clearance/install/templates/db/migrate/{create_users.rb → create_users.rb.erb} +2 -2
  56. data/lib/generators/clearance/install/templates/user.rb.erb +3 -0
  57. data/lib/generators/clearance/routes/routes_generator.rb +23 -0
  58. data/lib/generators/clearance/routes/templates/routes.rb +7 -7
  59. data/lib/generators/clearance/specs/templates/factories/clearance.rb +2 -2
  60. data/lib/generators/clearance/specs/templates/features/clearance/user_signs_out_spec.rb.tt +1 -1
  61. data/lib/generators/clearance/specs/templates/features/clearance/visitor_resets_password_spec.rb.tt +12 -3
  62. data/lib/generators/clearance/specs/templates/features/clearance/visitor_signs_in_spec.rb.tt +3 -3
  63. data/lib/generators/clearance/specs/templates/features/clearance/visitor_signs_up_spec.rb.tt +1 -1
  64. data/lib/generators/clearance/specs/templates/features/clearance/visitor_updates_password_spec.rb.tt +2 -2
  65. data/lib/generators/clearance/specs/templates/support/features/clearance_helpers.rb +2 -2
  66. data/spec/acceptance/clearance_installation_spec.rb +15 -7
  67. data/spec/app_templates/app/models/rails5/user.rb +5 -0
  68. data/spec/app_templates/config/initializers/clearance.rb +2 -0
  69. data/spec/app_templates/testapp/Gemfile +1 -1
  70. data/spec/app_templates/testapp/app/controllers/home_controller.rb +5 -1
  71. data/spec/clearance/back_door_spec.rb +70 -6
  72. data/spec/clearance/session_spec.rb +4 -16
  73. data/spec/clearance/testing/controller_helpers_spec.rb +38 -0
  74. data/spec/clearance/testing/view_helpers_spec.rb +37 -0
  75. data/spec/configuration_spec.rb +79 -86
  76. data/spec/controllers/apis_controller_spec.rb +6 -2
  77. data/spec/controllers/forgeries_controller_spec.rb +12 -3
  78. data/spec/controllers/passwords_controller_spec.rb +74 -38
  79. data/spec/controllers/permissions_controller_spec.rb +13 -3
  80. data/spec/controllers/sessions_controller_spec.rb +40 -11
  81. data/spec/controllers/users_controller_spec.rb +16 -8
  82. data/spec/dummy/app/controllers/application_controller.rb +5 -1
  83. data/spec/dummy/application.rb +9 -11
  84. data/spec/factories.rb +5 -5
  85. data/spec/generators/clearance/install/install_generator_spec.rb +29 -3
  86. data/spec/generators/clearance/routes/routes_generator_spec.rb +5 -1
  87. data/spec/helpers/helper_helpers_spec.rb +10 -0
  88. data/spec/{user_spec.rb → models/user_spec.rb} +10 -1
  89. data/spec/password_strategies/blowfish_spec.rb +1 -1
  90. data/spec/requests/cookie_options_spec.rb +52 -0
  91. data/spec/requests/csrf_rotation_spec.rb +35 -0
  92. data/spec/requests/password_maintenance_spec.rb +18 -0
  93. data/spec/requests/token_expiration_spec.rb +54 -0
  94. data/spec/spec_helper.rb +22 -4
  95. data/spec/support/environment.rb +12 -0
  96. data/spec/support/generator_spec_helpers.rb +13 -1
  97. data/spec/support/http_method_shim.rb +25 -0
  98. data/spec/support/request_with_remember_token.rb +5 -0
  99. data/spec/views/view_helpers_spec.rb +10 -0
  100. metadata +69 -15
  101. data/gemfiles/rails3.2.gemfile +0 -18
  102. data/gemfiles/rails4.0.gemfile +0 -19
  103. data/gemfiles/rails4.1.gemfile +0 -18
  104. data/gemfiles/rails4.2.gemfile +0 -18
  105. data/lib/generators/clearance/install/templates/user.rb +0 -3
  106. data/spec/clearance/testing/helpers_spec.rb +0 -38
@@ -5,11 +5,9 @@ describe Clearance::PasswordsController do
5
5
 
6
6
  describe "#new" do
7
7
  it "renders the password reset form" do
8
- user = create(:user)
8
+ get :new
9
9
 
10
- get :new, user_id: user
11
-
12
- expect(response).to be_success
10
+ expect(response).to be_successful
13
11
  expect(response).to render_template(:new)
14
12
  end
15
13
  end
@@ -19,7 +17,9 @@ describe Clearance::PasswordsController do
19
17
  it "generates a password change token" do
20
18
  user = create(:user)
21
19
 
22
- post :create, password: { email: user.email.upcase }
20
+ post :create, params: {
21
+ password: { email: user.email.upcase },
22
+ }
23
23
 
24
24
  expect(user.reload.confirmation_token).not_to be_nil
25
25
  end
@@ -28,7 +28,9 @@ describe Clearance::PasswordsController do
28
28
  ActionMailer::Base.deliveries.clear
29
29
  user = create(:user)
30
30
 
31
- post :create, password: { email: user.email }
31
+ post :create, params: {
32
+ password: { email: user.email },
33
+ }
32
34
 
33
35
  email = ActionMailer::Base.deliveries.last
34
36
  expect(email.subject).to match(/change your password/i)
@@ -40,7 +42,9 @@ describe Clearance::PasswordsController do
40
42
  ActionMailer::Base.deliveries.clear
41
43
  email = "this_user_does_not_exist@non_existent_domain.com"
42
44
 
43
- post :create, password: { email: email }
45
+ post :create, params: {
46
+ password: { email: email },
47
+ }
44
48
 
45
49
  expect(ActionMailer::Base.deliveries).to be_empty
46
50
  end
@@ -48,22 +52,42 @@ describe Clearance::PasswordsController do
48
52
  it "still responds with success so as not to leak registered users" do
49
53
  email = "this_user_does_not_exist@non_existent_domain.com"
50
54
 
51
- post :create, password: { email: email }
55
+ post :create, params: {
56
+ password: { email: email },
57
+ }
52
58
 
53
- expect(response).to be_success
59
+ expect(response).to be_successful
54
60
  expect(response).to render_template "passwords/create"
55
61
  end
56
62
  end
57
63
  end
58
64
 
59
65
  describe "#edit" do
60
- context "valid id and token are supplied" do
61
- it "renders the password form for the user" do
66
+ context "valid id and token are supplied in url" do
67
+ it "redirects to the edit page with token now removed from url" do
68
+ user = create(:user, :with_forgotten_password)
69
+
70
+ get :edit, params: {
71
+ user_id: user,
72
+ token: user.confirmation_token,
73
+ }
74
+
75
+ expect(response).to be_redirect
76
+ expect(response).to redirect_to edit_user_password_url(user)
77
+ expect(session[:password_reset_token]).to eq user.confirmation_token
78
+ end
79
+ end
80
+
81
+ context "valid id in url and valid token in session" do
82
+ it "renders the password reset form" do
62
83
  user = create(:user, :with_forgotten_password)
63
84
 
64
- get :edit, user_id: user, token: user.confirmation_token
85
+ request.session[:password_reset_token] = user.confirmation_token
86
+ get :edit, params: {
87
+ user_id: user,
88
+ }
65
89
 
66
- expect(response).to be_success
90
+ expect(response).to be_successful
67
91
  expect(response).to render_template(:edit)
68
92
  expect(assigns(:user)).to eq user
69
93
  end
@@ -71,7 +95,10 @@ describe Clearance::PasswordsController do
71
95
 
72
96
  context "blank token is supplied" do
73
97
  it "renders the new password reset form with a flash notice" do
74
- get :edit, user_id: 1, token: ""
98
+ get :edit, params: {
99
+ user_id: 1,
100
+ token: "",
101
+ }
75
102
 
76
103
  expect(response).to render_template(:new)
77
104
  expect(flash.now[:notice]).to match(/double check the URL/i)
@@ -82,12 +109,31 @@ describe Clearance::PasswordsController do
82
109
  it "renders the new password reset form with a flash notice" do
83
110
  user = create(:user, :with_forgotten_password)
84
111
 
85
- get :edit, user_id: 1, token: user.confirmation_token + "a"
112
+ get :edit, params: {
113
+ user_id: 1,
114
+ token: user.confirmation_token + "a",
115
+ }
86
116
 
87
117
  expect(response).to render_template(:new)
88
118
  expect(flash.now[:notice]).to match(/double check the URL/i)
89
119
  end
90
120
  end
121
+
122
+ context "old token in session and recent token in params" do
123
+ it "updates password reset session and redirect to edit page" do
124
+ user = create(:user, :with_forgotten_password)
125
+ request.session[:password_reset_token] = user.confirmation_token
126
+
127
+ user.forgot_password!
128
+ get :edit, params: {
129
+ user_id: user.id,
130
+ token: user.reload.confirmation_token,
131
+ }
132
+
133
+ expect(response).to redirect_to(edit_user_password_url(user))
134
+ expect(session[:password_reset_token]).to eq(user.confirmation_token)
135
+ end
136
+ end
91
137
  end
92
138
 
93
139
  describe "#update" do
@@ -96,37 +142,24 @@ describe Clearance::PasswordsController do
96
142
  user = create(:user, :with_forgotten_password)
97
143
  old_encrypted_password = user.encrypted_password
98
144
 
99
- put :update, update_parameters(user, new_password: "my_new_password")
145
+ put :update, params: update_parameters(
146
+ user,
147
+ new_password: "my_new_password",
148
+ )
100
149
 
101
150
  expect(user.reload.encrypted_password).not_to eq old_encrypted_password
102
151
  end
103
-
104
- it "sets the remember token and clears the confirmation token" do
105
- user = create(:user, :with_forgotten_password)
106
-
107
- put :update, update_parameters(user, new_password: "my_new_password")
108
-
109
- user.reload
110
- expect(user.remember_token).not_to be_nil
111
- expect(user.confirmation_token).to be_nil
112
- end
113
-
114
- it "signs the user in and redirects" do
115
- user = create(:user, :with_forgotten_password)
116
-
117
- put :update, update_parameters(user, new_password: "my_new_password")
118
-
119
- expect(response).to redirect_to(Clearance.configuration.redirect_url)
120
- expect(cookies[:remember_token]).to be_present
121
- end
122
152
  end
123
153
 
124
- context "no password provided" do
154
+ context "password update fails" do
125
155
  it "does not update the password" do
126
156
  user = create(:user, :with_forgotten_password)
127
157
  old_encrypted_password = user.encrypted_password
128
158
 
129
- put :update, update_parameters(user, new_password: "")
159
+ put :update, params: update_parameters(
160
+ user,
161
+ new_password: "",
162
+ )
130
163
 
131
164
  user.reload
132
165
  expect(user.encrypted_password).to eq old_encrypted_password
@@ -136,7 +169,10 @@ describe Clearance::PasswordsController do
136
169
  it "re-renders the password edit form" do
137
170
  user = create(:user, :with_forgotten_password)
138
171
 
139
- put :update, update_parameters(user, new_password: "")
172
+ put :update, params: update_parameters(
173
+ user,
174
+ new_password: "",
175
+ )
140
176
 
141
177
  expect(flash.now[:notice]).to match(/password can't be blank/i)
142
178
  expect(response).to render_template(:edit)
@@ -3,14 +3,18 @@ require 'spec_helper'
3
3
  class PermissionsController < ActionController::Base
4
4
  include Clearance::Controller
5
5
 
6
- before_filter :require_login, only: :show
6
+ if respond_to?(:before_action)
7
+ before_action :require_login, only: :show
8
+ else
9
+ before_filter :require_login, only: :show
10
+ end
7
11
 
8
12
  def new
9
- render text: 'New page'
13
+ head :ok
10
14
  end
11
15
 
12
16
  def show
13
- render text: 'Show page'
17
+ head :ok
14
18
  end
15
19
  end
16
20
 
@@ -54,6 +58,12 @@ describe PermissionsController do
54
58
 
55
59
  expect(subject).to deny_access(redirect: sign_in_url)
56
60
  end
61
+
62
+ it "denies access to show and display a flash message" do
63
+ get :show
64
+
65
+ expect(flash[:notice]).to match(/^Please sign in to continue/)
66
+ end
57
67
  end
58
68
 
59
69
  context 'when remember_token is blank' do
@@ -28,7 +28,9 @@ describe Clearance::SessionsController do
28
28
  it "renders the page with error" do
29
29
  user = create(:user_with_optional_password)
30
30
 
31
- post :create, session: { email: user.email, password: user.password }
31
+ post :create, params: {
32
+ session: { email: user.email, password: user.password },
33
+ }
32
34
 
33
35
  expect(response).to render_template(:new)
34
36
  expect(flash[:notice]).to match(/^Bad email or password/)
@@ -39,13 +41,15 @@ describe Clearance::SessionsController do
39
41
  before do
40
42
  @user = create(:user)
41
43
  @user.update_attribute :remember_token, "old-token"
42
- post :create, session: { email: @user.email, password: @user.password }
44
+ post :create, params: {
45
+ session: { email: @user.email, password: @user.password },
46
+ }
43
47
  end
44
48
 
45
49
  it { should redirect_to_url_after_create }
46
50
 
47
51
  it "sets the user in the clearance session" do
48
- expect(controller.current_user).to eq @user
52
+ expect(request.env[:clearance].current_user).to eq @user
49
53
  end
50
54
 
51
55
  it "should not change the remember token" do
@@ -54,15 +58,40 @@ describe Clearance::SessionsController do
54
58
  end
55
59
 
56
60
  context "with good credentials and a session return url" do
57
- before do
58
- @user = create(:user)
59
- @return_url = "/url_in_the_session?foo=bar"
60
- @request.session[:return_to] = @return_url
61
- post :create, session: { email: @user.email, password: @user.password }
61
+ it "redirects to the return URL maintaining query and fragment" do
62
+ user = create(:user)
63
+ return_url = "/url_in_the_session?foo=bar#baz"
64
+ request.session[:return_to] = return_url
65
+
66
+ post :create, params: {
67
+ session: { email: user.email, password: user.password },
68
+ }
69
+
70
+ should redirect_to(return_url)
71
+ end
72
+
73
+ it "redirects to the return URL maintaining query without fragment" do
74
+ user = create(:user)
75
+ return_url = "/url_in_the_session?foo=bar"
76
+ request.session[:return_to] = return_url
77
+
78
+ post :create, params: {
79
+ session: { email: user.email, password: user.password },
80
+ }
81
+
82
+ should redirect_to(return_url)
62
83
  end
63
84
 
64
- it "redirects to the return URL" do
65
- should redirect_to(@return_url)
85
+ it "redirects to the return URL without query or fragment" do
86
+ user = create(:user)
87
+ return_url = "/url_in_the_session"
88
+ request.session[:return_to] = return_url
89
+
90
+ post :create, params: {
91
+ session: { email: user.email, password: user.password },
92
+ }
93
+
94
+ should redirect_to(return_url)
66
95
  end
67
96
  end
68
97
  end
@@ -92,7 +121,7 @@ describe Clearance::SessionsController do
92
121
  end
93
122
 
94
123
  it "should unset the current user" do
95
- expect(@controller.current_user).to be_nil
124
+ expect(request.env[:clearance].current_user).to be_nil
96
125
  end
97
126
  end
98
127
  end
@@ -8,15 +8,17 @@ describe Clearance::UsersController do
8
8
  it "renders a form for a new user" do
9
9
  get :new
10
10
 
11
- expect(response).to be_success
11
+ expect(response).to be_successful
12
12
  expect(response).to render_template(:new)
13
13
  end
14
14
 
15
15
  it "defaults email field to the value provided in the query string" do
16
- get :new, user: { email: "a@example.com" }
16
+ get :new, params: {
17
+ user: { email: "a@example.com" },
18
+ }
17
19
 
18
20
  expect(assigns(:user).email).to eq "a@example.com"
19
- expect(response).to be_success
21
+ expect(response).to be_successful
20
22
  expect(response).to render_template(:new)
21
23
  end
22
24
  end
@@ -36,10 +38,12 @@ describe Clearance::UsersController do
36
38
  context "when signed out" do
37
39
  context "with valid attributes" do
38
40
  it "assigns and creates a user then redirects to the redirect_url" do
39
- user_attributes = FactoryGirl.attributes_for(:user)
41
+ user_attributes = FactoryBot.attributes_for(:user)
40
42
  old_user_count = User.count
41
43
 
42
- post :create, user: user_attributes
44
+ post :create, params: {
45
+ user: user_attributes,
46
+ }
43
47
 
44
48
  expect(assigns(:user)).to be_present
45
49
  expect(User.count).to eq old_user_count + 1
@@ -49,12 +53,14 @@ describe Clearance::UsersController do
49
53
 
50
54
  context "with valid attributes and a session return url" do
51
55
  it "assigns and creates a user then redirects to the return_url" do
52
- user_attributes = FactoryGirl.attributes_for(:user)
56
+ user_attributes = FactoryBot.attributes_for(:user)
53
57
  old_user_count = User.count
54
58
  return_url = "/url_in_the_session"
55
59
  @request.session[:return_to] = return_url
56
60
 
57
- post :create, user: user_attributes
61
+ post :create, params: {
62
+ user: user_attributes,
63
+ }
58
64
 
59
65
  expect(assigns(:user)).to be_present
60
66
  expect(User.count).to eq old_user_count + 1
@@ -67,7 +73,9 @@ describe Clearance::UsersController do
67
73
  it "redirects to the configured clearance redirect url" do
68
74
  sign_in
69
75
 
70
- post :create, user: {}
76
+ post :create, params: {
77
+ user: {},
78
+ }
71
79
 
72
80
  expect(response).to redirect_to(Clearance.configuration.redirect_url)
73
81
  end
@@ -2,6 +2,10 @@ class ApplicationController < ActionController::Base
2
2
  include Clearance::Controller
3
3
 
4
4
  def show
5
- render text: '', layout: 'application'
5
+ if Rails::VERSION::MAJOR >= 5
6
+ render html: "", layout: "application"
7
+ else
8
+ render text: "", layout: "application"
9
+ end
6
10
  end
7
11
  end
@@ -4,10 +4,6 @@ require "clearance"
4
4
  module Dummy
5
5
  APP_ROOT = File.expand_path("..", __FILE__).freeze
6
6
 
7
- def self.rails4?
8
- Rails::VERSION::MAJOR >= 4
9
- end
10
-
11
7
  I18n.enforce_available_locales = true
12
8
 
13
9
  class Application < Rails::Application
@@ -18,7 +14,6 @@ module Dummy
18
14
  config.action_mailer.delivery_method = :test
19
15
  config.active_support.deprecation = :stderr
20
16
  config.active_support.test_order = :random
21
- config.assets.enabled = true
22
17
  config.cache_classes = true
23
18
  config.consider_all_requests_local = true
24
19
  config.eager_load = false
@@ -28,13 +23,16 @@ module Dummy
28
23
  config.paths["app/views"] << "#{APP_ROOT}/app/views"
29
24
  config.paths["config/database"] = "#{APP_ROOT}/config/database.yml"
30
25
  config.paths["log"] = "tmp/log/development.log"
31
- config.secret_token = "SECRET_TOKEN_IS_MIN_30_CHARS_LONG"
32
26
 
33
- if Dummy.rails4?
34
- config.paths.add "config/routes.rb", with: "#{APP_ROOT}/config/routes.rb"
35
- config.secret_key_base = "SECRET_KEY_BASE"
36
- else
37
- config.paths.add "config/routes", with: "#{APP_ROOT}/config/routes.rb"
27
+ config.paths.add "config/routes.rb", with: "#{APP_ROOT}/config/routes.rb"
28
+ config.secret_key_base = "SECRET_KEY_BASE"
29
+
30
+ if config.active_record.sqlite3.respond_to?(:represent_boolean_as_integer)
31
+ config.active_record.sqlite3.represent_boolean_as_integer = true
32
+ end
33
+
34
+ if config.respond_to?(:active_job)
35
+ config.active_job.queue_adapter = :inline
38
36
  end
39
37
 
40
38
  def require_environment!
data/spec/factories.rb CHANGED
@@ -1,19 +1,19 @@
1
- FactoryGirl.define do
1
+ FactoryBot.define do
2
2
  sequence :email do |n|
3
3
  "user#{n}@example.com"
4
4
  end
5
5
 
6
6
  factory :user do
7
7
  email
8
- password 'password'
8
+ password { 'password' }
9
9
 
10
10
  trait :with_forgotten_password do
11
- confirmation_token Clearance::Token.new
11
+ confirmation_token { Clearance::Token.new }
12
12
  end
13
13
 
14
14
  factory :user_with_optional_password, class: 'UserWithOptionalPassword' do
15
- password nil
16
- encrypted_password ''
15
+ password { nil }
16
+ encrypted_password { '' }
17
17
  end
18
18
  end
19
19
  end
@@ -37,6 +37,7 @@ describe Clearance::Generators::InstallGenerator, :generator do
37
37
 
38
38
  expect(user_class).to exist
39
39
  expect(user_class).to have_correct_syntax
40
+ expect(user_class).to contain_models_inherit_from
40
41
  expect(user_class).to contain("include Clearance::User")
41
42
  end
42
43
  end
@@ -51,6 +52,7 @@ describe Clearance::Generators::InstallGenerator, :generator do
51
52
 
52
53
  expect(user_class).to exist
53
54
  expect(user_class).to have_correct_syntax
55
+ expect(user_class).to contain_models_inherit_from
54
56
  expect(user_class).to contain("include Clearance::User")
55
57
  expect(user_class).to have_method("previously_existed?")
56
58
  end
@@ -61,9 +63,7 @@ describe Clearance::Generators::InstallGenerator, :generator do
61
63
  context "users table does not exist" do
62
64
  it "creates a migration to create the users table" do
63
65
  provide_existing_application_controller
64
- allow(ActiveRecord::Base.connection).to receive(:table_exists?).
65
- with(:users).
66
- and_return(false)
66
+ table_does_not_exist(:users)
67
67
 
68
68
  run_generator
69
69
  migration = migration_file("db/migrate/create_users.rb")
@@ -115,4 +115,30 @@ describe Clearance::Generators::InstallGenerator, :generator do
115
115
  end
116
116
  end
117
117
  end
118
+
119
+ def table_does_not_exist(name)
120
+ connection = ActiveRecord::Base.connection
121
+
122
+ if connection.respond_to?(:data_source_exists?)
123
+ allow(connection).to receive(:data_source_exists?).
124
+ with(name).
125
+ and_return(false)
126
+ else
127
+ allow(connection).to receive(:table_exists?).
128
+ with(name).
129
+ and_return(false)
130
+ end
131
+ end
132
+
133
+ def contain_models_inherit_from
134
+ contain "< #{models_inherit_from}\n"
135
+ end
136
+
137
+ def models_inherit_from
138
+ if Rails.version >= "5.0.0"
139
+ "ApplicationRecord"
140
+ else
141
+ "ActiveRecord::Base"
142
+ end
143
+ end
118
144
  end
@@ -4,14 +4,18 @@ require "generators/clearance/routes/routes_generator"
4
4
  describe Clearance::Generators::RoutesGenerator, :generator do
5
5
  it "adds clearance routes to host application routes" do
6
6
  provide_existing_routes_file
7
+ provide_existing_initializer
7
8
 
8
9
  routes = file("config/routes.rb")
10
+ initializer = file("config/initializers/clearance.rb")
9
11
 
10
12
  run_generator
11
13
 
14
+ expect(initializer).to have_correct_syntax
15
+ expect(initializer).to contain("config.routes = false")
12
16
  expect(routes).to have_correct_syntax
13
17
  expect(routes).to contain(
14
- "get '/sign_in' => 'clearance/sessions#new', as: 'sign_in'"
18
+ 'get "/sign_in" => "clearance/sessions#new", as: "sign_in"'
15
19
  )
16
20
  end
17
21
  end
@@ -0,0 +1,10 @@
1
+ require "spec_helper"
2
+
3
+ describe "Clearance RSpec helper spec configuration", type: :helper do
4
+ it "lets me use clearance's helper methods in helper specs" do
5
+ user = double("User")
6
+ sign_in_as(user)
7
+
8
+ expect(helper.current_user).to eq user
9
+ end
10
+ end
@@ -23,7 +23,7 @@ describe User do
23
23
  end
24
24
 
25
25
  subject { create(:user) }
26
- it { is_expected.to validate_uniqueness_of(:email) }
26
+ it { is_expected.to validate_uniqueness_of(:email).case_insensitive }
27
27
  end
28
28
 
29
29
  describe ".authenticate" do
@@ -85,6 +85,15 @@ describe User do
85
85
 
86
86
  expect(user.confirmation_token).to be_nil
87
87
  end
88
+
89
+ it "sets the remember token" do
90
+ user = create(:user, :with_forgotten_password)
91
+
92
+ user.update_password("my_new_password")
93
+
94
+ user.reload
95
+ expect(user.remember_token).not_to be_nil
96
+ end
88
97
  end
89
98
 
90
99
  context "with blank password" do
@@ -24,7 +24,7 @@ describe Clearance::PasswordStrategies::Blowfish do
24
24
  model_instance.salt = salt
25
25
  model_instance.password = password
26
26
  cipher = OpenSSL::Cipher::Cipher.new("bf-cbc").encrypt
27
- cipher.key = Digest::SHA256.digest(salt)
27
+ cipher.key = Digest::SHA256.digest(salt).first(16)
28
28
  expected = cipher.update("--#{salt}--#{password}--") << cipher.final
29
29
 
30
30
  encrypted_password = Base64.decode64(model_instance.encrypted_password)
@@ -0,0 +1,52 @@
1
+ require "spec_helper"
2
+
3
+ describe "Cookie options" do
4
+ before do
5
+ Clearance.configuration.httponly = httponly
6
+ end
7
+
8
+ context "when httponly config value is false" do
9
+ let(:httponly) { false }
10
+ describe "sign in" do
11
+ before do
12
+ user = create(:user, password: "password")
13
+ get sign_in_path
14
+
15
+ post session_path, params: {
16
+ session: { email: user.email, password: "password" },
17
+ }
18
+ end
19
+
20
+ it { should_have_one_remember_token }
21
+
22
+ it "should not have the httponly flag set" do
23
+ expect(remember_token_cookies.last).not_to match(/HttpOnly/)
24
+ end
25
+ end
26
+ end
27
+
28
+ context "when httponly config value is true" do
29
+ let(:httponly) { true }
30
+ describe "sign in" do
31
+ before do
32
+ user = create(:user, password: "password")
33
+ get sign_in_path
34
+
35
+ post session_path, params: {
36
+ session: { email: user.email, password: "password" },
37
+ }
38
+ end
39
+
40
+ it { should_have_one_remember_token }
41
+
42
+ it "should have the httponly flag set" do
43
+ expect(remember_token_cookies.last).to match(/HttpOnly/)
44
+ end
45
+ end
46
+ end
47
+
48
+ def should_have_one_remember_token
49
+ expect(remember_token_cookies.length).to eq(1),
50
+ "expected one 'remember_token' cookie:\n#{remember_token_cookies}"
51
+ end
52
+ end
@@ -0,0 +1,35 @@
1
+ require "spec_helper"
2
+
3
+ describe "CSRF Rotation" do
4
+ around do |example|
5
+ ActionController::Base.allow_forgery_protection = true
6
+ example.run
7
+ ActionController::Base.allow_forgery_protection = false
8
+ end
9
+
10
+ context "Clearance is configured to rotate CSRF token on sign in" do
11
+ describe "sign in" do
12
+ it "rotates the CSRF token" do
13
+ Clearance.configure { |config| config.rotate_csrf_on_sign_in = true }
14
+ get sign_in_path
15
+ user = create(:user, password: "password")
16
+ original_token = csrf_token
17
+
18
+ post session_path, params: {
19
+ session: session_params(user, "password"),
20
+ }
21
+
22
+ expect(csrf_token).not_to eq original_token
23
+ expect(csrf_token).to be_present
24
+ end
25
+ end
26
+ end
27
+
28
+ def csrf_token
29
+ session[:_csrf_token]
30
+ end
31
+
32
+ def session_params(user, password)
33
+ { email: user.email, password: password, authenticity_token: csrf_token }
34
+ end
35
+ end