quo_vadis 2.1.6 → 2.1.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -1
- data/README.md +9 -11
- data/app/controllers/quo_vadis/confirmations_controller.rb +1 -1
- data/app/views/quo_vadis/logs/index.html.erb +1 -1
- data/lib/quo_vadis/controller.rb +7 -0
- data/lib/quo_vadis/crypt.rb +12 -6
- data/lib/quo_vadis/model.rb +7 -3
- data/lib/quo_vadis/version.rb +1 -1
- data/test/dummy/app/controllers/sign_ups_controller.rb +5 -0
- data/test/dummy/config/routes.rb +1 -0
- data/test/integration/account_confirmation_test.rb +4 -0
- data/test/integration/sessions_test.rb +1 -1
- data/test/models/crypt_test.rb +19 -0
- data/test/models/model_test.rb +10 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3776f82ebb4987586131a44692de5891ec821e7e82a1de249fc3d8905ef551d8
|
4
|
+
data.tar.gz: 6914f2e05d50ffbd9451d6e50484674d7bb775d2d871922749cc4be1553e35a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5229c567fe09a76fad025b0dea0fc36192defd24e04077374816cfb99414b6a9cc3213e8410f5ee23fedb297d1b157d64345c317d646d1226655c5e60e50c1ac
|
7
|
+
data.tar.gz: 0ace7c8c5c1075eec8a54aa31ba01564a8a9ff0fec23387650a00a1721affca7c9326e083ac9f26a8afdd04bee6f6e8a748ae1c9bbf5b4e9d3b005804362afe6
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,23 @@
|
|
4
4
|
## HEAD
|
5
5
|
|
6
6
|
|
7
|
+
## 2.1.9 (13 September 2022)
|
8
|
+
|
9
|
+
* Enable code to be run after sign up.
|
10
|
+
|
11
|
+
|
12
|
+
## 2.1.8 (18 June 2022)
|
13
|
+
|
14
|
+
* Extract convenience method for has authentication account.
|
15
|
+
* Only authenticating models react to email change.
|
16
|
+
|
17
|
+
|
18
|
+
## 2.1.7 (30 May 2022)
|
19
|
+
|
20
|
+
* Use SHA256 digest for encryption.
|
21
|
+
* Use <time> element in logs view.
|
22
|
+
|
23
|
+
|
7
24
|
## 2.1.6 (30 May 2022)
|
8
25
|
|
9
26
|
* Fix typo in session scope.
|
@@ -23,7 +40,7 @@
|
|
23
40
|
|
24
41
|
## 2.1.3 (30 September 2021)
|
25
42
|
|
26
|
-
* Pass IP and timestamp as
|
43
|
+
* Pass IP and timestamp as parameters to mailer.
|
27
44
|
|
28
45
|
|
29
46
|
## 2.1.2 (30 September 2021)
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Quo Vadis
|
2
2
|
|
3
|
-
Multifactor authentication for your Rails 6 app.
|
3
|
+
Multifactor authentication for your Rails 6 or Rails 7 app.
|
4
4
|
|
5
5
|
Designed in accordance with the [OWASP Application Security Verification Standard](https://owasp.org/www-project-application-security-verification-standard/) and relevant [OWASP Cheatsheets](https://cheatsheetseries.owasp.org).
|
6
6
|
|
@@ -177,7 +177,7 @@ Your new user sign-up form ([example](https://github.com/airblade/quo_vadis/blob
|
|
177
177
|
|
178
178
|
In your controller, use the `#login` method to log in your new user. The optional second argument sets the length of the session (defaults to the browser session) - see the description above in the Controllers section).
|
179
179
|
|
180
|
-
After logging in the user, redirect them
|
180
|
+
After logging in the user, redirect them wherever you like. You can use `qv.path_after_signup` which resolves to the first of these routes that exists: `:after_signup`, `:after_login`, the root route.
|
181
181
|
|
182
182
|
```ruby
|
183
183
|
class UsersController < ApplicationController
|
@@ -185,7 +185,7 @@ class UsersController < ApplicationController
|
|
185
185
|
@user = User.new user_params
|
186
186
|
if @user.save
|
187
187
|
login @user
|
188
|
-
redirect_to qv.
|
188
|
+
redirect_to qv.path_after_signup
|
189
189
|
else
|
190
190
|
# ...
|
191
191
|
end
|
@@ -258,10 +258,7 @@ Next, write the page where the user can amend their email address if they made a
|
|
258
258
|
|
259
259
|
Finally, write the page where people can put in their identifier (not their email, unless the identifier is email) again to request another confirmation email ([example](https://github.com/airblade/quo_vadis/blob/master/app/views/quo_vadis/confirmations/new.html.erb)). It must be in `app/views/quo_vadis/confirmations/new.html.:format`.
|
260
260
|
|
261
|
-
After the user has confirmed their account, they will be logged in and redirected to the first of these that exists:
|
262
|
-
|
263
|
-
- a route named `:after_login`;
|
264
|
-
- your root route.
|
261
|
+
After the user has confirmed their account, they will be logged in and redirected to `qv.path_after_signup` which resolves to the first of these routes that exists: `:after_signup`, `:after_login`, the root route.
|
265
262
|
|
266
263
|
So add whichever works best for you.
|
267
264
|
|
@@ -518,12 +515,13 @@ end
|
|
518
515
|
|
519
516
|
__Routes__
|
520
517
|
|
521
|
-
You can set up your post-authentication and post-password-change routes. If you don't, you must have a root route. For example:
|
518
|
+
You can set up your post-signup, post-authentication, and post-password-change routes. If you don't, you must have a root route. For example:
|
522
519
|
|
523
520
|
```ruby
|
524
521
|
# config/routes.rb
|
525
|
-
get '/
|
526
|
-
get '/
|
522
|
+
get '/signups/confirmed', to: 'dashboards#show', as: 'after_signup'
|
523
|
+
get '/dashboard', to: 'dashboards#show', as: 'after_login'
|
524
|
+
get '/profile', to: 'profiles#show', as: 'after_password_change'
|
527
525
|
```
|
528
526
|
|
529
527
|
### I18n
|
@@ -537,6 +535,6 @@ If you don't want a specific flash message at all, give the key an empty value i
|
|
537
535
|
|
538
536
|
## Intellectual Property
|
539
537
|
|
540
|
-
Copyright 2011-
|
538
|
+
Copyright 2011-2022 Andrew Stewart (boss@airbladesoftware.com).
|
541
539
|
|
542
540
|
Released under the MIT licence.
|
@@ -51,7 +51,7 @@ module QuoVadis
|
|
51
51
|
session.delete :account_pending_confirmation
|
52
52
|
|
53
53
|
login account.model, true
|
54
|
-
redirect_to qv.
|
54
|
+
redirect_to qv.path_after_signup, notice: QuoVadis.translate('flash.confirmation.confirmed')
|
55
55
|
end
|
56
56
|
|
57
57
|
|
@@ -12,7 +12,7 @@
|
|
12
12
|
<tbody>
|
13
13
|
<% @logs.each do |log| %>
|
14
14
|
<tr>
|
15
|
-
<td><%= log.created_at %></td>
|
15
|
+
<td><time datetime="<%= log.created_at.to_formatted_s(:iso8601) %>"><%= log.created_at.to_formatted_s('%-d %B %Y') %></time></td>
|
16
16
|
<td><%= QuoVadis.translate "log.action.#{log.action}" %></td>
|
17
17
|
<td><%= log.ip %></td>
|
18
18
|
<td><%= log.metadata.empty? ? '' : log.metadata.map {|k,v| "#{k}: #{v}"}.join(', ') %></td>
|
data/lib/quo_vadis/controller.rb
CHANGED
@@ -192,6 +192,13 @@ module QuoVadis
|
|
192
192
|
Log.create account: account, action: action, ip: request.remote_ip, metadata: metadata
|
193
193
|
end
|
194
194
|
|
195
|
+
def path_after_signup
|
196
|
+
return main_app.after_signup_path if main_app.respond_to?(:after_signup_path)
|
197
|
+
return main_app.after_login_path if main_app.respond_to?(:after_login_path)
|
198
|
+
return main_app.root_path if main_app.respond_to?(:root_path)
|
199
|
+
raise RuntimeError, 'Missing routes: after_signup_path, after_login_path, root_path; define at least one of them.'
|
200
|
+
end
|
201
|
+
|
195
202
|
def path_after_authentication
|
196
203
|
if (bookmark = rails_session[:qv_bookmark])
|
197
204
|
rails_session.delete :qv_bookmark
|
data/lib/quo_vadis/crypt.rb
CHANGED
@@ -8,7 +8,7 @@ module QuoVadis
|
|
8
8
|
return '' if value == ''
|
9
9
|
|
10
10
|
salt = SecureRandom.hex KEY_LENGTH
|
11
|
-
crypt = encryptor
|
11
|
+
crypt = encryptor salt
|
12
12
|
ciphertext = crypt.encrypt_and_sign value
|
13
13
|
[salt, ciphertext].join SEPARATOR
|
14
14
|
end
|
@@ -18,7 +18,7 @@ module QuoVadis
|
|
18
18
|
return '' if value == ''
|
19
19
|
|
20
20
|
salt, data = value.split SEPARATOR
|
21
|
-
crypt = encryptor
|
21
|
+
crypt = encryptor salt
|
22
22
|
crypt.decrypt_and_verify(data)
|
23
23
|
end
|
24
24
|
|
@@ -27,12 +27,18 @@ module QuoVadis
|
|
27
27
|
KEY_LENGTH = ActiveSupport::MessageEncryptor.key_len
|
28
28
|
SEPARATOR = '$$'
|
29
29
|
|
30
|
-
def self.encryptor(
|
31
|
-
|
30
|
+
def self.encryptor(salt)
|
31
|
+
key_sha256 = key salt, OpenSSL::Digest::SHA256
|
32
|
+
key_sha1 = key salt, OpenSSL::Digest::SHA1
|
33
|
+
ActiveSupport::MessageEncryptor.new(key_sha256).tap { |crypt|
|
34
|
+
crypt.rotate key_sha1
|
35
|
+
}
|
32
36
|
end
|
33
37
|
|
34
|
-
def self.key(salt)
|
35
|
-
ActiveSupport::KeyGenerator
|
38
|
+
def self.key(salt, hash_digest_class)
|
39
|
+
ActiveSupport::KeyGenerator
|
40
|
+
.new(secret, hash_digest_class: hash_digest_class)
|
41
|
+
.generate_key(salt, KEY_LENGTH)
|
36
42
|
end
|
37
43
|
|
38
44
|
def self.secret
|
data/lib/quo_vadis/model.rb
CHANGED
@@ -14,7 +14,7 @@ module QuoVadis
|
|
14
14
|
|
15
15
|
has_one :qv_account, as: :model, class_name: 'QuoVadis::Account', dependent: :destroy, autosave: true
|
16
16
|
|
17
|
-
before_validation :qv_copy_identifier_to_account, if:
|
17
|
+
before_validation :qv_copy_identifier_to_account, if: :has_authentication_account?
|
18
18
|
|
19
19
|
validate :qv_copy_password_errors, if: Proc.new { |m| m.qv_account&.password }
|
20
20
|
|
@@ -29,8 +29,8 @@ module QuoVadis
|
|
29
29
|
qv_account.identifier = send identifier
|
30
30
|
end
|
31
31
|
|
32
|
-
after_update :qv_log_email_change,
|
33
|
-
after_update :qv_notify_email_change, if: :saved_change_to_email?
|
32
|
+
after_update :qv_log_email_change, if: [:has_authentication_account?, :saved_change_to_email?]
|
33
|
+
after_update :qv_notify_email_change, if: [:has_authentication_account?, :saved_change_to_email?]
|
34
34
|
|
35
35
|
QuoVadis.register_model self.name, identifier
|
36
36
|
end
|
@@ -57,6 +57,10 @@ module QuoVadis
|
|
57
57
|
qv_account.revoke
|
58
58
|
end
|
59
59
|
|
60
|
+
def has_authentication_account?
|
61
|
+
!!qv_account
|
62
|
+
end
|
63
|
+
|
60
64
|
private
|
61
65
|
|
62
66
|
def qv_copy_password_errors
|
data/lib/quo_vadis/version.rb
CHANGED
data/test/dummy/config/routes.rb
CHANGED
@@ -33,6 +33,8 @@ class AccountConfirmationTest < IntegrationTest
|
|
33
33
|
put action_path
|
34
34
|
|
35
35
|
# verify logged in
|
36
|
+
assert_redirected_to '/sign_ups/confirmed'
|
37
|
+
follow_redirect!
|
36
38
|
assert_redirected_to '/articles/secret'
|
37
39
|
assert_equal 'Thanks for confirming your account. You are now logged in.', flash[:notice]
|
38
40
|
assert controller.logged_in?
|
@@ -101,6 +103,8 @@ class AccountConfirmationTest < IntegrationTest
|
|
101
103
|
|
102
104
|
put extract_path_from_email
|
103
105
|
|
106
|
+
assert_redirected_to '/sign_ups/confirmed'
|
107
|
+
follow_redirect!
|
104
108
|
assert_redirected_to '/articles/secret'
|
105
109
|
assert_equal 'Thanks for confirming your account. You are now logged in.', flash[:notice]
|
106
110
|
|
@@ -54,7 +54,7 @@ class SessionsTest < IntegrationTest
|
|
54
54
|
phone.get quo_vadis.sessions_path
|
55
55
|
phone.assert_response :success
|
56
56
|
phone.assert_select 'td', 'This session'
|
57
|
-
phone.assert_select 'td
|
57
|
+
phone.assert_select 'td button[type=submit]', text: 'Log out', count: 1
|
58
58
|
|
59
59
|
# on phone, log out the desktop session
|
60
60
|
phone.delete quo_vadis.session_path(QuoVadis::Session.first.id)
|
data/test/models/crypt_test.rb
CHANGED
@@ -4,6 +4,13 @@ class CryptTest < ActiveSupport::TestCase
|
|
4
4
|
|
5
5
|
setup do
|
6
6
|
@crypt = QuoVadis::Crypt
|
7
|
+
|
8
|
+
@crypt_sha1 = Class.new(QuoVadis::Crypt) do
|
9
|
+
def self.encryptor(salt)
|
10
|
+
key_sha1 = key salt, OpenSSL::Digest::SHA1
|
11
|
+
ActiveSupport::MessageEncryptor.new key_sha1
|
12
|
+
end
|
13
|
+
end
|
7
14
|
end
|
8
15
|
|
9
16
|
test 'round trip' do
|
@@ -19,4 +26,16 @@ class CryptTest < ActiveSupport::TestCase
|
|
19
26
|
refute_equal ciphertext, @crypt.encrypt(plaintext)
|
20
27
|
end
|
21
28
|
|
29
|
+
test 'rotation' do
|
30
|
+
# This test only works if our test Rails contains this commit:
|
31
|
+
# https://github.com/rails/rails/commit/447e28347eb46e2ad5dc625de616152bd1b69a32
|
32
|
+
return unless ActiveSupport::KeyGenerator.respond_to? :hash_digest_class
|
33
|
+
|
34
|
+
plaintext = 'the quick brown fox'
|
35
|
+
# Encrypt with SHA1 digest
|
36
|
+
ciphertext_sha1 = @crypt_sha1.encrypt plaintext
|
37
|
+
# Ensure code can decrypt it.
|
38
|
+
assert_equal plaintext, @crypt.decrypt(ciphertext_sha1)
|
39
|
+
end
|
40
|
+
|
22
41
|
end
|
data/test/models/model_test.rb
CHANGED
@@ -66,4 +66,14 @@ class ModelTest < ActiveSupport::TestCase
|
|
66
66
|
u.update email: 'robert@example.com'
|
67
67
|
end
|
68
68
|
end
|
69
|
+
|
70
|
+
|
71
|
+
test 'does not notify or log on email change when no account' do
|
72
|
+
u = User.create! name: 'bob', email: 'bob@example.com'
|
73
|
+
assert_no_difference 'QuoVadis::Log.count' do
|
74
|
+
assert_no_enqueued_emails do
|
75
|
+
u.update email: 'robert@example.com'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
69
79
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quo_vadis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Stewart
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -224,7 +224,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
224
224
|
- !ruby/object:Gem::Version
|
225
225
|
version: '0'
|
226
226
|
requirements: []
|
227
|
-
rubygems_version: 3.2.
|
227
|
+
rubygems_version: 3.2.33
|
228
228
|
signing_key:
|
229
229
|
specification_version: 4
|
230
230
|
summary: Multifactor authentication for Rails 6 and 7.
|