clearance 2.1.0 → 2.4.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.
- checksums.yaml +4 -4
- data/.erb-lint.yml +5 -0
- data/.github/workflows/tests.yml +52 -0
- data/Appraisals +14 -19
- data/Gemfile +11 -7
- data/Gemfile.lock +140 -85
- data/NEWS.md +62 -0
- data/README.md +23 -12
- data/RELEASING.md +25 -0
- data/Rakefile +6 -1
- data/app/controllers/clearance/passwords_controller.rb +2 -3
- data/app/views/clearance_mailer/change_password.html.erb +2 -2
- data/app/views/clearance_mailer/change_password.text.erb +2 -2
- data/app/views/passwords/edit.html.erb +1 -1
- data/clearance.gemspec +1 -0
- data/gemfiles/rails_5.0.gemfile +10 -9
- data/gemfiles/rails_5.1.gemfile +11 -10
- data/gemfiles/rails_5.2.gemfile +11 -10
- data/gemfiles/rails_6.0.gemfile +11 -10
- data/gemfiles/rails_6.1.gemfile +21 -0
- data/lib/clearance/back_door.rb +2 -1
- data/lib/clearance/configuration.rb +19 -0
- data/lib/clearance/password_strategies.rb +2 -5
- data/lib/clearance/password_strategies/argon2.rb +23 -0
- data/lib/clearance/rack_session.rb +1 -1
- data/lib/clearance/session.rb +24 -12
- data/lib/clearance/user.rb +11 -2
- data/lib/clearance/version.rb +1 -1
- data/lib/generators/clearance/install/install_generator.rb +4 -1
- data/spec/clearance/back_door_spec.rb +20 -4
- data/spec/clearance/rack_session_spec.rb +1 -2
- data/spec/clearance/session_spec.rb +116 -43
- data/spec/configuration_spec.rb +28 -0
- data/spec/generators/clearance/install/install_generator_spec.rb +8 -2
- data/spec/mailers/clearance_mailer_spec.rb +33 -0
- data/spec/models/user_spec.rb +29 -0
- data/spec/password_strategies/argon2_spec.rb +79 -0
- data/spec/support/clearance.rb +11 -0
- data/spec/support/request_with_remember_token.rb +8 -6
- metadata +29 -4
- data/.travis.yml +0 -27
data/README.md
CHANGED
@@ -60,7 +60,9 @@ Clearance.configure do |config|
|
|
60
60
|
config.password_strategy = Clearance::PasswordStrategies::BCrypt
|
61
61
|
config.redirect_url = "/"
|
62
62
|
config.rotate_csrf_on_sign_in = true
|
63
|
+
config.same_site = nil
|
63
64
|
config.secure_cookie = false
|
65
|
+
config.signed_cookie = false
|
64
66
|
config.sign_in_guards = []
|
65
67
|
config.user_model = "User"
|
66
68
|
config.parent_controller = "ApplicationController"
|
@@ -285,24 +287,33 @@ and `password` attributes. Over-riding the `email_optional?` or
|
|
285
287
|
`skip_password_validation?` methods to return `true` will disable those
|
286
288
|
validations from being added.
|
287
289
|
|
288
|
-
###
|
290
|
+
### Signed Cookies
|
289
291
|
|
290
|
-
|
291
|
-
|
292
|
-
automatically take advantage of your configured queue.
|
292
|
+
By default, Clearance uses unsigned cookies. If you would like to use signed
|
293
|
+
cookies you can do so by overriding the default in an initializer like so:
|
293
294
|
|
294
|
-
|
295
|
-
|
296
|
-
|
295
|
+
```ruby
|
296
|
+
Clearance.configure do |config|
|
297
|
+
# ... other overrides
|
298
|
+
config.signed_cookie = true
|
299
|
+
end
|
300
|
+
```
|
301
|
+
|
302
|
+
If you are currently not using unsigned cookies but would like to migrate your
|
303
|
+
users over to them without breaking current sessions, you can do so by passing
|
304
|
+
in `:migrate` rather than `true` as so:
|
297
305
|
|
298
306
|
```ruby
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
end
|
307
|
+
Clearance.configure do |config|
|
308
|
+
# ... other overrides
|
309
|
+
config.signed_cookie = :migrate
|
303
310
|
end
|
304
311
|
```
|
305
312
|
|
313
|
+
You can read more about signed cookies in Clearance and why they are a good idea
|
314
|
+
in the [pull request that added them](https://github.com/thoughtbot/clearance/pull/917).
|
315
|
+
|
316
|
+
|
306
317
|
## Extending Sign In
|
307
318
|
|
308
319
|
By default, Clearance will sign in any user with valid credentials. If you need
|
@@ -333,7 +344,7 @@ Here's an example custom guard to handle email confirmation:
|
|
333
344
|
|
334
345
|
```ruby
|
335
346
|
Clearance.configure do |config|
|
336
|
-
config.sign_in_guards = [EmailConfirmationGuard]
|
347
|
+
config.sign_in_guards = ["EmailConfirmationGuard"]
|
337
348
|
end
|
338
349
|
```
|
339
350
|
|
data/RELEASING.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Releasing
|
2
|
+
|
3
|
+
1. Update version file accordingly.
|
4
|
+
1. Run `bundle install` to update Gemfile.lock
|
5
|
+
1. Update `NEWS.md` to reflect the changes since last release.
|
6
|
+
1. Commit changes.
|
7
|
+
There shouldn't be code changes,
|
8
|
+
and thus CI doesn't need to run,
|
9
|
+
you can then add "[ci skip]" to the commit message.
|
10
|
+
1. Push the new commit
|
11
|
+
1. Tag the release: `git tag -s vVERSION`
|
12
|
+
- We recommend the [_quick guide on how to sign a commit_] from GitHub.
|
13
|
+
1. Push changes: `git push --tags`
|
14
|
+
1. Build and publish:
|
15
|
+
```bash
|
16
|
+
gem build clearance.gemspec
|
17
|
+
gem push clearance-*.gem
|
18
|
+
```
|
19
|
+
1. Add a new GitHub release using the recent `NEWS.md` as the content. Sample
|
20
|
+
URL: https://github.com/thoughtbot/clearance/releases/new?tag=vVERSION
|
21
|
+
1. Announce the new release,
|
22
|
+
making sure to say "thank you" to the contributors
|
23
|
+
who helped shape this version!
|
24
|
+
|
25
|
+
[_quick guide on how to sign a commit_]: https://docs.github.com/en/github/authenticating-to-github/signing-commits
|
data/Rakefile
CHANGED
@@ -22,5 +22,10 @@ RSpec::Core::RakeTask.new("spec:acceptance") do |task|
|
|
22
22
|
task.verbose = false
|
23
23
|
end
|
24
24
|
|
25
|
+
desc "Lint ERB templates"
|
26
|
+
task :erb_lint do
|
27
|
+
sh("bundle", "exec", "erblint", "app/views/**/*.erb")
|
28
|
+
end
|
29
|
+
|
25
30
|
desc "Run the specs and acceptance tests"
|
26
|
-
task default: %w(spec spec:acceptance)
|
31
|
+
task default: %w(spec spec:acceptance erb_lint)
|
@@ -45,8 +45,7 @@ class Clearance::PasswordsController < Clearance::BaseController
|
|
45
45
|
private
|
46
46
|
|
47
47
|
def deliver_email(user)
|
48
|
-
|
49
|
-
mail.deliver_later
|
48
|
+
::ClearanceMailer.change_password(user).deliver_now
|
50
49
|
end
|
51
50
|
|
52
51
|
def password_from_password_reset_params
|
@@ -58,7 +57,7 @@ class Clearance::PasswordsController < Clearance::BaseController
|
|
58
57
|
token = params[:token] || session[:password_reset_token]
|
59
58
|
|
60
59
|
Clearance.configuration.user_model.
|
61
|
-
|
60
|
+
find_by(id: params[user_param], confirmation_token: token.to_s)
|
62
61
|
end
|
63
62
|
|
64
63
|
def email_from_password_params
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
<p>
|
4
4
|
<%= link_to t(".link_text", default: "Change my password"),
|
5
|
-
|
5
|
+
url_for([@user, :password, action: :edit, token: @user.confirmation_token]) %>
|
6
6
|
</p>
|
7
7
|
|
8
|
-
<p><%=
|
8
|
+
<p><%= t(".closing") %></p>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<p><%= t(".description") %></p>
|
5
5
|
|
6
6
|
<%= form_for :password_reset,
|
7
|
-
url:
|
7
|
+
url: [@user, :password, token: @user.confirmation_token],
|
8
8
|
html: { method: :put } do |form| %>
|
9
9
|
<div class="password-field">
|
10
10
|
<%= form.label :password %>
|
data/clearance.gemspec
CHANGED
@@ -3,6 +3,7 @@ require 'clearance/version'
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.add_dependency 'bcrypt', '>= 3.1.1'
|
6
|
+
s.add_dependency 'argon2', '~> 2.0', '>= 2.0.2'
|
6
7
|
s.add_dependency 'email_validator', '~> 2.0'
|
7
8
|
s.add_dependency 'railties', '>= 5.0'
|
8
9
|
s.add_dependency 'activemodel', '>= 5.0'
|
data/gemfiles/rails_5.0.gemfile
CHANGED
@@ -2,19 +2,20 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "addressable"
|
5
|
+
gem "addressable"
|
6
6
|
gem "ammeter"
|
7
7
|
gem "appraisal"
|
8
|
-
gem "capybara", ">= 2.6.2"
|
9
|
-
gem "database_cleaner"
|
10
|
-
gem "
|
11
|
-
gem "
|
8
|
+
gem "capybara", ">= 2.6.2", "< 3.33.0"
|
9
|
+
gem "database_cleaner"
|
10
|
+
gem "erb_lint", require: false
|
11
|
+
gem "factory_bot_rails"
|
12
|
+
gem "nokogiri"
|
12
13
|
gem "pry", require: false
|
13
|
-
gem "shoulda-matchers", "~> 4.1"
|
14
|
-
gem "timecop", "~> 0.6"
|
15
|
-
gem "railties", "~> 5.0.0"
|
16
14
|
gem "rails-controller-testing"
|
17
|
-
gem "sqlite3", "~> 1.3.13"
|
18
15
|
gem "rspec-rails", "~> 3.1"
|
16
|
+
gem "shoulda-matchers"
|
17
|
+
gem "sqlite3", "~> 1.3.13"
|
18
|
+
gem "timecop"
|
19
|
+
gem "railties", "~> 5.0"
|
19
20
|
|
20
21
|
gemspec path: "../"
|
data/gemfiles/rails_5.1.gemfile
CHANGED
@@ -2,19 +2,20 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "addressable"
|
5
|
+
gem "addressable"
|
6
6
|
gem "ammeter"
|
7
7
|
gem "appraisal"
|
8
|
-
gem "capybara"
|
9
|
-
gem "database_cleaner"
|
10
|
-
gem "
|
11
|
-
gem "
|
8
|
+
gem "capybara"
|
9
|
+
gem "database_cleaner"
|
10
|
+
gem "erb_lint", require: false
|
11
|
+
gem "factory_bot_rails"
|
12
|
+
gem "nokogiri"
|
12
13
|
gem "pry", require: false
|
13
|
-
gem "shoulda-matchers", "~> 4.1"
|
14
|
-
gem "timecop", "~> 0.6"
|
15
|
-
gem "railties", "~> 5.1.0"
|
16
14
|
gem "rails-controller-testing"
|
17
|
-
gem "
|
18
|
-
gem "
|
15
|
+
gem "rspec-rails"
|
16
|
+
gem "shoulda-matchers"
|
17
|
+
gem "sqlite3"
|
18
|
+
gem "timecop"
|
19
|
+
gem "railties", "~> 5.1"
|
19
20
|
|
20
21
|
gemspec path: "../"
|
data/gemfiles/rails_5.2.gemfile
CHANGED
@@ -2,19 +2,20 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "addressable"
|
5
|
+
gem "addressable"
|
6
6
|
gem "ammeter"
|
7
7
|
gem "appraisal"
|
8
|
-
gem "capybara"
|
9
|
-
gem "database_cleaner"
|
10
|
-
gem "
|
11
|
-
gem "
|
8
|
+
gem "capybara"
|
9
|
+
gem "database_cleaner"
|
10
|
+
gem "erb_lint", require: false
|
11
|
+
gem "factory_bot_rails"
|
12
|
+
gem "nokogiri"
|
12
13
|
gem "pry", require: false
|
13
|
-
gem "shoulda-matchers", "~> 4.1"
|
14
|
-
gem "timecop", "~> 0.6"
|
15
|
-
gem "railties", "~> 5.2.0"
|
16
14
|
gem "rails-controller-testing"
|
17
|
-
gem "
|
18
|
-
gem "
|
15
|
+
gem "rspec-rails"
|
16
|
+
gem "shoulda-matchers"
|
17
|
+
gem "sqlite3"
|
18
|
+
gem "timecop"
|
19
|
+
gem "railties", "~> 5.2"
|
19
20
|
|
20
21
|
gemspec path: "../"
|
data/gemfiles/rails_6.0.gemfile
CHANGED
@@ -2,19 +2,20 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "addressable"
|
5
|
+
gem "addressable"
|
6
6
|
gem "ammeter"
|
7
7
|
gem "appraisal"
|
8
|
-
gem "capybara"
|
9
|
-
gem "database_cleaner"
|
10
|
-
gem "
|
11
|
-
gem "
|
8
|
+
gem "capybara"
|
9
|
+
gem "database_cleaner"
|
10
|
+
gem "erb_lint", require: false
|
11
|
+
gem "factory_bot_rails"
|
12
|
+
gem "nokogiri"
|
12
13
|
gem "pry", require: false
|
13
|
-
gem "shoulda-matchers", "~> 4.1"
|
14
|
-
gem "timecop", "~> 0.6"
|
15
|
-
gem "railties", "~> 6.0.0"
|
16
14
|
gem "rails-controller-testing"
|
17
|
-
gem "rspec-rails"
|
18
|
-
gem "
|
15
|
+
gem "rspec-rails"
|
16
|
+
gem "shoulda-matchers"
|
17
|
+
gem "sqlite3"
|
18
|
+
gem "timecop"
|
19
|
+
gem "railties", "~> 6.0"
|
19
20
|
|
20
21
|
gemspec path: "../"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "addressable"
|
6
|
+
gem "ammeter"
|
7
|
+
gem "appraisal"
|
8
|
+
gem "capybara"
|
9
|
+
gem "database_cleaner"
|
10
|
+
gem "erb_lint", require: false
|
11
|
+
gem "factory_bot_rails"
|
12
|
+
gem "nokogiri"
|
13
|
+
gem "pry", require: false
|
14
|
+
gem "rails-controller-testing"
|
15
|
+
gem "rspec-rails"
|
16
|
+
gem "shoulda-matchers"
|
17
|
+
gem "sqlite3"
|
18
|
+
gem "timecop"
|
19
|
+
gem "railties", "~> 6.1"
|
20
|
+
|
21
|
+
gemspec path: "../"
|
data/lib/clearance/back_door.rb
CHANGED
@@ -49,7 +49,8 @@ module Clearance
|
|
49
49
|
# @api private
|
50
50
|
def sign_in_through_the_back_door(env)
|
51
51
|
params = Rack::Utils.parse_query(env["QUERY_STRING"])
|
52
|
-
user_param = params
|
52
|
+
user_param = params.delete("as")
|
53
|
+
env["QUERY_STRING"] = Rack::Utils.build_query(params)
|
53
54
|
|
54
55
|
if user_param.present?
|
55
56
|
user = find_user(user_param)
|
@@ -88,6 +88,14 @@ module Clearance
|
|
88
88
|
# @return [Boolean]
|
89
89
|
attr_accessor :secure_cookie
|
90
90
|
|
91
|
+
# Controls whether cookies are signed.
|
92
|
+
# Defaults to `false` for backwards compatibility.
|
93
|
+
# When set, uses Rails' signed cookies
|
94
|
+
# (more secure against timing/brute-force attacks)
|
95
|
+
# see [ActionDispatch::Cookies](https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html)
|
96
|
+
# @return [Boolean|:migrate]
|
97
|
+
attr_reader :signed_cookie
|
98
|
+
|
91
99
|
# The array of sign in guards to run when signing a user in.
|
92
100
|
# Defaults to an empty array. Sign in guards respond to `call` and are
|
93
101
|
# initialized with a session and the current stack. Each guard can decide
|
@@ -124,9 +132,20 @@ module Clearance
|
|
124
132
|
@rotate_csrf_on_sign_in = true
|
125
133
|
@routes = true
|
126
134
|
@secure_cookie = false
|
135
|
+
@signed_cookie = false
|
127
136
|
@sign_in_guards = []
|
128
137
|
end
|
129
138
|
|
139
|
+
def signed_cookie=(value)
|
140
|
+
if [true, false, :migrate].include? value
|
141
|
+
@signed_cookie = value
|
142
|
+
else
|
143
|
+
raise "Clearance's signed_cookie configuration value is invalid. " \
|
144
|
+
"Valid values are true, false, or :migrate. " \
|
145
|
+
"Set this option via Clearance.configure in an initializer"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
130
149
|
# The class representing the configured user model.
|
131
150
|
# In the default configuration, this is the `User` class.
|
132
151
|
# @return [Class]
|
@@ -13,10 +13,7 @@ module Clearance
|
|
13
13
|
# `password=(new_password)`. For an example of how to implement these methods,
|
14
14
|
# see {Clearance::PasswordStrategies::BCrypt}.
|
15
15
|
module PasswordStrategies
|
16
|
-
autoload :BCrypt,
|
17
|
-
autoload :
|
18
|
-
'clearance/password_strategies/bcrypt_migration_from_sha1'
|
19
|
-
autoload :Blowfish, 'clearance/password_strategies/blowfish'
|
20
|
-
autoload :SHA1, 'clearance/password_strategies/sha1'
|
16
|
+
autoload :BCrypt, "clearance/password_strategies/bcrypt"
|
17
|
+
autoload :Argon2, "clearance/password_strategies/argon2"
|
21
18
|
end
|
22
19
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Clearance
|
2
|
+
module PasswordStrategies
|
3
|
+
# Uses Argon2 to authenticate users and store encrypted passwords.
|
4
|
+
|
5
|
+
module Argon2
|
6
|
+
require "argon2"
|
7
|
+
|
8
|
+
def authenticated?(password)
|
9
|
+
if encrypted_password.present?
|
10
|
+
::Argon2::Password.verify_password(password, encrypted_password)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def password=(new_password)
|
15
|
+
@password = new_password
|
16
|
+
|
17
|
+
if new_password.present?
|
18
|
+
self.encrypted_password = ::Argon2::Password.new.create(new_password)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/clearance/session.rb
CHANGED
@@ -14,15 +14,9 @@ module Clearance
|
|
14
14
|
# Called by {RackSession} to add the Clearance session cookie to a response.
|
15
15
|
#
|
16
16
|
# @return [void]
|
17
|
-
def add_cookie_to_headers
|
17
|
+
def add_cookie_to_headers
|
18
18
|
if signed_in_with_remember_token?
|
19
|
-
|
20
|
-
headers,
|
21
|
-
remember_token_cookie,
|
22
|
-
cookie_options.merge(
|
23
|
-
value: current_user.remember_token,
|
24
|
-
),
|
25
|
-
)
|
19
|
+
set_remember_token(current_user.remember_token)
|
26
20
|
end
|
27
21
|
end
|
28
22
|
|
@@ -112,9 +106,27 @@ module Clearance
|
|
112
106
|
@cookies ||= ActionDispatch::Request.new(@env).cookie_jar
|
113
107
|
end
|
114
108
|
|
109
|
+
# @api private
|
110
|
+
def set_remember_token(token)
|
111
|
+
case Clearance.configuration.signed_cookie
|
112
|
+
when true, :migrate
|
113
|
+
cookies.signed[remember_token_cookie] = cookie_options(token)
|
114
|
+
when false
|
115
|
+
cookies[remember_token_cookie] = cookie_options(token)
|
116
|
+
end
|
117
|
+
remember_token
|
118
|
+
end
|
119
|
+
|
115
120
|
# @api private
|
116
121
|
def remember_token
|
117
|
-
|
122
|
+
case Clearance.configuration.signed_cookie
|
123
|
+
when true
|
124
|
+
cookies.signed[remember_token_cookie]
|
125
|
+
when :migrate
|
126
|
+
cookies.signed[remember_token_cookie] || cookies[remember_token_cookie]
|
127
|
+
when false
|
128
|
+
cookies[remember_token_cookie]
|
129
|
+
end
|
118
130
|
end
|
119
131
|
|
120
132
|
# @api private
|
@@ -159,7 +171,7 @@ module Clearance
|
|
159
171
|
end
|
160
172
|
|
161
173
|
# @api private
|
162
|
-
def cookie_options
|
174
|
+
def cookie_options(value)
|
163
175
|
{
|
164
176
|
domain: domain,
|
165
177
|
expires: remember_token_expires,
|
@@ -167,7 +179,7 @@ module Clearance
|
|
167
179
|
same_site: Clearance.configuration.same_site,
|
168
180
|
path: Clearance.configuration.cookie_path,
|
169
181
|
secure: Clearance.configuration.secure_cookie,
|
170
|
-
value:
|
182
|
+
value: value,
|
171
183
|
}
|
172
184
|
end
|
173
185
|
|
@@ -175,7 +187,7 @@ module Clearance
|
|
175
187
|
def delete_cookie_options
|
176
188
|
Hash.new.tap do |options|
|
177
189
|
if configured_cookie_domain
|
178
|
-
options[:domain] =
|
190
|
+
options[:domain] = domain
|
179
191
|
end
|
180
192
|
end
|
181
193
|
end
|