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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/app/assets/images/booth/fido/passkey_mark_b_reverse.svg +55 -0
  4. data/config/locales/de.yml +5 -5
  5. data/lib/booth/core/sessions/to_passport.rb +11 -1
  6. data/lib/booth/core/webauth/authentication_verification.rb +5 -5
  7. data/lib/booth/models/authenticator.rb +11 -0
  8. data/lib/booth/passport.rb +15 -0
  9. data/lib/booth/requests/authentication.rb +1 -1
  10. data/lib/booth/requests/storages/login.rb +4 -1
  11. data/lib/booth/requests/sudo.rb +12 -2
  12. data/lib/booth/test.rb +10 -17
  13. data/lib/booth/testing/incorporation_test_case.rb +3 -1
  14. data/lib/booth/testing/shortcuts.rb +8 -17
  15. data/lib/booth/testing/support/assert_logged_in.rb +26 -27
  16. data/lib/booth/testing/support/assert_logged_out.rb +15 -11
  17. data/lib/booth/testing/support/assert_partial.rb +20 -29
  18. data/lib/booth/testing/support/capybara_step_logger.rb +22 -0
  19. data/lib/booth/testing/support/{soft_reset_session.rb → clear_cookies.rb} +5 -6
  20. data/lib/booth/testing/support/cookie_data_from_browser.rb +38 -0
  21. data/lib/booth/testing/support/shortcuts/create_and_onboard.rb +4 -0
  22. data/lib/booth/testing/support/shortcuts/login_with_passkey.rb +6 -4
  23. data/lib/booth/testing/support/shortcuts/register_new_passkey.rb +6 -2
  24. data/lib/booth/testing/support/virtual_authenticator.rb +196 -0
  25. data/lib/booth/testing/support/virtual_authenticators/create.rb +22 -7
  26. data/lib/booth/testing/support/virtual_authenticators/destroy.rb +14 -9
  27. data/lib/booth/testing/support/virtual_authenticators/enable.rb +14 -2
  28. data/lib/booth/testing/support/virtual_authenticators/load.rb +7 -19
  29. data/lib/booth/testing/support/virtual_authenticators.rb +106 -0
  30. data/lib/booth/testing/support/visit.rb +1 -0
  31. data/lib/booth/testing/userland/login_remotely.rb +2 -2
  32. data/lib/booth/testing/userland/onboarding_first_time.rb +5 -4
  33. data/lib/booth/testing/userland/onboarding_to_reset_passkeys.rb +3 -3
  34. data/lib/booth/testing/userland/registration_with_passkey.rb +9 -6
  35. data/lib/booth/testing/userland/registration_without_passkey.rb +11 -7
  36. data/lib/booth/testing/userland/sessions_manage_behavior.rb +14 -3
  37. data/lib/booth/userland/webauths/index.rb +9 -3
  38. data/lib/booth/userland/webauths/new.rb +10 -2
  39. data/lib/booth/userland/webauths/transitions/create/choose_nickname.rb +1 -1
  40. data/lib/booth/userland/webauths/transitions/sudo/authentication_initiation.rb +5 -0
  41. data/lib/booth/userland/webauths/transitions/sudo/authentication_verification.rb +1 -1
  42. data/lib/booth/version.rb +1 -1
  43. metadata +8 -4
  44. data/lib/booth/testing/support/get_session_value.rb +0 -37
  45. 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.clone_from_other_session
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 _1 }
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
- should_add_more_authenticators: should_add_more_authenticators?
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:, nickname: authenticator&.nickname
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 { 'The nickname successfully changed' }
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
@@ -25,7 +25,7 @@ module Booth
25
25
  ::Booth::Core::Webauth::AuthenticationVerification.call(
26
26
  request:,
27
27
  credential_id: request.authentication.credential_id,
28
- challenge: request.sudo.webauthn_challenge
28
+ challenge: request.sudo.webauthn_challenge,
29
29
  )
30
30
  end
31
31
  end
data/lib/booth/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Booth
4
- VERSION = '0.0.3'
4
+ VERSION = '0.0.4'
5
5
  end
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.3
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/soft_reset_session.rb
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