booth 0.0.1 → 0.0.2

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 (383) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/LICENSE.md +1 -2
  4. data/README.md +37 -6
  5. data/app/assets/images/booth/browsers/README.md +1 -2
  6. data/app/assets/images/booth/browsers/chrome.svg +1 -1
  7. data/app/assets/images/booth/browsers/edge.svg +1 -1
  8. data/app/assets/images/booth/browsers/firefox.svg +1 -1
  9. data/app/assets/images/booth/browsers/opera.svg +1 -1
  10. data/app/assets/images/booth/browsers/safari.svg +1 -1
  11. data/app/assets/images/booth/fido/passkey_mark_a.svg +10 -0
  12. data/app/assets/images/booth/fido/passkey_mark_a_black.svg +32 -0
  13. data/app/assets/images/booth/fido/passkey_mark_a_reverse.svg +33 -0
  14. data/app/assets/images/booth/fido/passkey_mark_a_white.svg +32 -0
  15. data/app/assets/images/booth/fido/passkey_mark_b_black.svg +1 -0
  16. data/app/assets/images/booth/platforms/android.svg +1 -6
  17. data/app/assets/images/booth/platforms/apple.svg +1 -6
  18. data/app/assets/images/booth/platforms/linux.svg +1 -6
  19. data/app/assets/images/booth/platforms/windows.svg +1 -6
  20. data/app/assets/javascripts/booth/authentication.js +29 -0
  21. data/app/assets/javascripts/booth/authentication.js.map +1 -0
  22. data/app/assets/javascripts/booth/error.js +38 -0
  23. data/app/assets/javascripts/booth/error.js.map +1 -0
  24. data/app/assets/javascripts/booth/form.js +78 -0
  25. data/app/assets/javascripts/booth/form.js.map +1 -0
  26. data/app/assets/javascripts/booth/gui.js +53 -0
  27. data/app/assets/javascripts/booth/gui.js.map +1 -0
  28. data/app/assets/javascripts/booth/registration.js +29 -0
  29. data/app/assets/javascripts/booth/registration.js.map +1 -0
  30. data/app/assets/javascripts/booth/setup.js +14 -0
  31. data/app/assets/javascripts/booth/verification.js +49 -0
  32. data/app/assets/javascripts/booth/verification.js.map +1 -0
  33. data/app/assets/javascripts/declarations/authentication.d.ts +6 -0
  34. data/app/assets/javascripts/declarations/error.d.ts +36 -0
  35. data/app/assets/javascripts/declarations/form.d.ts +8 -0
  36. data/app/assets/javascripts/declarations/gui.d.ts +4 -0
  37. data/app/assets/javascripts/declarations/registration.d.ts +6 -0
  38. data/app/assets/javascripts/declarations/setup.d.ts +3 -0
  39. data/app/assets/javascripts/declarations/verification.d.ts +6 -0
  40. data/app/assets/javascripts/src/authentication.ts +41 -0
  41. data/app/assets/javascripts/src/error.ts +35 -0
  42. data/app/assets/javascripts/src/form.ts +90 -0
  43. data/app/assets/javascripts/src/gui.ts +59 -0
  44. data/app/assets/javascripts/src/registration.ts +44 -0
  45. data/app/assets/javascripts/src/verification.ts +61 -0
  46. data/app/assets/stylesheets/booth/booth.css +3 -0
  47. data/config/importmap.rb +11 -0
  48. data/config/locales/de.yml +14 -38
  49. data/config/locales/en.yml +17 -36
  50. data/data/combined_aaguid.json +1 -0
  51. data/lib/booth/adminland/credentials/create.rb +10 -12
  52. data/lib/booth/adminland/credentials/index.rb +31 -0
  53. data/lib/booth/adminland/onboardings/create.rb +24 -15
  54. data/lib/booth/adminland/onboardings/destroy.rb +8 -4
  55. data/lib/booth/adminland/onboardings/find.rb +52 -45
  56. data/lib/booth/adminland/onboardings/find_unconsumed.rb +61 -0
  57. data/lib/booth/adminland/onboardings/index.rb +6 -3
  58. data/lib/booth/adminland/periodic_cleanup.rb +7 -2
  59. data/lib/booth/adminland.rb +17 -18
  60. data/lib/booth/coercers/domain.rb +11 -0
  61. data/lib/booth/coercers/request.rb +51 -0
  62. data/lib/booth/coercers/scope.rb +11 -0
  63. data/lib/booth/comparisons/domain.rb +38 -0
  64. data/lib/booth/comparisons/scope.rb +38 -0
  65. data/lib/booth/concerns/action.rb +25 -13
  66. data/lib/booth/concerns/transition.rb +5 -2
  67. data/lib/booth/configuration.rb +14 -73
  68. data/lib/booth/configure.rb +3 -10
  69. data/lib/booth/{audits/register → core/audit}/completed_onboarding.rb +8 -6
  70. data/lib/booth/core/audit/credential_created.rb +24 -0
  71. data/lib/booth/core/audit/logout.rb +24 -0
  72. data/lib/booth/core/authenticators/confirm.rb +30 -0
  73. data/lib/booth/core/authenticators/step.rb +24 -0
  74. data/lib/booth/core/cooldowns/distance_of_time.rb +50 -0
  75. data/lib/booth/core/cooldowns/strategies/exponential.rb +88 -0
  76. data/lib/booth/core/cooldowns/strategies/global.rb +66 -0
  77. data/lib/booth/core/cooldowns/strategies/result.rb +27 -0
  78. data/lib/booth/core/credentials/create.rb +32 -0
  79. data/lib/booth/core/credentials/find_by_username.rb +63 -0
  80. data/lib/booth/core/credentials/index.rb +15 -0
  81. data/lib/booth/core/credentials/webauth_challenge.rb +37 -0
  82. data/lib/booth/core/geolocation.rb +25 -0
  83. data/lib/booth/core/onboardings/find.rb +92 -0
  84. data/lib/booth/core/onboardings/step.rb +19 -0
  85. data/lib/booth/core/remotes/get.rb +45 -0
  86. data/lib/booth/core/remotes/respond.rb +82 -0
  87. data/lib/booth/core/remotes/set_for_login.rb +31 -0
  88. data/lib/booth/core/sessions/create_and_login.rb +63 -0
  89. data/lib/booth/core/sessions/historical_locations.rb +22 -0
  90. data/lib/booth/core/sessions/index.rb +66 -0
  91. data/lib/booth/core/sessions/revoke.rb +59 -0
  92. data/lib/booth/core/sessions/revoke_all_others.rb +49 -0
  93. data/lib/booth/core/sessions/to_passport.rb +35 -0
  94. data/lib/booth/core/webauth/authentication_verification.rb +76 -0
  95. data/lib/booth/core/webauth/options_for_create.rb +56 -0
  96. data/lib/booth/core/webauth/options_for_get.rb +30 -0
  97. data/lib/booth/core/webauth/provider.rb +36 -0
  98. data/lib/booth/core/webauth/registration_verification.rb +100 -0
  99. data/lib/booth/credential.rb +35 -0
  100. data/lib/booth/engine.rb +15 -4
  101. data/lib/booth/errors.rb +2 -0
  102. data/lib/booth/hooks/after_fetch.rb +14 -6
  103. data/lib/booth/hooks/before_logout.rb +5 -3
  104. data/lib/booth/hooks/serialize_from_session.rb +13 -5
  105. data/lib/booth/hooks/serialize_into_session.rb +6 -3
  106. data/lib/booth/logging.rb +13 -42
  107. data/lib/booth/models/application_record.rb +3 -0
  108. data/lib/booth/models/audit.rb +10 -11
  109. data/lib/booth/models/authenticator.rb +6 -9
  110. data/lib/booth/models/credential.rb +17 -20
  111. data/lib/booth/models/onboarding.rb +16 -39
  112. data/lib/booth/models/{contest.rb → remote.rb} +13 -14
  113. data/lib/booth/models/remotes/scopes/recently_created.rb +26 -0
  114. data/lib/booth/models/remotes/scopes/recently_responded.rb +35 -0
  115. data/lib/booth/models/session.rb +15 -10
  116. data/lib/booth/models/user_agent.rb +2 -0
  117. data/lib/booth/request.rb +43 -22
  118. data/lib/booth/requests/agent.rb +3 -1
  119. data/lib/booth/requests/authentication.rb +15 -5
  120. data/lib/booth/requests/ip.rb +4 -2
  121. data/lib/booth/requests/return_path.rb +4 -2
  122. data/lib/booth/requests/session.rb +6 -4
  123. data/lib/booth/requests/storage.rb +5 -31
  124. data/lib/booth/requests/storages/login.rb +35 -29
  125. data/lib/booth/requests/storages/registration.rb +2 -0
  126. data/lib/booth/requests/storages/webauth.rb +3 -0
  127. data/lib/booth/requests/sudo.rb +6 -50
  128. data/lib/booth/routes/userland.rb +13 -59
  129. data/lib/booth/syntaxes/domain.rb +46 -0
  130. data/lib/booth/syntaxes/email.rb +11 -8
  131. data/lib/booth/syntaxes/ip.rb +6 -4
  132. data/lib/booth/syntaxes/remote_code.rb +60 -0
  133. data/lib/booth/syntaxes/scope.rb +7 -3
  134. data/lib/booth/syntaxes/secret_key.rb +8 -6
  135. data/lib/booth/syntaxes/username.rb +23 -10
  136. data/lib/booth/syntaxes/uuid.rb +3 -1
  137. data/lib/booth/test.rb +27 -22
  138. data/lib/booth/testing/incorporation_test_case.rb +29 -0
  139. data/lib/booth/testing/shortcuts.rb +77 -0
  140. data/lib/booth/testing/support/assert_all_partials_were_covered.rb +69 -0
  141. data/lib/booth/testing/support/assert_logged_in.rb +68 -0
  142. data/lib/booth/{test → testing}/support/assert_logged_out.rb +7 -4
  143. data/lib/booth/testing/support/assert_partial.rb +56 -0
  144. data/lib/booth/{test → testing}/support/force_login.rb +10 -4
  145. data/lib/booth/{test → testing}/support/get_session_value.rb +8 -6
  146. data/lib/booth/testing/support/scenario.rb +23 -0
  147. data/lib/booth/testing/support/shortcuts/create_and_onboard.rb +56 -0
  148. data/lib/booth/testing/support/shortcuts/login_with_passkey.rb +55 -0
  149. data/lib/booth/testing/support/shortcuts/register_new_passkey.rb +51 -0
  150. data/lib/booth/testing/support/soft_reset_session.rb +24 -0
  151. data/lib/booth/testing/support/virtual_authenticators/create.rb +34 -0
  152. data/lib/booth/testing/support/virtual_authenticators/destroy.rb +20 -0
  153. data/lib/booth/testing/support/virtual_authenticators/enable.rb +24 -0
  154. data/lib/booth/testing/support/virtual_authenticators/load.rb +38 -0
  155. data/lib/booth/testing/support/virtual_authenticators/manager.rb +124 -0
  156. data/lib/booth/testing/support/visit.rb +62 -0
  157. data/lib/booth/testing/userland/login_remotely.rb +100 -0
  158. data/lib/booth/testing/userland/onboarding_first_time.rb +81 -0
  159. data/lib/booth/testing/userland/onboarding_to_reset_passkeys.rb +129 -0
  160. data/lib/booth/testing/userland/registration_with_passkey.rb +93 -0
  161. data/lib/booth/testing/userland/registration_without_passkey.rb +101 -0
  162. data/lib/booth/testing/userland/sessions_manage_behavior.rb +68 -0
  163. data/lib/booth/testing/userland/sessions_revoke_all_others.rb +17 -0
  164. data/lib/booth/testing/userland/sessions_revoke_one.rb +17 -0
  165. data/lib/booth/testing/userland.rb +36 -0
  166. data/lib/booth/to_struct.rb +9 -2
  167. data/lib/booth/userland/extract_flash_messages.rb +10 -3
  168. data/lib/booth/userland/logins/create.rb +8 -6
  169. data/lib/booth/userland/logins/destroy.rb +23 -6
  170. data/lib/booth/userland/logins/new.rb +23 -25
  171. data/lib/booth/userland/logins/transitions/create/choose_username.rb +62 -27
  172. data/lib/booth/userland/logins/transitions/create/skip_remotes.rb +18 -14
  173. data/lib/booth/userland/logins/transitions/create/webauth_authentication_initiation.rb +54 -48
  174. data/lib/booth/userland/logins/transitions/create/webauth_authentication_verification.rb +62 -58
  175. data/lib/booth/userland/logins/transitions/new/already_logged_in.rb +4 -3
  176. data/lib/booth/userland/logins/transitions/new/fallible.rb +4 -0
  177. data/lib/booth/userland/logins/transitions/new/{mode_username_and_password.rb → missing_authenticators.rb} +5 -4
  178. data/lib/booth/userland/logins/transitions/new/mode_username_and_webauth.rb +6 -4
  179. data/lib/booth/userland/logins/transitions/new/no_username_chosen.rb +3 -1
  180. data/lib/booth/userland/logins/transitions/new/remote_session_available.rb +20 -13
  181. data/lib/booth/userland/logins/transitions/new/timed_out.rb +3 -1
  182. data/lib/booth/userland/onboardings/show.rb +65 -39
  183. data/lib/booth/userland/onboardings/update.rb +46 -38
  184. data/lib/booth/userland/registrations/create.rb +51 -20
  185. data/lib/booth/userland/registrations/new.rb +6 -7
  186. data/lib/booth/userland/remotes/show.rb +56 -0
  187. data/lib/booth/userland/{personal_contests → remotes}/update.rb +5 -3
  188. data/lib/booth/userland/sessions/destroy_one_or_other.rb +3 -16
  189. data/lib/booth/userland/sessions/index.rb +4 -2
  190. data/lib/booth/userland/sessions/show.rb +5 -6
  191. data/lib/booth/userland/sessions/transitions/destroy/enter_webauth.rb +8 -6
  192. data/lib/booth/userland/sessions/transitions/destroy/webauth_authentication_initiation.rb +8 -6
  193. data/lib/booth/userland/sessions/transitions/destroy/webauth_authentication_verification.rb +7 -5
  194. data/lib/booth/userland/sessions/transitions/show/enter_webauth.rb +8 -6
  195. data/lib/booth/userland/webauths/create.rb +20 -17
  196. data/lib/booth/userland/webauths/destroy.rb +6 -16
  197. data/lib/booth/userland/webauths/guards/sudo.rb +10 -5
  198. data/lib/booth/userland/webauths/index.rb +4 -2
  199. data/lib/booth/userland/webauths/new.rb +7 -22
  200. data/lib/booth/userland/webauths/sudo.rb +3 -1
  201. data/lib/booth/userland/webauths/transitions/create/authentication_initiation.rb +8 -11
  202. data/lib/booth/userland/webauths/transitions/create/authentication_verification.rb +11 -13
  203. data/lib/booth/userland/webauths/transitions/create/choose_nickname.rb +8 -5
  204. data/lib/booth/userland/webauths/transitions/create/registration_initiation.rb +15 -14
  205. data/lib/booth/userland/webauths/transitions/create/registration_verification.rb +34 -28
  206. data/lib/booth/userland/webauths/transitions/create/reset.rb +2 -0
  207. data/lib/booth/userland/webauths/transitions/new/step.rb +3 -1
  208. data/lib/booth/userland/webauths/transitions/sudo/authentication_initiation.rb +5 -10
  209. data/lib/booth/userland/webauths/transitions/sudo/authentication_verification.rb +4 -2
  210. data/lib/booth/userland.rb +53 -109
  211. data/lib/booth/version.rb +3 -1
  212. data/lib/booth.rb +6 -236
  213. data/lib/generators/booth/migration/migration_generator.rb +2 -1
  214. data/lib/generators/booth/migration/templates/add_credential_to_users.erb +6 -4
  215. data/lib/generators/booth/migration/templates/create_booth_tables.erb +61 -72
  216. metadata +124 -571
  217. data/app/assets/config/booth_manifest.js +0 -15
  218. data/app/assets/images/booth/browsers/internet_explorer.svg +0 -1
  219. data/app/assets/javascripts/booth/all.js +0 -162
  220. data/app/assets/javascripts/booth/all.js.map +0 -1
  221. data/app/assets/javascripts/booth/booth.ts +0 -194
  222. data/app/assets/javascripts/booth/webauthn-json.ts +0 -99
  223. data/lib/booth/adminland/recoveries/consume.rb +0 -70
  224. data/lib/booth/audits/register/added_otp.rb +0 -22
  225. data/lib/booth/audits/register/changed_otp.rb +0 -22
  226. data/lib/booth/audits/register/correct_otp.rb +0 -42
  227. data/lib/booth/audits/register/correct_password.rb +0 -43
  228. data/lib/booth/audits/register/logout.rb +0 -22
  229. data/lib/booth/audits/register/requested_password_reset.rb +0 -22
  230. data/lib/booth/audits/register/wrong_otp.rb +0 -22
  231. data/lib/booth/audits/register/wrong_password.rb +0 -25
  232. data/lib/booth/authenticators/confirm.rb +0 -34
  233. data/lib/booth/authenticators/credential_mode_after_confirmation.rb +0 -25
  234. data/lib/booth/authenticators/step.rb +0 -19
  235. data/lib/booth/contests/get.rb +0 -36
  236. data/lib/booth/contests/respond.rb +0 -78
  237. data/lib/booth/contests/set_for_login.rb +0 -28
  238. data/lib/booth/cooldowns/distance_of_time.rb +0 -46
  239. data/lib/booth/cooldowns/otp.rb +0 -22
  240. data/lib/booth/cooldowns/password.rb +0 -44
  241. data/lib/booth/cooldowns/password_reset.rb +0 -24
  242. data/lib/booth/cooldowns/strategies/exponential.rb +0 -82
  243. data/lib/booth/cooldowns/strategies/global.rb +0 -62
  244. data/lib/booth/cooldowns/strategies/result.rb +0 -22
  245. data/lib/booth/credentials/create.rb +0 -28
  246. data/lib/booth/credentials/create_with_onboarding.rb +0 -26
  247. data/lib/booth/credentials/find_by_username.rb +0 -45
  248. data/lib/booth/credentials/mode.rb +0 -69
  249. data/lib/booth/credentials/modes/otp_addable.rb +0 -23
  250. data/lib/booth/credentials/modes/otp_changeable.rb +0 -23
  251. data/lib/booth/credentials/modes/otp_manageable.rb +0 -17
  252. data/lib/booth/credentials/modes/otp_removable.rb +0 -23
  253. data/lib/booth/credentials/modes/password_addable.rb +0 -29
  254. data/lib/booth/credentials/modes/password_changeable.rb +0 -31
  255. data/lib/booth/credentials/modes/password_manageable.rb +0 -17
  256. data/lib/booth/credentials/modes/password_removable.rb +0 -24
  257. data/lib/booth/credentials/modes/password_removal_requires_user_verifiable_webauth.rb +0 -16
  258. data/lib/booth/credentials/modes/webauth_addable.rb +0 -26
  259. data/lib/booth/credentials/modes/webauth_manageable.rb +0 -16
  260. data/lib/booth/credentials/modes/webauth_removable.rb +0 -25
  261. data/lib/booth/credentials/otp_authentication.rb +0 -59
  262. data/lib/booth/credentials/password_authentication.rb +0 -72
  263. data/lib/booth/credentials/webauth_challenge.rb +0 -28
  264. data/lib/booth/geolocation.rb +0 -20
  265. data/lib/booth/logger.rb +0 -41
  266. data/lib/booth/method_object.rb +0 -73
  267. data/lib/booth/mode.rb +0 -22
  268. data/lib/booth/models/concerns/modeable.rb +0 -50
  269. data/lib/booth/models/concerns/otpable.rb +0 -37
  270. data/lib/booth/models/concerns/passwordable.rb +0 -58
  271. data/lib/booth/models/contests/scopes/recently_created.rb +0 -23
  272. data/lib/booth/models/contests/scopes/recently_responded.rb +0 -32
  273. data/lib/booth/models/password_reset.rb +0 -41
  274. data/lib/booth/models/recovery.rb +0 -32
  275. data/lib/booth/models/registration.rb +0 -10
  276. data/lib/booth/modes/base.rb +0 -25
  277. data/lib/booth/modes/username_and_password.rb +0 -7
  278. data/lib/booth/modes/username_and_webauth.rb +0 -7
  279. data/lib/booth/modes/username_password_and_otp.rb +0 -7
  280. data/lib/booth/modes/username_password_and_webauth.rb +0 -7
  281. data/lib/booth/onboardings/find.rb +0 -35
  282. data/lib/booth/onboardings/propagate_to_credential.rb +0 -63
  283. data/lib/booth/onboardings/step.rb +0 -68
  284. data/lib/booth/password_resets/create.rb +0 -57
  285. data/lib/booth/password_resets/find.rb +0 -36
  286. data/lib/booth/password_resets/propagate_to_credential.rb +0 -36
  287. data/lib/booth/password_resets/step.rb +0 -18
  288. data/lib/booth/recoveries/create.rb +0 -45
  289. data/lib/booth/requests/storages/otp.rb +0 -54
  290. data/lib/booth/requests/storages/password.rb +0 -49
  291. data/lib/booth/requests/storages/password_reset.rb +0 -35
  292. data/lib/booth/requests/storages/recovery.rb +0 -35
  293. data/lib/booth/sessions/create_and_login.rb +0 -46
  294. data/lib/booth/sessions/historical_locations.rb +0 -18
  295. data/lib/booth/sessions/index.rb +0 -59
  296. data/lib/booth/sessions/revoke.rb +0 -51
  297. data/lib/booth/sessions/revoke_all_others.rb +0 -43
  298. data/lib/booth/sessions/to_passport.rb +0 -51
  299. data/lib/booth/syntaxes/contest_code.rb +0 -58
  300. data/lib/booth/syntaxes/otp.rb +0 -57
  301. data/lib/booth/syntaxes/scope_comparison.rb +0 -28
  302. data/lib/booth/test/helpers.rb +0 -63
  303. data/lib/booth/test/support/assert_all_partials_were_covered.rb +0 -63
  304. data/lib/booth/test/support/assert_logged_in.rb +0 -49
  305. data/lib/booth/test/support/assert_partial.rb +0 -29
  306. data/lib/booth/test/support/otp_code_from_session.rb +0 -30
  307. data/lib/booth/test/support/soft_reset_session.rb +0 -22
  308. data/lib/booth/test/userland/logins/missing_authenticators.rb +0 -72
  309. data/lib/booth/test/userland/logins/missing_onboarding.rb +0 -35
  310. data/lib/booth/test/userland/logins/username_and_password.rb +0 -40
  311. data/lib/booth/test/userland/logins/username_and_webauth.rb +0 -75
  312. data/lib/booth/test/userland/logins/username_password_and_otp.rb +0 -45
  313. data/lib/booth/test/userland/logins/username_password_and_webauth.rb +0 -86
  314. data/lib/booth/test/userland/onboardings/already_logged_in.rb +0 -64
  315. data/lib/booth/test/userland/onboardings/otp.rb +0 -63
  316. data/lib/booth/test/userland/onboardings/password.rb +0 -49
  317. data/lib/booth/test/userland/onboardings/timeout.rb +0 -47
  318. data/lib/booth/test/userland/otps/manage.rb +0 -86
  319. data/lib/booth/test/userland/password_resets/reset.rb +0 -102
  320. data/lib/booth/test/userland.rb +0 -38
  321. data/lib/booth/test/webauthn/disable.rb +0 -17
  322. data/lib/booth/test/webauthn/enable.rb +0 -19
  323. data/lib/booth/test/webauthn/virtual_authenticators/create.rb +0 -38
  324. data/lib/booth/test/webauthn/virtual_authenticators/destroy.rb +0 -20
  325. data/lib/booth/userland/logins/transitions/create/enter_otp.rb +0 -70
  326. data/lib/booth/userland/logins/transitions/create/verify_password.rb +0 -70
  327. data/lib/booth/userland/logins/transitions/new/mode_first_time.rb +0 -20
  328. data/lib/booth/userland/logins/transitions/new/mode_username_password_and_otp.rb +0 -24
  329. data/lib/booth/userland/logins/transitions/new/mode_username_password_and_webauth.rb +0 -24
  330. data/lib/booth/userland/onboardings/transitions/update/choose_mode.rb +0 -58
  331. data/lib/booth/userland/onboardings/transitions/update/choose_password.rb +0 -41
  332. data/lib/booth/userland/onboardings/transitions/update/choose_webauth_nickname.rb +0 -50
  333. data/lib/booth/userland/onboardings/transitions/update/confirm_otp.rb +0 -58
  334. data/lib/booth/userland/onboardings/transitions/update/confirm_password.rb +0 -49
  335. data/lib/booth/userland/onboardings/transitions/update/register_otp.rb +0 -31
  336. data/lib/booth/userland/onboardings/transitions/update/reset_otp.rb +0 -40
  337. data/lib/booth/userland/onboardings/transitions/update/reset_password.rb +0 -35
  338. data/lib/booth/userland/onboardings/transitions/update/reset_webauth.rb +0 -46
  339. data/lib/booth/userland/onboardings/transitions/update/webauth_authentication_initiation.rb +0 -40
  340. data/lib/booth/userland/onboardings/transitions/update/webauth_authentication_verification.rb +0 -59
  341. data/lib/booth/userland/onboardings/transitions/update/webauth_registration_initiation.rb +0 -46
  342. data/lib/booth/userland/onboardings/transitions/update/webauth_registration_verification.rb +0 -56
  343. data/lib/booth/userland/otps/destroy.rb +0 -42
  344. data/lib/booth/userland/otps/edit.rb +0 -72
  345. data/lib/booth/userland/otps/guards/manageable.rb +0 -21
  346. data/lib/booth/userland/otps/guards/sudo.rb +0 -23
  347. data/lib/booth/userland/otps/show.rb +0 -36
  348. data/lib/booth/userland/otps/sudo.rb +0 -51
  349. data/lib/booth/userland/otps/transitions/update/confirm.rb +0 -84
  350. data/lib/booth/userland/otps/transitions/update/register.rb +0 -40
  351. data/lib/booth/userland/otps/transitions/update/reset.rb +0 -31
  352. data/lib/booth/userland/otps/update.rb +0 -34
  353. data/lib/booth/userland/password_resets/create.rb +0 -73
  354. data/lib/booth/userland/password_resets/guards/logged_out.rb +0 -21
  355. data/lib/booth/userland/password_resets/new.rb +0 -57
  356. data/lib/booth/userland/password_resets/show.rb +0 -77
  357. data/lib/booth/userland/password_resets/transitions/update/choose_password.rb +0 -48
  358. data/lib/booth/userland/password_resets/transitions/update/confirm_password.rb +0 -54
  359. data/lib/booth/userland/password_resets/transitions/update/reset_password.rb +0 -29
  360. data/lib/booth/userland/password_resets/update.rb +0 -65
  361. data/lib/booth/userland/passwords/destroy.rb +0 -41
  362. data/lib/booth/userland/passwords/edit.rb +0 -54
  363. data/lib/booth/userland/passwords/guards/manageable.rb +0 -21
  364. data/lib/booth/userland/passwords/guards/removable.rb +0 -21
  365. data/lib/booth/userland/passwords/guards/sudo.rb +0 -21
  366. data/lib/booth/userland/passwords/remove.rb +0 -34
  367. data/lib/booth/userland/passwords/show.rb +0 -32
  368. data/lib/booth/userland/passwords/sudo.rb +0 -55
  369. data/lib/booth/userland/passwords/transitions/remove/step.rb +0 -27
  370. data/lib/booth/userland/passwords/transitions/update/choose_password.rb +0 -62
  371. data/lib/booth/userland/passwords/transitions/update/confirm_password.rb +0 -82
  372. data/lib/booth/userland/passwords/update.rb +0 -33
  373. data/lib/booth/userland/personal_contests/show.rb +0 -60
  374. data/lib/booth/userland/recoveries/create.rb +0 -48
  375. data/lib/booth/userland/recoveries/new.rb +0 -35
  376. data/lib/booth/userland/sessions/transitions/destroy/enter_password.rb +0 -50
  377. data/lib/booth/userland/sessions/transitions/destroy/verify_password.rb +0 -83
  378. data/lib/booth/userland/webauths/guards/manageable.rb +0 -21
  379. data/lib/booth/webauth/authentication_verification.rb +0 -68
  380. data/lib/booth/webauth/demand_user_verification.rb +0 -29
  381. data/lib/booth/webauth/options_for_create.rb +0 -46
  382. data/lib/booth/webauth/options_for_get.rb +0 -29
  383. data/lib/generators/booth/migration/templates/create_booth_mode_types.erb +0 -20
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Booth
4
+ module Testing
5
+ module Userland
6
+ class OnboardingToResetPasskeys < ::Booth::Testing::IncorporationTestCase
7
+ def call
8
+ before_test&.call
9
+
10
+ visit_namespaced controller: :onboardings, action: :show, params: { id: 'wrong-key' }
11
+
12
+ # ------------------------------ SIGNIFICANT TEST ---------------------------
13
+ # Onboardings can only be opened with the secret key, typos can be too short.
14
+ # ---------------------------------------------------------------------------
15
+ assert_userland_view controller: :onboardings, step: :not_found
16
+
17
+ credential = ::Booth::Models::Credential.create!(
18
+ domain: ::Capybara.app_host.remove('http://'),
19
+ username: 'alice',
20
+ scope:,
21
+ )
22
+
23
+ after_credential&.call(credential_id: credential.id)
24
+
25
+ temporary_onboarding = ::Booth::Models::Onboarding.create!(
26
+ credential_id: credential.id,
27
+ )
28
+
29
+ visit_namespaced controller: :onboardings, action: :show,
30
+ params: { id: temporary_onboarding.secret_key }
31
+
32
+ assert_userland_view controller: :onboardings, step: :redeem
33
+ click_on :submit
34
+
35
+ assert_logged_in username: 'alice'
36
+
37
+ temporary_onboarding.destroy!
38
+ onboarding = ::Booth::Models::Onboarding.create!(
39
+ credential_id: credential.id,
40
+ )
41
+
42
+ visit_namespaced controller: :onboardings, action: :show,
43
+ params: { id: onboarding.secret_key }
44
+
45
+ # ----------------------- SIGNIFICANT TEST ---------------
46
+ # Logged in without Authenticators requires no Onboarding.
47
+ # --------------------------------------------------------
48
+ assert_userland_view controller: :onboardings, step: :not_needed
49
+
50
+ virtual_authenticators.create
51
+ visit_namespaced controller: :webauths, action: :new
52
+
53
+ assert_userland_view controller: :webauths, step: :register
54
+
55
+ click_on :register
56
+
57
+ assert_userland_view controller: :webauths, step: :choose_nickname
58
+
59
+ fill_in :nickname, with: 'Latchkey'
60
+ click_on :submit
61
+
62
+ assert_userland_view controller: :webauths, step: :confirm
63
+ click_on :test
64
+
65
+ assert_userland_view controller: :webauths, step: :completed
66
+
67
+ authenticator = ::Booth::Models::Authenticator.sole
68
+
69
+ assert_equal 'Latchkey', authenticator.nickname
70
+
71
+ soft_reset_session
72
+
73
+ # Onboard via URL
74
+
75
+ visit_namespaced controller: :onboardings, action: :show,
76
+ params: { id: onboarding.secret_key }
77
+
78
+ assert_userland_view controller: :onboardings, step: :redeem
79
+
80
+ click_on :submit
81
+
82
+ assert_equal 0, ::Booth::Models::Authenticator.count # (Side-effect, so we can avoid sudo)
83
+
84
+ visit_namespaced controller: :webauths, action: :new
85
+
86
+ # ------------------ SIGNIFICANT TEST -------------------
87
+ # Redeeming an Onboarding allows for adding new Passkeys.
88
+ # -------------------------------------------------------
89
+ assert_userland_view controller: :webauths, step: :register
90
+
91
+ click_on :register
92
+
93
+ assert_userland_view controller: :webauths, step: :choose_nickname
94
+
95
+ fill_in :nickname, with: 'Superkey'
96
+ click_on :submit
97
+
98
+ assert_userland_view controller: :webauths, step: :confirm
99
+ click_on :test
100
+
101
+ assert_userland_view controller: :webauths, step: :completed
102
+
103
+ authenticator = ::Booth::Models::Authenticator.sole
104
+
105
+ assert_equal 'Superkey', authenticator.nickname
106
+
107
+ travel 2.weeks
108
+ visit_namespaced controller: :onboardings, action: :show,
109
+ params: { id: onboarding.secret_key }
110
+
111
+ # ---------------------- SIGNIFICANT TEST -----------------
112
+ # Cannot open an old Onboarding (even if already consumed).
113
+ # ---------------------------------------------------------
114
+ assert_userland_view controller: :onboardings, step: :timed_out
115
+
116
+ ::Booth::Models::Credential.sole.update!(blocked_at: Time.current)
117
+
118
+ visit_namespaced controller: :onboardings, action: :show,
119
+ params: { id: onboarding.secret_key }
120
+
121
+ # ---------------------- SIGNIFICANT TEST -----------------
122
+ # Cannot open an old Onboarding when Credential is blocked.
123
+ # ---------------------------------------------------------
124
+ assert_userland_view controller: :onboardings, step: :blocked
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Booth
4
+ module Testing
5
+ module Userland
6
+ class RegistrationWithPasskey < ::Booth::Testing::IncorporationTestCase
7
+ def call
8
+ before_test&.call
9
+
10
+ # Register
11
+
12
+ visit_namespaced controller: :registrations, action: :new
13
+
14
+ return 'Skipping self-registration tests' if page.has_text?('HTTP ERROR 410') # Chrome Page
15
+
16
+ virtual_authenticators.create
17
+
18
+ assert_userland_view controller: :registrations, step: :choose_username
19
+
20
+ fill_in :username, with: 'alice'
21
+ click_on :submit
22
+
23
+ visit_namespaced controller: :webauths, action: :new
24
+
25
+ assert_userland_view controller: :webauths, step: :register
26
+
27
+ click_on :register
28
+
29
+ assert_userland_view controller: :webauths, step: :choose_nickname
30
+
31
+ fill_in :nickname, with: 'My Yubikey'
32
+ click_on :submit
33
+
34
+ assert_userland_view controller: :webauths, step: :confirm
35
+ click_on :test
36
+
37
+ # ------------ SIGNIFICANT TEST --------------
38
+ # Registering a Passkey via the Browser works.
39
+ # --------------------------------------------
40
+ assert_userland_view controller: :webauths, step: :completed
41
+
42
+ soft_reset_session
43
+
44
+ # Login
45
+
46
+ visit_namespaced controller: :logins, action: :new
47
+
48
+ assert_userland_view controller: :logins, step: :enter_username
49
+
50
+ fill_in :username, with: 'alice'
51
+ click_on :submit
52
+
53
+ # --------------------- SIGNIFICANT TEST -------------------------
54
+ # Without actually logging out, a Session is still regarded alive.
55
+ # ----------------------------------------------------------------
56
+ assert_userland_view controller: :logins, step: :remote_session_available
57
+
58
+ click_on :skip
59
+
60
+ assert_userland_view controller: :logins, step: :enter_webauth
61
+
62
+ # --------------------- SIGNIFICANT TEST --------------------------
63
+ # After registration and adding a Passkey, you can use it to login.
64
+ # -----------------------------------------------------------------
65
+ click_on :authenticate
66
+
67
+ visit_namespaced controller: :webauths, action: :index
68
+
69
+ assert_userland_view controller: :webauths, step: :index
70
+
71
+ # Try to register when already logged in
72
+
73
+ visit_namespaced controller: :registrations, action: :new
74
+
75
+ assert_userland_view controller: :registrations, step: :already_logged_in
76
+
77
+ visit_namespaced controller: :webauths, action: :new
78
+
79
+ assert_userland_view controller: :webauths, step: :register
80
+
81
+ click_on :register
82
+
83
+ assert_userland_view controller: :webauths, step: :register
84
+
85
+ # --------------------- SIGNIFICANT TEST ---------------
86
+ # A hardware device cannot be registered multiple times.
87
+ # ------------------------------------------------------
88
+ assert_text 'already registered with the relying party'
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Booth
4
+ module Testing
5
+ module Userland
6
+ class RegistrationWithoutPasskey < ::Booth::Testing::IncorporationTestCase
7
+ def call
8
+ before_test&.call
9
+
10
+ # Register normally
11
+
12
+ visit_namespaced controller: :registrations, action: :new
13
+
14
+ # Chrome Page
15
+ return 'Skipping self-registration tests' if page.has_text?('HTTP ERROR 410')
16
+
17
+ assert_userland_view controller: :registrations, step: :choose_username
18
+
19
+ fill_in :username, with: 'alice'
20
+ click_on :submit
21
+
22
+ # Propagate login to other device
23
+
24
+ code = nil
25
+ using_session(:other_device) do
26
+ # Login
27
+
28
+ visit_namespaced controller: :logins, action: :new
29
+
30
+ assert_userland_view controller: :logins, step: :enter_username
31
+
32
+ fill_in :username, with: 'alice'
33
+ click_on :submit
34
+
35
+ assert_userland_view controller: :logins, step: :remote_session_available
36
+
37
+ code = find('[data-booth="code"]').text
38
+ end
39
+
40
+ # Redeem remote code
41
+
42
+ visit_namespaced controller: :remote_logins, action: :show
43
+
44
+ # ------------- SIGNIFICANT TEST -----------------
45
+ # After registering you are immediately logged in.
46
+ # ------------------------------------------------
47
+
48
+ assert_userland_view controller: :remote_logins, step: :remote_login
49
+
50
+ fill_in :code, with: code
51
+ click_on :submit
52
+
53
+ assert_userland_view controller: :remote_logins, step: :remote_solved
54
+
55
+ using_session(:other_device) do
56
+ visit_namespaced controller: :webauths, action: :index
57
+
58
+ # -------------------------- SIGNIFICANT TEST ---------------------------
59
+ # You can remote-login even though you don't have any Authenticators yet.
60
+ # -----------------------------------------------------------------------
61
+ assert_userland_view controller: :webauths, step: :index
62
+ end
63
+
64
+ # Try to Login without having any Authenticators
65
+
66
+ soft_reset_session
67
+
68
+ visit_namespaced controller: :logins, action: :new
69
+
70
+ assert_userland_view controller: :logins, step: :enter_username
71
+
72
+ fill_in :username, with: 'alice'
73
+ click_on :submit
74
+
75
+ assert_userland_view controller: :logins, step: :remote_session_available
76
+
77
+ click_on :skip
78
+
79
+ # ------------------ SIGNIFICANT TEST --------------------
80
+ # If you didn't register any Passkeys, you are locked out.
81
+ # --------------------------------------------------------
82
+ assert_userland_view controller: :logins, step: :no_authenticators
83
+
84
+ visit_namespaced controller: :registrations, action: :new
85
+
86
+ assert_userland_view controller: :registrations, step: :choose_username
87
+
88
+ fill_in :username, with: 'alice'
89
+ click_on :submit
90
+
91
+ assert_userland_view controller: :registrations, step: :choose_username
92
+
93
+ # ---------------- SIGNIFICANT TEST -------------------
94
+ # You cannot register a username that is already taken.
95
+ # -----------------------------------------------------
96
+ assert_text 'username already exists'
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Booth
4
+ module Testing
5
+ module Userland
6
+ module SessionsManageBehavior
7
+ include ::Booth::Logging
8
+
9
+ def run(button_name:, template:)
10
+ before_test&.call
11
+
12
+ create_and_onboard(username: 'alice')
13
+ virtual_authenticators.create
14
+ register_new_passkey(username: 'alice')
15
+
16
+ using_session(:"second_browser_#{button_name}") do
17
+ virtual_authenticators.clone_from_other_session
18
+ login_with_passkey(username: 'alice')
19
+
20
+ visit_namespaced controller: :sessions, action: :index
21
+
22
+ assert_userland_view controller: :sessions, step: :index
23
+ click_on button_name
24
+
25
+ # ----------------- SIGNIFICANT TEST -------------------
26
+ # Revoking another sessions keeps the current one alive.
27
+ # ------------------------------------------------------
28
+ assert_logged_in username: 'alice'
29
+ end
30
+
31
+ visit current_path
32
+
33
+ # ----------- SIGNIFICANT TEST ---------------
34
+ # A revoked session is not logged in any more.
35
+ # --------------------------------------------
36
+ assert_logged_out
37
+
38
+ # Login on first device again and revoke all others
39
+ virtual_authenticators.refresh_from_other_session
40
+ login_with_passkey(username: 'alice')
41
+
42
+ visit_namespaced controller: :sessions, action: :index
43
+
44
+ assert_userland_view controller: :sessions, step: :index
45
+ travel 21.minutes
46
+ visit_namespaced controller: :sessions, action: :index
47
+
48
+ assert_userland_view controller: :sessions, step: :index
49
+ click_on button_name
50
+
51
+ assert_userland_view controller: :sessions, step: template
52
+ click_on :authenticate
53
+
54
+ assert_userland_view controller: :sessions, step: :index
55
+
56
+ using_session(:"second_browser_#{button_name}") do
57
+ visit_namespaced controller: :sessions, action: :index
58
+
59
+ # ----------- SIGNIFICANT TEST ---------------
60
+ # A revoked session is not logged in any more.
61
+ # --------------------------------------------
62
+ assert_logged_out
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'sessions_manage_behavior'
4
+
5
+ module Booth
6
+ module Testing
7
+ module Userland
8
+ class SessionsRevokeAllOthers < ::Booth::Testing::IncorporationTestCase
9
+ include SessionsManageBehavior
10
+
11
+ def call
12
+ run(button_name: :revoke_all_others, template: :enter_webauth_to_destroy_all_others)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'sessions_manage_behavior'
4
+
5
+ module Booth
6
+ module Testing
7
+ module Userland
8
+ class SessionsRevokeOne < ::Booth::Testing::IncorporationTestCase
9
+ include SessionsManageBehavior
10
+
11
+ def call
12
+ run(button_name: :revoke, template: :enter_webauth_to_destroy)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'support/shortcuts/create_and_onboard'
4
+ require_relative 'support/shortcuts/register_new_passkey'
5
+ require_relative 'support/shortcuts/login_with_passkey'
6
+
7
+ require_relative 'userland/login_remotely'
8
+ require_relative 'userland/onboarding_first_time'
9
+ require_relative 'userland/onboarding_to_reset_passkeys'
10
+ require_relative 'userland/registration_with_passkey'
11
+ require_relative 'userland/registration_without_passkey'
12
+ require_relative 'userland/sessions_manage_behavior'
13
+ require_relative 'userland/sessions_revoke_all_others'
14
+ require_relative 'userland/sessions_revoke_one'
15
+
16
+ module Booth
17
+ module Testing
18
+ module Userland
19
+ SCENARIOS = [
20
+ ::Booth::Testing::Userland::RegistrationWithPasskey,
21
+ ::Booth::Testing::Userland::RegistrationWithoutPasskey,
22
+ ::Booth::Testing::Userland::OnboardingFirstTime,
23
+ ::Booth::Testing::Userland::OnboardingToResetPasskeys,
24
+ ::Booth::Testing::Userland::LoginRemotely,
25
+ ::Booth::Testing::Userland::SessionsRevokeOne,
26
+ ::Booth::Testing::Userland::SessionsRevokeAllOthers,
27
+ ].freeze
28
+
29
+ def self.scenarios
30
+ SCENARIOS.each do |klass|
31
+ yield ::Booth::Testing::Support::Scenario.new(klass)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,11 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
4
+ # Converts a Hash to a Data object.
2
5
  class ToStruct
