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
@@ -1,38 +1,73 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
2
- module Logins
3
- module Transitions
4
- module Create
5
- class ChooseUsername
6
- include ::Booth::Concerns::Transition
7
-
8
- def self.applicable?(params:)
9
- params.dig :login, :username
10
- end
4
+ module Userland
5
+ module Logins
6
+ module Transitions
7
+ module Create
8
+ # Someone tells us with a POST as which username they would like to log in.
9
+ class ChooseUsername
10
+ include ::Booth::Concerns::Transition
11
11
 
12
- def call
13
- finding = ::Booth::Credentials::FindByUsername.call(username: username_param)
12
+ def self.applicable?(params:)
13
+ params.dig :login, :username
14
+ end
14
15
 
15
- finding.on_success do
16
- # Each time a username was entered, we destroy and recreate the Contest for this Credential.
17
- contest = ::Booth::Contests::SetForLogin.call(request:, credential_id: finding.credential.id).contest
18
- storage.contest_for_username = contest
19
- storage.credential_for_username = finding.credential
16
+ def call
17
+ do_find_record
18
+ .on_success { do_check_domain }
19
+ .on_success { do_check_scope }
20
+ .on_success { do_reset_remote }
20
21
  end
21
22
 
22
- # Whether the username is valid or not, we persist it in the cookie,
23
- # so that it can be shown again in the username text input.
24
- storage.username = finding.normalized_invalid_username
25
- finding
26
- end
23
+ private
27
24
 
28
- private
25
+ attr_accessor :credential
29
26
 
30
- def username_param
31
- params.require(:login).permit(:username)[:username]
32
- end
27
+ def do_find_record
28
+ finding = ::Booth::Core::Credentials::FindByUsername.call(username: username_param)
29
+
30
+ # Whether the username is valid or not, we persist it in the cookie,
31
+ # so that it can be shown again in the username text input.
32
+ storage.username = finding.normalized_invalid_username
33
+ return finding if finding.failure?
34
+
35
+ self.credential = finding.credential
36
+ Tron.success :record_found
37
+ end
38
+
39
+ def do_check_domain
40
+ ::Booth::Comparisons::Domain.call actual: request.host, expected: credential.domain
41
+ end
33
42
 
34
- def storage
35
- request.storage.login
43
+ def do_check_scope
44
+ ::Booth::Comparisons::Scope.call actual: scope, expected: credential.scope
45
+ end
46
+
47
+ def do_reset_remote
48
+ # Each time a username was entered, we re-create the Remote for that Credential.
49
+ # Usernames are secret until voluntarily or involutarily revealed to a hacker.
50
+ # This feature here could lead to a kind of DOS attack where an attacker enters
51
+ # the username again and again and the real user could not login remotely.
52
+ # But in that case - you want to know that there is a hacker - simply rename the user.
53
+ remote = ::Booth::Core::Remotes::SetForLogin.call(
54
+ credential_id: credential.id,
55
+ request:,
56
+ ).remote
57
+
58
+ storage.remote_for_username = remote
59
+ storage.credential_for_username = credential
60
+
61
+ Tron.success :username_found_and_remote_reset
62
+ end
63
+
64
+ def username_param
65
+ params.expect(login: [:username])[:username]
66
+ end
67
+
68
+ def storage
69
+ request.storage.login
70
+ end
36
71
  end
37
72
  end
38
73
  end
@@ -1,21 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
2
- module Logins
3
- module Transitions
4
- module Create
5
- class SkipRemotes
6
- include ::Booth::Concerns::Transition
4
+ module Userland
5
+ module Logins
6
+ module Transitions
7
+ module Create
8
+ class SkipRemotes
9
+ include ::Booth::Concerns::Transition
7
10
 
8
- def self.applicable?(params:)
9
- params[:skip_remotes].present?
10
- end
11
+ def self.applicable?(params:)
12
+ params[:skip_remotes].present?
13
+ end
11
14
 
12
- def call
13
- storage.skip_remotes
14
- Tron.success :skipping_remotes
15
- end
15
+ def call
16
+ storage.skip_remotes
17
+ Tron.success :skipping_remotes
18
+ end
16
19
 
17
- def storage
18
- request.storage.login
20
+ def storage
21
+ request.storage.login
22
+ end
19
23
  end
20
24
  end
21
25
  end
@@ -1,53 +1,59 @@
1
- module Booth
2
- module Logins
3
- module Transitions
4
- module Create
5
- class WebauthAuthenticationInitiation
6
- include ::Booth::Concerns::Transition
7
-
8
- def self.applicable?(params:)
9
- params[:webauth] && !params[:type]
10
- end
11
-
12
- def call
13
- do_check_stale_session
14
- .on_success { do_find_credential }
15
- .on_success { do_check_webauth }
16
- end
17
-
18
- # Helpers
19
-
20
- def do_check_stale_session
21
- return Tron.success :session_not_stale unless storage.timed_out?
22
-
23
- public_message = I18n.t('booth.login_timeout', lifespan_minutes: (storage.lifespan / 60))
24
- Tron.failure :stale_session, step: :enter_username,
25
- public_message:
26
- end
1
+ # frozen_string_literal: true
27
2
 
