quo_vadis 2.1.6 → 2.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.
|