3
- include ::Booth::MethodObject
6
+ include Calls
4
7
 
5
8
  param :attributes
6
9
 
7
10
  def call
8
- ::Struct.new(*attributes.keys, keyword_init: true).new(**attributes)
11
+ ::Data.define(*attributes.keys) do
12
+ def self.inspect
13
+ "<Data :#{members.join(', :')}>"
14
+ end
15
+ end.new(**attributes)
9
16
  end
10
17
  end
11
18
  end
@@ -1,10 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
2
4
  module Userland
3
5
  class ExtractFlashMessages
4
6
  include ::Booth::Logging
5
- include ::Booth::MethodObject
7
+ include Calls
6
8
 
9
+ # Where the message is extracted from.
10
+ # Example: `from: my_object` where my_object responds to #public_message
7
11
  option :from
12
+
13
+ # Where the message is passed on to.
14
+ # Example: `to: flash` where flash responds to #notice=
8
15
  option :to
9
16
 
10
17
  def call
@@ -13,10 +20,10 @@ module Booth
13
20
  return if from.public_message.blank?
14
21
 
15
22
  if from.success?
16
- debug { "Saving flash notice: #{from.public_message}" }
23
+ log { "Saving flash notice: #{from.public_message}" }
17
24
  to[:notice] = from.public_message