28
- def do_find_credential
29
- @credential = storage.credential_for_username
30
- return Tron.success :found_credential if @credential
31
-
32
- debug { 'I do not know the credential for this username' }
33
- Tron.failure :missing_credential
34
- end
35
-
36
- def do_check_webauth
37
- debug { 'Preparing webauth challenge...' }
38
- challenging = Booth::Credentials::WebauthChallenge.call(credential: @credential)
39
- result = Tron.success :webauth_for_you, public_json: challenging.options_for_get, http_status: :ok
40
- debug { "The challenge is #{challenging.challenge}" }
41
- storage.webauthn_challenge = challenging.challenge
42
- debug { "Responding with JSON: #{result.public_json}" }
43
- result
44
- end
45
-
46
- def storage
47
- request.storage.login
3
+ module Booth
4
+ module Userland
5
+ module Logins
6
+ module Transitions
7
+ module Create
8
+ class WebauthAuthenticationInitiation
9
+ include ::Booth::Concerns::Transition
10
+
11
+ def self.applicable?(params:)
12
+ params[:webauth] && !params[:handshake]&.key?(:type)
13
+ end
14
+
15
+ def call
16
+ do_check_stale_session
17
+ .on_success { do_find_credential }
18
+ .on_success { do_check_webauth }
19
+ end
20
+
21
+ # Helpers
22
+
23
+ def do_check_stale_session
24
+ return Tron.success :session_not_stale unless storage.timed_out?
25
+
26
+ public_message = I18n.t('booth.login_timeout',
27
+ lifespan_minutes: (storage.lifespan / 60))
28
+ Tron.failure :stale_session, step: :enter_username, public_message:
29
+ end
30
+
31
+ def do_find_credential
32
+ @credential = storage.credential_for_username
33
+ return Tron.success :found_credential if @credential
34
+
35
+ log { 'I do not know the credential for this username' }
36
+ Tron.failure :missing_credential
37
+ end
38
+
39
+ def do_check_webauth
40
+ log { 'Preparing webauth challenge...' }
41
+ challenging = Booth::Core::Credentials::WebauthChallenge.call(credential: @credential,
42
+ request:)
43
+ return Tron.failure(:no_registered_authenticators) if challenging.failure?
44
+
45
+ result = Tron.success :webauth_for_you, public_json: challenging.options_for_get,
46
+ http_status: :created
47
+ log { "The challenge is #{challenging.challenge}" }
48
+ storage.webauthn_challenge = challenging.challenge
49
+ log { "Responding with JSON: #{result.public_json}" }
50
+ result
51
+ end
52
+
53
+ def storage
54
+ request.storage.login
55
+ end
48
56
  end
49
-
50
- delegate :authentication, to: :request
51
57
  end
52
58
  end
53
59
  end
@@ -1,77 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ 65
1
4
  module Booth
2
- module Logins
3
- module Transitions
4
- module Create
5
- class WebauthAuthenticationVerification
6
- include ::Booth::Concerns::Transition
7
-
8
- def self.applicable?(params:)
9
- params[:webauth] && params[:type]
10
- end
5
+ module Userland
6
+ module Logins
7
+ module Transitions
8
+ module Create
9
+ class WebauthAuthenticationVerification
10
+ include ::Booth::Concerns::Transition
11
+
12
+ def self.applicable?(params:)
13
+ params[:webauth] && params[:handshake]&.key?(:type)
14
+ end
11
15
 
12
- def call
13
- do_check_stale_session
14
- .on_success { do_find_credential }
15
- .on_success { do_find_challenge }
16
- .on_success { do_check_webauth }
17
- end
16
+ def call
17
+ do_check_stale_session
18
+ .on_success { do_find_credential }
19
+ .on_success { do_find_challenge }
20
+ .on_success { do_check_webauth }
21
+ end
18
22
 
19
- private
23
+ private
20
24
 
21
- delegate :sudo, to: :request
25
+ delegate :sudo, to: :request
22
26
 
23
- # Helpers
27
+ # Helpers
24
28
 
25
- def do_check_stale_session
26
- return Tron.success :session_not_stale unless storage.timed_out?
29
+ def do_check_stale_session
30
+ return Tron.success :session_not_stale unless storage.timed_out?
27
31
 
