booth 0.0.3 → 0.0.4
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 +3 -0
- data/app/assets/images/booth/fido/passkey_mark_b_reverse.svg +55 -0
- data/config/locales/de.yml +5 -5
- data/lib/booth/core/sessions/to_passport.rb +11 -1
- data/lib/booth/core/webauth/authentication_verification.rb +5 -5
- data/lib/booth/models/authenticator.rb +11 -0
- data/lib/booth/passport.rb +15 -0
- data/lib/booth/requests/authentication.rb +1 -1
- data/lib/booth/requests/storages/login.rb +4 -1
- data/lib/booth/requests/sudo.rb +12 -2
- data/lib/booth/test.rb +10 -17
- data/lib/booth/testing/incorporation_test_case.rb +3 -1
- data/lib/booth/testing/shortcuts.rb +8 -17
- data/lib/booth/testing/support/assert_logged_in.rb +26 -27
- data/lib/booth/testing/support/assert_logged_out.rb +15 -11
- data/lib/booth/testing/support/assert_partial.rb +20 -29
- data/lib/booth/testing/support/capybara_step_logger.rb +22 -0
- data/lib/booth/testing/support/{soft_reset_session.rb → clear_cookies.rb} +5 -6
- data/lib/booth/testing/support/cookie_data_from_browser.rb +38 -0
- data/lib/booth/testing/support/shortcuts/create_and_onboard.rb +4 -0
- data/lib/booth/testing/support/shortcuts/login_with_passkey.rb +6 -4
- data/lib/booth/testing/support/shortcuts/register_new_passkey.rb +6 -2
- data/lib/booth/testing/support/virtual_authenticator.rb +196 -0
- data/lib/booth/testing/support/virtual_authenticators/create.rb +22 -7
- data/lib/booth/testing/support/virtual_authenticators/destroy.rb +14 -9
- data/lib/booth/testing/support/virtual_authenticators/enable.rb +14 -2
- data/lib/booth/testing/support/virtual_authenticators/load.rb +7 -19
- data/lib/booth/testing/support/virtual_authenticators.rb +106 -0
- data/lib/booth/testing/support/visit.rb +1 -0
- data/lib/booth/testing/userland/login_remotely.rb +2 -2
- data/lib/booth/testing/userland/onboarding_first_time.rb +5 -4
- data/lib/booth/testing/userland/onboarding_to_reset_passkeys.rb +3 -3
- data/lib/booth/testing/userland/registration_with_passkey.rb +9 -6
- data/lib/booth/testing/userland/registration_without_passkey.rb +11 -7
- data/lib/booth/testing/userland/sessions_manage_behavior.rb +14 -3
- data/lib/booth/userland/webauths/index.rb +9 -3
- data/lib/booth/userland/webauths/new.rb +10 -2
- data/lib/booth/userland/webauths/transitions/create/choose_nickname.rb +1 -1
- data/lib/booth/userland/webauths/transitions/sudo/authentication_initiation.rb +5 -0
- data/lib/booth/userland/webauths/transitions/sudo/authentication_verification.rb +1 -1
- data/lib/booth/version.rb +1 -1
- metadata +8 -4
- data/lib/booth/testing/support/get_session_value.rb +0 -37
- data/lib/booth/testing/support/virtual_authenticators/manager.rb +0 -124
|
@@ -10,13 +10,19 @@ module Booth
|
|
|
10
10
|
before_test&.call
|
|
11
11
|
|
|
12
12
|
create_and_onboard(username: 'alice')
|
|
13
|
-
virtual_authenticators.create
|
|
13
|
+
latchkey = virtual_authenticators.create
|
|
14
14
|
register_new_passkey(username: 'alice')
|
|
15
15
|
|
|
16
|
+
# Capture credentials after registration
|
|
17
|
+
latchkey.pull
|
|
18
|
+
|
|
16
19
|
using_session(:"second_browser_#{button_name}") do
|
|
17
|
-
virtual_authenticators.
|
|
20
|
+
virtual_authenticators.import(latchkey)
|
|
18
21
|
login_with_passkey(username: 'alice')
|
|
19
22
|
|
|
23
|
+
# Capture updated sign count from second browser
|
|
24
|
+
latchkey.pull
|
|
25
|
+
|
|
20
26
|
visit_namespaced controller: :sessions, action: :index
|
|
21
27
|
|
|
22
28
|
assert_userland_view controller: :sessions, step: :index
|
|
@@ -30,13 +36,18 @@ module Booth
|
|
|
30
36
|
|
|
31
37
|
visit current_path
|
|
32
38
|
|
|
39
|
+
# Refresh to force session validation with server
|
|
40
|
+
page.refresh
|
|
41
|
+
|
|
33
42
|
# ----------- SIGNIFICANT TEST ---------------
|
|
34
43
|
# A revoked session is not logged in any more.
|
|
35
44
|
# --------------------------------------------
|
|
36
45
|
assert_logged_out
|
|
37
46
|
|
|
47
|
+
# Sync sign count from second browser to first browser
|
|
48
|
+
latchkey.push
|
|
49
|
+
|
|
38
50
|
# Login on first device again and revoke all others
|
|
39
|
-
virtual_authenticators.refresh_from_other_session
|
|
40
51
|
login_with_passkey(username: 'alice')
|
|
41
52
|
|
|
42
53
|
visit_namespaced controller: :sessions, action: :index
|
|
@@ -12,7 +12,7 @@ module Booth
|
|
|
12
12
|
request.must_be_html!
|
|
13
13
|
request.must_be_logged_in!
|
|
14
14
|
|
|
15
|
-
::Booth::Userland::Webauths::Guards::Sudo.call(request:, credential:) { return
|
|
15
|
+
::Booth::Userland::Webauths::Guards::Sudo.call(request:, credential:) { return it }
|
|
16
16
|
|
|
17
17
|
do_index
|
|
18
18
|
end
|
|
@@ -23,7 +23,8 @@ module Booth
|
|
|
23
23
|
request.storage.webauth.reset if params[:reset]
|
|
24
24
|
|
|
25
25
|
Tron.success :current_webauth, step: :index,
|
|
26
|
-
authenticators: authenticator_structs
|
|
26
|
+
authenticators: authenticator_structs,
|
|
27
|
+
authenticators?: authenticators?
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
def authenticator_structs
|
|
@@ -31,11 +32,16 @@ module Booth
|
|
|
31
32
|
::Booth::ToStruct.call(
|
|
32
33
|
authenticator.attributes
|
|
33
34
|
.symbolize_keys
|
|
34
|
-
.slice(:id, :confirmed_at, :nickname)
|
|
35
|
+
.slice(:id, :confirmed_at, :nickname, :aaguid)
|
|
36
|
+
.merge(authenticator.provider_attributes),
|
|
35
37
|
)
|
|
36
38
|
end
|
|
37
39
|
end
|
|
38
40
|
|
|
41
|
+
def authenticators?
|
|
42
|
+
credential.registered_authenticators?
|
|
43
|
+
end
|
|
44
|
+
|
|
39
45
|
def credential
|
|
40
46
|
@credential ||= ::Booth::Models::Credential.find(request.authentication.credential_id)
|
|
41
47
|
end
|
|
@@ -15,7 +15,8 @@ module Booth
|
|
|
15
15
|
if authenticator&.confirmed?
|
|
16
16
|
return Tron.success :completed, step: :completed,
|
|
17
17
|
nickname: authenticator&.nickname,
|
|
18
|
-
|
|
18
|
+
authenticators?: authenticators?,
|
|
19
|
+
should_add_more_authenticators?: should_add_more_authenticators?
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
::Booth::Userland::Webauths::Guards::Sudo.call(request:, credential:) { return it }
|
|
@@ -29,7 +30,10 @@ module Booth
|
|
|
29
30
|
log { "WebAuthn Authenticator registration in step #{step}" }
|
|
30
31
|
# log { authenticator.inspect }
|
|
31
32
|
|
|
32
|
-
Tron.success :add_webauth, step:,
|
|
33
|
+
Tron.success :add_webauth, step:,
|
|
34
|
+
nickname: authenticator&.nickname,
|
|
35
|
+
authenticators?: authenticators?,
|
|
36
|
+
should_add_more_authenticators?: should_add_more_authenticators?
|
|
33
37
|
end
|
|
34
38
|
|
|
35
39
|
def step
|
|
@@ -38,6 +42,10 @@ module Booth
|
|
|
38
42
|
authenticator.step
|
|
39
43
|
end
|
|
40
44
|
|
|
45
|
+
def authenticators?
|
|
46
|
+
credential.registered_authenticators?
|
|
47
|
+
end
|
|
48
|
+
|
|
41
49
|
def should_add_more_authenticators?
|
|
42
50
|
credential.authenticators.registered_scope.count < 2
|
|
43
51
|
end
|
|
@@ -29,7 +29,7 @@ module Booth
|
|
|
29
29
|
|
|
30
30
|
def do_update_authenticator
|
|
31
31
|
if authenticator.update nickname: nickname_param
|
|
32
|
-
log {
|
|
32
|
+
log { "The nickname successfully changed to #{authenticator.nickname}" }
|
|
33
33
|
Tron.success :nickname_saved
|
|
34
34
|
else
|
|
35
35
|
public_message = authenticator.errors.to_a.to_sentence
|
|
@@ -28,9 +28,14 @@ module Booth
|
|
|
28
28
|
|
|
29
29
|
request.sudo.webauthn_challenge = webauth.challenge
|
|
30
30
|
Tron.success :test_challenge_created, public_json: webauth.as_json,
|
|
31
|
+
authenticators?: authenticators?,
|
|
31
32
|
http_status: :created
|
|
32
33
|
end
|
|
33
34
|
|
|
35
|
+
def authenticators?
|
|
36
|
+
credential.registered_authenticators?
|
|
37
|
+
end
|
|
38
|
+
|
|
34
39
|
def credential
|
|
35
40
|
@credential ||= ::Booth::Models::Credential.find(request.authentication.credential_id)
|
|
36
41
|
end
|
data/lib/booth/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: booth
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- halo
|
|
@@ -187,6 +187,7 @@ files:
|
|
|
187
187
|
- app/assets/images/booth/fido/passkey_mark_a_reverse.svg
|
|
188
188
|
- app/assets/images/booth/fido/passkey_mark_a_white.svg
|
|
189
189
|
- app/assets/images/booth/fido/passkey_mark_b_black.svg
|
|
190
|
+
- app/assets/images/booth/fido/passkey_mark_b_reverse.svg
|
|
190
191
|
- app/assets/images/booth/platforms/README.md
|
|
191
192
|
- app/assets/images/booth/platforms/android.svg
|
|
192
193
|
- app/assets/images/booth/platforms/apple.svg
|
|
@@ -292,6 +293,7 @@ files:
|
|
|
292
293
|
- lib/booth/models/remotes/scopes/recently_responded.rb
|
|
293
294
|
- lib/booth/models/session.rb
|
|
294
295
|
- lib/booth/models/user_agent.rb
|
|
296
|
+
- lib/booth/passport.rb
|
|
295
297
|
- lib/booth/request.rb
|
|
296
298
|
- lib/booth/requests/agent.rb
|
|
297
299
|
- lib/booth/requests/authentication.rb
|
|
@@ -319,18 +321,20 @@ files:
|
|
|
319
321
|
- lib/booth/testing/support/assert_logged_in.rb
|
|
320
322
|
- lib/booth/testing/support/assert_logged_out.rb
|
|
321
323
|
- lib/booth/testing/support/assert_partial.rb
|
|
324
|
+
- lib/booth/testing/support/capybara_step_logger.rb
|
|
325
|
+
- lib/booth/testing/support/clear_cookies.rb
|
|
326
|
+
- lib/booth/testing/support/cookie_data_from_browser.rb
|
|
322
327
|
- lib/booth/testing/support/force_login.rb
|
|
323
|
-
- lib/booth/testing/support/get_session_value.rb
|
|
324
328
|
- lib/booth/testing/support/scenario.rb
|
|
325
329
|
- lib/booth/testing/support/shortcuts/create_and_onboard.rb
|
|
326
330
|
- lib/booth/testing/support/shortcuts/login_with_passkey.rb
|
|
327
331
|
- lib/booth/testing/support/shortcuts/register_new_passkey.rb
|
|
328
|
-
- lib/booth/testing/support/
|
|
332
|
+
- lib/booth/testing/support/virtual_authenticator.rb
|
|
333
|
+
- lib/booth/testing/support/virtual_authenticators.rb
|
|
329
334
|
- lib/booth/testing/support/virtual_authenticators/create.rb
|
|
330
335
|
- lib/booth/testing/support/virtual_authenticators/destroy.rb
|
|
331
336
|
- lib/booth/testing/support/virtual_authenticators/enable.rb
|
|
332
337
|
- lib/booth/testing/support/virtual_authenticators/load.rb
|
|
333
|
-
- lib/booth/testing/support/virtual_authenticators/manager.rb
|
|
334
338
|
- lib/booth/testing/support/visit.rb
|
|
335
339
|
- lib/booth/testing/userland.rb
|
|
336
340
|
- lib/booth/testing/userland/login_remotely.rb
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Booth
|
|
4
|
-
module Testing
|
|
5
|
-
module Support
|
|
6
|
-
class GetSessionValue
|
|
7
|
-
include ::Calls
|
|
8
|
-
include ::Capybara::DSL
|
|
9
|
-
include ::Booth::Logging
|
|
10
|
-
|
|
11
|
-
option :key
|
|
12
|
-
|
|
13
|
-
def call
|
|
14
|
-
::Capybara::Lockstep.synchronize
|
|
15
|
-
|
|
16
|
-
result = nil
|
|
17
|
-
in_new_window do
|
|
18
|
-
result = page.get_rack_session[key]
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
log { "session[#{key.inspect}] is: #{result.inspect}" }
|
|
22
|
-
|
|
23
|
-
::Capybara::Lockstep.synchronize
|
|
24
|
-
result
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
private
|
|
28
|
-
|
|
29
|
-
def in_new_window(&)
|
|
30
|
-
window = open_new_window
|
|
31
|
-
within_window(window, &)
|
|
32
|
-
window.close
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Booth
|
|
4
|
-
module Testing
|
|
5
|
-
module Support
|
|
6
|
-
module VirtualAuthenticators
|
|
7
|
-
# Capybara handles multiple separate Chrome sessions (like separate browsers).
|
|
8
|
-
# In Chrome each of those sessions is completely distinct and nows nothing about the others.
|
|
9
|
-
# To "simulate" having the *same* passkey in "two browsers", we need to clone and sync it.
|
|
10
|
-
# This means transferring the secret key, and synchronizing the sign count.
|
|
11
|
-
# This class holds a state of all browser sessions to easier facilitate transfer and sync.
|
|
12
|
-
class Manager
|
|
13
|
-
include ::Capybara::DSL
|
|
14
|
-
include ::Booth::Logging
|
|
15
|
-
|
|
16
|
-
# Creates a brand new passkey.
|
|
17
|
-
def create(has_user_verification: true)
|
|
18
|
-
::Booth::Testing::Support::VirtualAuthenticators::Enable.call
|
|
19
|
-
|
|
20
|
-
new_device = ::Booth::Testing::Support::VirtualAuthenticators::Create.call(
|
|
21
|
-
has_user_verification:,
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
this_session[new_device.instance_variable_get(:@id)] = new_device
|
|
25
|
-
|
|
26
|
-
nil
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Takes a passkey from (the) other browser and injects it into this browser.
|
|
30
|
-
def clone_from_other_session
|
|
31
|
-
new_device = ::Booth::Testing::Support::VirtualAuthenticators::Create.call(
|
|
32
|
-
has_user_verification: true,
|
|
33
|
-
)
|
|
34
|
-
new_device.add_credential(other_credential)
|
|
35
|
-
|
|
36
|
-
this_session[new_device.instance_variable_get(:@id)] = new_device
|
|
37
|
-
nil
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Inspects a passkey in (the) other browser and syncs the passkey here if needed.
|
|
41
|
-
def refresh_from_other_session
|
|
42
|
-
this_sign_count = this_credential.instance_variable_get(:@sign_count)
|
|
43
|
-
other_sign_count = other_credential.instance_variable_get(:@sign_count)
|
|
44
|
-
|
|
45
|
-
if this_sign_count == other_sign_count
|
|
46
|
-
log { "Sign count of both Virtual Keys is #{this_sign_count}" }
|
|
47
|
-
return
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
log { "Changing Virtual Key sign count from #{this_sign_count} to #{other_sign_count}" }
|
|
51
|
-
|
|
52
|
-
new_credential = this_credential
|
|
53
|
-
# This only affects the Credential here in our Ruby instance.
|
|
54
|
-
new_credential.instance_variable_set(:@sign_count, other_sign_count)
|
|
55
|
-
|
|
56
|
-
# So let us send our Ruby instance to the browser by replacing the key there.
|
|
57
|
-
this_device.remove_all_credentials
|
|
58
|
-
this_device.add_credential(new_credential)
|
|
59
|
-
|
|
60
|
-
nil
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
private
|
|
64
|
-
|
|
65
|
-
def this_credential
|
|
66
|
-
these_credentials = this_device.credentials
|
|
67
|
-
|
|
68
|
-
unless these_credentials.size == 1
|
|
69
|
-
raise "Don't know which other Virtual Credential to pick: #{these_credentials}"
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
these_credentials.first
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def other_credential
|
|
76
|
-
other_credentials = other_device.credentials
|
|
77
|
-
|
|
78
|
-
unless other_credentials.size == 1
|
|
79
|
-
raise "Don't know which Virtual Credential of mine to pick: #{other_credentials}"
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
other_credentials.first
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def this_device
|
|
86
|
-
unless this_session.size == 1
|
|
87
|
-
raise "Don't know which Virtual Authenticator of mine to pick: #{this_session.keys}"
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
this_session.values.first
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def other_device
|
|
94
|
-
unless other_session.size == 1
|
|
95
|
-
raise "Don't know which other Virtual Authenticator to pick: #{other_session.keys}"
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
other_session.values.first
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def this_session
|
|
102
|
-
sessions[Capybara.session_name]
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def other_session
|
|
106
|
-
other_session_keys = sessions.keys.reject { it == Capybara.session_name }
|
|
107
|
-
|
|
108
|
-
unless other_session_keys.size == 1
|
|
109
|
-
raise "Cannot determine #{sessions.keys} as alternative to #{Capybara.session_name}"
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
sessions[other_session_keys.first]
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def sessions
|
|
116
|
-
@sessions ||= {}
|
|
117
|
-
@sessions[Capybara.session_name] ||= {}
|
|
118
|
-
@sessions
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
end
|