18
25
  else
19
- debug { "Saving flash alert: #{from.public_message}" }
26
+ log { "Saving flash alert: #{from.public_message}" }
20
27
  to[:alert] = from.public_message
21
28
  end
22
29
 
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
2
4
  module Userland
3
5
  module Logins
6
+ # This Action receives a proof of identity (i.e. username or passkey)
7
+ # and either asks for more, or logs you in.
4
8
  class Create
5
9
  include ::Booth::Concerns::Action
6
10
 
@@ -14,12 +18,10 @@ module Booth
14
18
 
15
19
  def transitions
16
20
  [
17
- ::Booth::Logins::Transitions::Create::ChooseUsername,
18
- ::Booth::Logins::Transitions::Create::EnterOtp,
19
- ::Booth::Logins::Transitions::Create::SkipRemotes,
20
- ::Booth::Logins::Transitions::Create::VerifyPassword,
21
- ::Booth::Logins::Transitions::Create::WebauthAuthenticationInitiation,
22
- ::Booth::Logins::Transitions::Create::WebauthAuthenticationVerification,
21
+ ::Booth::Userland::Logins::Transitions::Create::ChooseUsername,
22
+ ::Booth::Userland::Logins::Transitions::Create::SkipRemotes,
23
+ ::Booth::Userland::Logins::Transitions::Create::WebauthAuthenticationInitiation,
24
+ ::Booth::Userland::Logins::Transitions::Create::WebauthAuthenticationVerification
23
25
  ]