28
- public_message = I18n.t('booth.login_timeout', lifespan_minutes: (storage.lifespan / 60))
29
- debug { public_message }
30
- Tron.failure :stale_session, step: :enter_username,
31
- public_message:
32
- end
32
+ public_message = I18n.t('booth.login_timeout',
33
+ lifespan_minutes: (storage.lifespan / 60))
34
+ log { public_message }
35
+ Tron.failure :stale_session, step: :enter_username,
36
+ public_message:
37
+ end
38
+
39
+ def do_find_credential
40
+ @credential = storage.credential_for_username
41
+ return Tron.success :found_credential if @credential
33
42
 
34
- def do_find_credential
35
- @credential = storage.password_authenticated_credential
36
- return Tron.success :found_credential if @credential
43
+ log { 'I do not know the credential for this username' }
44
+ Tron.failure :missing_credential
45
+ end
37
46
 
38
- @credential = storage.credential_for_username
39
- return Tron.success :found_credential if @credential
47
+ def do_find_challenge
48
+ return Tron.success :challenge_ongoing if storage.webauthn_challenge.present?
40
49
 
41
- debug { 'I do not know the credential for this username' }
42
- Tron.failure :missing_credential
43
- end
50
+ log { 'There is no corresponding challenge in the session' }
51
+ Tron.failure :no_session_challenge, public_json: {},
52
+ http_status: :unprocessable_entity
53
+ end
44
54
 
45
- def do_find_challenge
46
- return Tron.success :challenge_ongoing if storage.webauthn_challenge.present?
55
+ def do_check_webauth
56
+ verification = ::Booth::Core::Webauth::AuthenticationVerification.call(
57
+ request:,
58
+ credential_id: @credential.id,
59
+ challenge: storage.webauthn_challenge,
60
+ )
61
+ return verification if verification.failure?
47
62
 
48
- debug { 'There is no corresponding challenge in the session' }
49
- Tron.failure :no_session_challenge, public_json: {}, http_status: :unprocessable_entity
50
- end
63
+ # Don't let valid Onboarding links linger around when not needed.
64
+ @credential.onboarding&.destroy!
51
65
 
52
- def do_check_webauth
53
- verification = ::Booth::Webauth::AuthenticationVerification.call(
54
- request:,
55
- credential_id: @credential.id,
56
- challenge: storage.webauthn_challenge
57
- )
58
- return verification if verification.failure?
59
-
60
- # sudo.webauth! # Why was this up here as well? I forgot.
61
- if @credential.mode_username_and_webauth?
62
- ::Booth::Sessions::CreateAndLogin.call(credential: verification.credential, request:)
63
- elsif @credential.mode_username_password_and_webauth? && @credential == storage.password_authenticated_credential
64
- ::Booth::Sessions::CreateAndLogin.call(credential: verification.credential, request:)
65
- end
66
+ ::Booth::Core::Sessions::CreateAndLogin.call(domain: request.host,
67
+ scope:,
68
+ credential: verification.credential,
69
+ request:)
66
70
 
67
- sudo.webauth! # This also re-sudos after reset caused by potential login above.
71
+ sudo.webauth! # This also re-sudos after reset caused by potential login above.
68
72
 
69
- Tron.success :login_completed, public_json: {},
70
- http_status: :created
71
- end
73
+ Tron.success :login_completed, public_json: {}, http_status: :created
74
+ end
72
75
 
73
- def storage
74
- request.storage.login
76
+ def storage
77
+ request.storage.login
78
+ end
75
79
  end
76
80
  end
77
81
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
2
4
  module Userland
3
5
  module Logins
@@ -5,11 +7,10 @@ module Booth
5
7
  module New
6
8
  class AlreadyLoggedIn
7
9
  include ::Booth::Concerns::Transition
8
- include ::Booth::Userland::Logins::Transitions::New::Fallible
9
10
 
10
11
  def call
11
- debug { "Good, it looks like you've managed to login" }
12
- debug { "Sending you to #{request.return_path.inspect}" } if request.return_path
12
+ log { "Good, it looks like you've managed to login in scope #{request.scope}" }
13
+ log { "Sending you to #{request.return_path.inspect}" } if request.return_path
13
14
 
14
15
  Tron.success :cannot_login_because_already_logged_in, return_path: request.return_path
15
16
  end
@@ -1,8 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
2
4
  module Userland
3
5
  module Logins
4
6
  module Transitions
5
7
  module New
8
+ # A Concern shared by other Login Transitions.
9
+ # Convenience method for returning an error with common metadata.
6
10
  module Fallible
7
11
  extend ActiveSupport::Concern
8
12
 
@@ -1,16 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
2
4
  module Userland
3
5
  module Logins
4
6
  module Transitions
5
7
  module New
6
- class ModeUsernameAndPassword
8
+ class MissingAuthenticators
7
9
  include ::Booth::Concerns::Transition
8
10
  include ::Booth::Userland::Logins::Transitions::New::Fallible
9
11
 
10
12
  def call