24
26
  end
25
27
  end
@@ -1,29 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
2
4
  module Userland
3
5
  module Logins
6
+ # This Action logs a user out.
4
7
  class Destroy
5
8
  include ::Booth::Concerns::Action
9
+ include ::Booth::Logging
6
10
 
7
- def call
11
+ def call # rubocop:disable Metrics/AbcSize
8
12
  request.must_be_delete!
9
13
  request.must_be_html!
10
14
 
11
15
  if request.authentication.logged_in?
12
16
  public_message = I18n.t('booth.successfully_logged_out')
13
- ::Booth::Audits::Register::Logout.call credential:,
14
- ip: request.ip,
15
- agent: request.agent
17
+ ::Booth::Core::Audit::Logout.call credential:, ip: request.ip, agent: request.agent
16
18
  end
17
19
 
18
20
  reset!
21
+ # cleanup_orphan_credential!
19
22
 
20
- Tron.success :logged_out, return_path: request.return_path,
21
- public_message:
23
+ Tron.success :logged_out, return_path: request.return_path, public_message:
22
24
  end
23
25
 
24
26
  private
25
27
 
26
28
  def credential
29
+ return unless request.authentication.credential_id
30
+
27
31
  @credential ||= ::Booth::Models::Credential.find(request.authentication.credential_id)
28
32
  end
29
33
 
@@ -31,6 +35,19 @@ module Booth
31
35
  request.storage.login.reset
32
36
  request.authentication.logout
33
37
  end
38
+
39
+ # If this is a newly registered Credential, you were only logged in into this browser.
40
+ # If you didn't add any Authenticators, you cannot log in again later.
41
+ # Now that you logged out, your Credential is disposable and the username can be freed.
42
+ # def cleanup_orphan_credential!
43
+ # return unless credential # Logged out in another browser window.
44
+ # return if credential.authenticators.any?
45
+
46
+ # credential.destroy
47
+ # rescue StandardError
48
+ # # The destruction is allowed to fail if there are tables referencing this credential.
49
+ # log { "Could not remove Authenticator-less Credential #{credential.id}" }
50
+ # end
34
51
  end
35
52
  end
36
53
  end
@@ -1,51 +1,49 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
2
4
  module Userland
5
+ # Actions in this namespace handle the login of a user.
3
6
  module Logins
7
+ # This Action shows a login form for entering proof of identity (e.g. username, OTP).
4
8
  class New
5
9
  include ::Booth::Concerns::Action
6
10
 
7
- def call
11
+ def call # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
8
12
  request.must_be_get!
9
13
  request.must_be_html!
10
14
 
11
15
  if already_logged_in?
12
16
  # Bail out early if it would conflict with another session.
13
- # Because this could lead to strong confusion for the end-user.
14
- ::Booth::Userland::Logins::Transitions::New::AlreadyLoggedIn.call(request:)
17
+ # Because this could lead to confusion for the end-user.
18
+ ::Booth::Userland::Logins::Transitions::New::AlreadyLoggedIn.call(scope:, request:)
15
19
 
16
20
  elsif storage.timed_out?
17
- # The browser cookie just destroyed itself.
18
- ::Booth::Userland::Logins::Transitions::New::TimedOut.call(request:)
21
+ # The browser cookie just destroyed itself due to age.
22
+ # Whether logged in, or not, now you're considered logged out.
23
+ ::Booth::Userland::Logins::Transitions::New::TimedOut.call(scope:, request:)
19
24
 
20
25
  elsif storage.credential_for_username.blank?
21
- # First of all, we need a username.
26
+ # At this point we're ready to show the login form, asking for a username.
27
+ # In order to log someone in, we would first need a username.
22
28
  # Without it we wouldn't know which credential to look for.
23
- ::Booth::Userland::Logins::Transitions::New::NoUsernameChosen.call(request:)
24
-
25
- elsif storage.credential_for_username.mode_first_time?
26
- # If that username is not initialized, bail out again.
27
- # This should be an informative site with remedy information.
28
- ::Booth::Userland::Logins::Transitions::New::ModeFirstTime.call(request:)
29
+ ::Booth::Userland::Logins::Transitions::New::NoUsernameChosen.call(scope:, request:)
29
30
 
30
31
  elsif remote_session_available?
31
32
  # If the user is logged in on another device,
32
33
  # suggest that that device is used as a remote to login here.
33
- ::Booth::Userland::Logins::Transitions::New::RemoteSessionAvailable.call(request:)
34
-
35
- elsif storage.credential_for_username.mode_username_and_password?
36
- ::Booth::Userland::Logins::Transitions::New::ModeUsernameAndPassword.call(request:)
34
+ # This is possible even without registered authenticators.
35
+ ::Booth::Userland::Logins::Transitions::New::RemoteSessionAvailable.call(scope:, request:)
37
36
 
38
- elsif storage.credential_for_username.mode_username_password_and_otp?
39
- ::Booth::Userland::Logins::Transitions::New::ModeUsernamePasswordAndOtp.call(request:)
40
-
41
- elsif storage.credential_for_username.mode_username_password_and_webauth?
42
- ::Booth::Userland::Logins::Transitions::New::ModeUsernamePasswordAndWebauth.call(request:)
43
-
44
- elsif storage.credential_for_username.mode_username_and_webauth?
45
- ::Booth::Userland::Logins::Transitions::New::ModeUsernameAndWebauth.call(request:)
37
+ elsif !storage.credential_for_username.registered_authenticators?
38
+ # If the username exists, but is not initialized, bail out.
39
+ # This could happen if the user self-registers, never adds a credential and logs out.
40
+ # This should be an informative site with remedy information.
41
+ # The remedy usually has the user go through an onboarding.
42
+ ::Booth::Userland::Logins::Transitions::New::MissingAuthenticators.call(scope:, request:)
46
43
 
47
44
  else
48
- raise 'Invalid Login State'
45
+ ::Booth::Userland::Logins::Transitions::New::ModeUsernameAndWebauth.call(scope:, request:)
46
+
49
47
  end
50
48
  end
51
49