11
- debug { 'I have your username and I only need your password' }
12
-
13
- fail_with :need_password, step: :enter_password
13
+ log { "Apparently we don't know any authenticators of this user" }
14
+ fail_with :no_authenticators, step: :no_authenticators
14
15
  end
15
16
  end
16
17
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
2
4
  module Userland
3
5
  module Logins
@@ -8,14 +10,14 @@ module Booth
8
10
  include ::Booth::Userland::Logins::Transitions::New::Fallible
9
11
 
10
12
  def call
11
- debug { 'I have your username and I only need your Webauth' }
13
+ log { 'I have your username and I only need your Webauth' }
12
14
 
13
- if storage.credential_for_username.registered_authenticator_ids.any?
14
- debug { 'I know that the user has an authenticator that is to be challenged' }
15
+ if storage.credential_for_username.registered_authenticators?
16
+ log { 'I know that the user has an authenticator that is to be challenged' }
15
17
  return fail_with(:need_webauth, step: :enter_webauth)
16
18
  end
17
19
 
18
- debug { "Apparently we don't know any authenticators of this user" }
20
+ log { "Apparently we don't know any authenticators of this user" }
19
21
  fail_with :no_authenticators, step: :no_authenticators
20
22
  end
21
23
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
2
4
  module Userland
3
5
  module Logins
@@ -8,7 +10,7 @@ module Booth
8
10
  include ::Booth::Userland::Logins::Transitions::New::Fallible
9
11
 
10
12
  def call
11
- debug { "I don't know of any valid username" }
13
+ log { "I don't know of any valid username" }
12
14
  fail_with :no_username_chosen, step: :enter_username
13
15
  end
14
16
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
2
4
  module Userland
3
5
  module Logins
@@ -6,9 +8,10 @@ module Booth
6
8
  class RemoteSessionAvailable
7
9
  include ::Booth::Concerns::Transition
8
10
  include ::Booth::Userland::Logins::Transitions::New::Fallible
11
+ include ::Booth::Logging
9
12
 
10
13
  def call
11
- # Prerequisites that were arranged by previous transitions.
14
+ # Checking prerequisites that were arranged by previous transitions.
12
15
  raise(::Booth::Errors::Internal, 'Expected a Credential in the cookie.') unless credential
13
16
 
14
17
  do_respond
@@ -17,22 +20,26 @@ module Booth
17
20
  private
18
21
 
19
22
  def do_respond
20
- if contest&.recently_responded?
21
- debug { 'Your Contest was solved on the other device. Logging you in...' }
23
+ if remote&.recently_responded?
24
+ log { 'Your Remote was solved on the other device. Logging you in...' }
22
25
  # Yes, it's unusual for a GET request to log you in.
23
- # We are polling this page and when the Contest was solved remotely, we log this user in.
24
- ::Booth::Sessions::CreateAndLogin.call(credential:, request:)
26
+ # We are polling this page and when the Remote was solved remotely,
27
+ # we log this user in.
28
+ ::Booth::Core::Sessions::CreateAndLogin.call(domain: request.host, scope:, credential:, request:)
25
29
 
26
- elsif contest&.recently_created?
27
- debug { 'If you like, you can try a remote device to log you in here.' }
28
- # Having entered a username earlier, we can be sure to find a corresponding Contest.
29
- expected_code = contest.formatted_code
30
+ elsif remote&.recently_created?
31
+ log { 'If you like, you can try a remote device to log you in here.' }
32
+ # Having entered a username earlier, we can be sure to find a corresponding Remote.
33
+ expected_code = remote.formatted_code
30
34
 
31
- fail_with :contest_available,
35
+ fail_with :remote_available,
32
36
  step: :remote_session_available,
33
37
  expected_code:
34
38
  else
35
- fail_with :invalid_contest,
39
+ log { 'The remote was not recently created.' }
40
+ # The user normally doesn't end up here, because there is a timeout on cookies.
41
+ # But we still need to check it in case one timeout is smaller than the other.
42
+ fail_with :invalid_remote,
36
43
  step: :remote_session_expired
37
44
  end
38
45
  end
@@ -41,8 +48,8 @@ module Booth
41
48
  request.storage.login.credential_for_username
42
49
  end
43
50
 
44
- def contest
45
- request.storage.login.contest_for_username
51
+ def remote
52
+ request.storage.login.remote_for_username
46
53
  end
47
54
  end
48
55
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Booth
2
4
  module Userland
3
5
  module Logins
@@ -8,7 +10,7 @@ module Booth
8
10
  include ::Booth::Userland::Logins::Transitions::New::Fallible
9
11
 
10
12
  def call
11
- debug { 'Stale session detected, you need to start all over again.' }
13
+ log { 'Stale session detected, you need to start all over again.' }
12
14
 
13
15
  fail_with :no_username_chosen, step: :enter_username,
14
16
  public_message: