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,62 +0,0 @@
1
- module Booth
2
- module Userland
3
- module Passwords
4
- module Transitions
5
- module Update
6
- class ChoosePassword
7
- include ::Booth::Concerns::Transition
8
-
9
- def self.applicable?(params:)
10
- params.dig(:password, :new_password)
11
- end
12
-
13
- def call
14
- do_check_sudo
15
- .on_success { do_check_password }
16
- .on_success { do_remember_password }
17
- end
18
-
19
- private
20
-
21
- def do_check_sudo
22
- return Tron.success :still_has_sudo if sudo.password?
23
-
24
- Tron.failure :missing_sudo, public_message: I18n.t('booth.password_change_timeout',
25
- lifespan_minutes: (sudo.lifespan / 60))
26
- end
27
-
28
- def do_check_password
29
- @dummy_credential = ::Booth::Models::Credential.new(
30
- password: password_param,
31
- username: :dummy,
32
- allowed_modes: [:first_time]
33
- )
34
- return Tron.success :password_is_acceptable if @dummy_credential.valid?
35
-
36
- debug { 'The newly chosen password is not acceptable' }
37
- Tron.failure :invalid_password, public_message: @dummy_credential.errors.full_messages.to_sentence
38
- end
39
-
40
- def do_remember_password
41
- storage.password_digest = @dummy_credential.password_digest
42
-
43
- Tron.success :remembered_password
44
- end
45
-
46
- def password_param
47
- params.require(:password).permit(:new_password)[:new_password]
48
- end
49
-
50
- def storage
51
- request.storage.password
52
- end
53
-
54
- def sudo
55
- request.sudo
56
- end
57
- end
58
- end
59
- end
60
- end
61
- end
62
- end
@@ -1,82 +0,0 @@
1
- module Booth
2
- module Userland
3
- module Passwords
4
- module Transitions
5
- module Update
6
- class ConfirmPassword
7
- include ::Booth::Concerns::Transition
8
-
9
- def self.applicable?(params:)
10
- params.dig(:password, :password)
11
- end
12
-
13
- def call
14
- do_check_sudo
15
- .on_success { do_compare_password }
16
- .on_success { do_update_credential }
17
- end
18
-
19
- private
20
-
21
- def do_check_sudo
22
- return Tron.success :still_has_sudo if sudo.password?
23
-
24
- Tron.failure :missing_sudo, public_message: I18n.t('booth.password_change_timeout',
25
- lifespan_minutes: (sudo.lifespan / 60))
26
- end
27
-
28
- def do_compare_password
29
- @dummy_credential = ::Booth::Models::Credential.new(
30
- password_digest: storage.password_digest,
31
- password_confirmation: password_param,
32
- username: :dummy,
33
- allowed_modes: [:first_time]
34
- )
35
-
36
- if @dummy_credential.valid?
37
- debug { 'The password confirmation was typed in correctly' }
38
- return Tron.success :passwords_match
39
- end
40
-
41
- debug { 'The password confirmation did not match' }
42
- Tron.failure :passwords_dont_match, public_message: @dummy_credential.errors.full_messages.to_sentence
43
- end
44
-
45
- def do_update_credential
46
- credential = ::Booth::Models::Credential.find(authentication.credential_id)
47
-
48
- if credential.update(password: password_param)
49
- debug { "Successfully updated password of credential with ID #{credential.id}" }
50
- storage.reset
51
- storage.password_recently_changed!
52
-
53
- # Prolong sudo to give the user more time to revoke all other sessions.
54
- sudo.password!
55
- return Tron.success :password_updated
56
- end
57
-
58
- debug { "Could not persist new password for credential with ID #{credential.id}" }
59
- Tron.failure :persistence_failed, public_message: credential.errors.full_messages.to_sentence
60
- end
61
-
62
- def password_param
63
- params.require(:password).permit(:password)[:password]
64
- end
65
-
66
- def storage
67
- request.storage.password
68
- end
69
-
70
- def authentication
71
- request.authentication
72
- end
73
-
74
- def sudo
75
- request.sudo
76
- end
77
- end
78
- end
79
- end
80
- end
81
- end
82
- end
@@ -1,33 +0,0 @@
1
- module Booth
2
- module Userland
3
- module Passwords
4
- class Update
5
- include ::Booth::Concerns::Action
6
-
7
- def call
8
- request.must_be_patch!
9
- request.must_be_logged_in!
10
-
11
- ::Booth::Userland::Passwords::Guards::Manageable.call(credential:) { return _1 }
12
- ::Booth::Userland::Passwords::Guards::Sudo.call(request:, credential:) { return _1 }
13
-
14
- do_transition
15
- end
16
-
17
- private
18
-
19
- def credential
20
- @credential ||= ::Booth::Models::Credential.find(request.authentication.credential_id)
21
- end
22
-
23
- def transitions
24
- [
25
- ::Booth::Userland::Passwords::Transitions::Update::ChoosePassword,
26
- # TODO: Add ResetPassword so that you can change your mind when confirming
27
- ::Booth::Userland::Passwords::Transitions::Update::ConfirmPassword,
28
- ]
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,60 +0,0 @@
1
- module Booth
2
- module Userland
3
- module PersonalContests
4
- class Show
5
- include ::Booth::Concerns::Action
6
-
7
- def call
8
- request.must_be_get!
9
- request.must_be_html!
10
- request.must_be_logged_in!
11
-
12
- debug { 'You want to receive your personal contest...' }
13
-
14
- if contest.failure?
15
- debug { "This credential doesn't have any contest right now." }
16
- return Tron.success :not_contested, step: :not_contested
17
- end
18
-
19
- if contest.recently_responded
20
- debug { 'The current contest has already been responded to' }
21
- return Tron.success :contest_responded, step: :contest_solved
22
- end
23
-
24
- step = case contest.reason
25
- when :login then :remote_login
26
- when :support then :support_authentication
27
- else raise "Unknown contest reason: #{contest.reason}"
28
- end
29
-
30
- debug { 'You have a contest to respond to' }
31
- Tron.success :you_are_contested,
32
- ip: contest.ip,
33
- agent: contest.agent.presence,
34
- location: contest.location.presence,
35
- browser_name: contest.browser_name,
36
- platform_name: contest.platform_name,
37
- browser_image_path: contest.browser_image_path,
38
- platform_image_path: contest.platform_image_path,
39
- step:
40
- end
41
-
42
- private
43
-
44
- def credential
45
- return @credential if defined?(@credential)
46
-
47
- id = request.authentication.credential_id
48
- @credential = ::Booth::Models::Credential.find_by(id:)
49
- end
50
-
51
- def contest
52
- return unless credential
53
- return @contest if defined?(@contest)
54
-
55
- @contest = ::Booth::Contests::Get.call(credential_id: credential.id)
56
- end
57
- end
58
- end
59
- end
60
- end
@@ -1,48 +0,0 @@
1
- module Booth
2
- module Userland
3
- module Recoveries
4
- class Create
5
- include ::Booth::Concerns::Action
6
-
7
- def call
8
- request.must_be_post!
9
-
10
- do_remember_email
11
- .on_success { do_create_password_reset }
12
- end
13
-
14
- private
15
-
16
- delegate :username, to: :storage, private: true
17
-
18
- def do_remember_email
19
- storage.email = ::Booth::Syntaxes::Email.call(email_param).normalized_invalid_email
20
-
21
- Tron.success :remembered_email
22
- end
23
-
24
- def do_create_password_reset
25
- creation = ::Booth::Recoveries::Create.call(
26
- scope:,
27
- email: email_param,
28
- ip: request.ip
29
- )
30
-
31
- creation.on_success do
32
- storage.email = nil
33
- end
34
-
35
- creation
36
- end
37
-
38
- def storage
39
- request.storage.recovery
40
- end
41
-
42
- def email_param
43
- params.require(:recovery).permit(:email)[:email]
44
- end
45
- end
46
- end
47
- end
48
- end
@@ -1,35 +0,0 @@
1
- module Booth
2
- module Userland
3
- module Recoveries
4
- class New
5
- include ::Booth::Concerns::Action
6
-
7
- def call
8
- request.must_be_get!
9
- request.must_be_html!
10
-
11
- if already_logged_in?
12
- call_already_logged_in
13
- else
14
- Tron.failure :enter_email, step: :enter_email, email: storage.email
15
- end
16
- end
17
-
18
- private
19
-
20
- def call_already_logged_in
21
- debug { "Looks like you're already logged in" }
22
- Tron.success :logged_in, step: :already_logged_in
23
- end
24
-
25
- def already_logged_in?
26
- request.authentication.logged_in?
27
- end
28
-
29
- def storage
30
- request.storage.recovery
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,50 +0,0 @@
1
- module Booth
2
- module Userland
3
- module Sessions
4
- module Transitions
5
- module Destroy
6
- class EnterPassword
7
- include ::Booth::Concerns::Transition
8
-
9
- def self.applicable?(params:)
10
- !params[:revocation]
11
- end
12
-
13
- def call
14
- if sudo.password?
15
- if session_id_param
16
- debug { 'Having password sudo, revoking the desired session...' }
17
- return ::Booth::Sessions::Revoke.call credential_id: authentication.credential_id,
18
- session_id: session_id_param
19
- else
20
- debug { 'Having password sudo, revoking all other sessions...' }
21
- return ::Booth::Sessions::RevokeAllOthers.call credential_id: authentication.credential_id,
22
- surviving_session_id: authentication.session_id
23
- end
24
- end
25
-
26
- if session_id_param
27
- Tron.failure :need_sudo_to_destroy_session,
28
- step: :enter_password_to_destroy,
29
- session_id: session_id_param
30
- else
31
- Tron.failure :need_sudo_to_destroy_all_other_sessions,
32
- step: :enter_password_to_destroy_all_others
33
- end
34
- end
35
-
36
- def session_id_param
37
- # If params[:id] is a UUID, then it's an ID for a `Booth::Models::Session` in the DB.
38
- # If params[:id] is something else, then it's just a WebAuth Ceremony argument.
39
- ::Booth::Syntaxes::Uuid.call(request.params[:id], raise_if_invalid: false).uuid
40
- end
41
-
42
- delegate :authentication, to: :request
43
-
44
- delegate :sudo, to: :request
45
- end
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,83 +0,0 @@
1
- module Booth
2
- module Userland
3
- module Sessions
4
- module Transitions
5
- module Destroy
6
- class VerifyPassword
7
- include ::Booth::Concerns::Transition
8
-
9
- def self.applicable?(params:)
10
- params.dig(:revocation, :password)
11
- end
12
-
13
- def call
14
- do_find_credential
15
- .on_success { do_revoke_sessions }
16
- end
17
-
18
- private
19
-
20
- def do_find_credential
21
- @credential = ::Booth::Models::Credential.find_by(id: authentication.credential_id)
22
- return Tron.success :found_credential if @credential
23
-
24
- Tron.failure :missing_credential
25
- end
26
-
27
- def do_revoke_sessions
28
- checking = ::Booth::Credentials::PasswordAuthentication.call(
29
- credential: @credential,
30
- password: password_param,
31
- ip: request.ip,
32
- agent: request.agent
33
- )
34
-
35
- if checking.success?
36
- if session_id_param
37
- debug { 'Having password sudo, revoking the desired session...' }
38
- return ::Booth::Sessions::Revoke.call credential_id: authentication.credential_id,
39
- session_id: session_id_param
40
- else
41
- debug { 'Having password sudo, revoking all other sessions...' }
42
- return ::Booth::Sessions::RevokeAllOthers.call credential_id: authentication.credential_id,
43
- surviving_session_id: authentication.session_id
44
- end
45
- end
46
-
47
- public_message = checking.public_message if checking.respond_to?(:public_message)
48
-
49
- if session_id_param
50
- Tron.failure :need_sudo_to_destroy_session,
51
- step: :enter_password_to_destroy,
52
- session_id: session_id_param,
53
- public_message: public_message
54
- else
55
- Tron.failure :need_sudo_to_destroy_all_other_sessions,
56
- step: :enter_password_to_destroy_all_others,
57
- public_message: public_message
58
- end
59
- end
60
-
61
- def session_id_param
62
- # If params[:id] is a UUID, then it's an ID for a `Booth::Models::Session` in the DB.
63
- # If params[:id] is something else, then it's just a WebAuth Ceremony argument.
64
- ::Booth::Syntaxes::Uuid.call(request.params[:id], raise_if_invalid: false).uuid
65
- end
66
-
67
- def password_param
68
- request.params.require(:revocation).permit(:password)[:password]
69
- end
70
-
71
- def authentication
72
- request.authentication
73
- end
74
-
75
- def sudo
76
- request.sudo
77
- end
78
- end
79
- end
80
- end
81
- end
82
- end
83
- end
@@ -1,21 +0,0 @@
1
- module Booth
2
- module Userland
3
- module Webauths
4
- module Guards
5
- class Manageable
6
- include ::Booth::Logging
7
- include ::Booth::MethodObject
8
-
9
- option :credential
10
-
11
- def call
12
- return if ::Booth::Credentials::Modes::WebauthManageable.call(credential)
13
-
14
- debug { 'Webauth is not relevant to this credential' }
15
- yield Tron.failure :webauth_not_configurable, public_message: I18n.t('booth.webauth_unavailable')
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,68 +0,0 @@
1
- module Booth
2
- module Webauth
3
- class AuthenticationVerification
4
- include ::Booth::MethodObject
5
- include ::Booth::Logging
6
-
7
- option :request
8
- option :credential_id
9
- option :challenge
10
-
11
- def call
12
- raise 'this authenticator doesnt match the credential' if credential_id != authenticator.credential_id
13
-
14
- debug do
15
- "Verifying using challenge #{challenge.inspect} and public key #{authenticator.public_key.inspect} and sign count #{authenticator.sign_count.inspect}"
16
- end
17
- webauth.verify(
18
- challenge,
19
- public_key: authenticator.public_key,
20
- sign_count: authenticator.sign_count
21
- )
22
- debug { 'Response successfully verified' }
23
-
24
- authenticator.update!(sign_count: webauth.sign_count)
25
- sudo.webauth!
26
-
27
- Tron.success :webauth_authentication_verification_successful,
28
- credential: authenticator.credential,
29
- public_json: {},
30
- http_status: :created
31
- rescue WebAuthn::SignCountVerificationError => e
32
- # TODO
33
- raise 'implement me, counter differed, not too bad?'
34
- rescue WebAuthn::Error => e
35
- debug { "Response verification failed: #{e.message}" }
36
- # TODO: Audit but don't throttle?
37
- Tron.failure :webauth_failed, public_json: {},
38
- public_message: "Verification failed: #{e.message}",
39
- http_status: :unprocessable_entity
40
- rescue RuntimeError => e
41
- # This happens e.g. if the param[:id] does not match the param[:rawId]
42
- raise
43
- ensure
44
- sudo.webauthn_challenge = nil
45
- end
46
-
47
- private
48
-
49
- delegate :sudo, to: :request
50
-
51
- def webauth
52
- # The route `/things/123` overrides the `param[:id]` with 123.
53
- # But thankfully, webauth also sends the rawId, which is identical to the id.
54
- # So we override `param[:id]` back to what the webauth JS client sent in.
55
- # If we don't do this, the webauth ruby library will raise a RuntimeError,
56
- # complaining that the `id` does not match the `rawId`.
57
- params_with_correct_id = request.params.dup.merge(id: request.params[:rawId])
58
- ::WebAuthn::Credential.from_get(params_with_correct_id)
59
- end
60
-
61
- def authenticator
62
- device_id = Base64.strict_encode64(webauth.raw_id)
63
- @authenticator ||= ::Booth::Models::Authenticator.where(credential_id:)
64
- .find_by!(device_id:)
65
- end
66
- end
67
- end
68
- end
@@ -1,29 +0,0 @@
1
- module Booth
2
- module Webauth
3
- class DemandUserVerification
4
- include ::Booth::MethodObject
5
-
6
- option :credential
7
- option :force, default: -> { false }
8
-
9
- def call
10
- !!why?
11
- end
12
-
13
- private
14
-
15
- def why?
16
- return :forced if force.present?
17
-
18
- # From scratch, you only have a username.
19
- # Adding webauth in that situation would mean passwordless login.
20
- return :first_time_passwordless if credential.mode_first_time?
21
-
22
- # Passwordless always requires verification
23
- return :passwordless if credential.mode_username_and_webauth?
24
-
25
- false
26
- end
27
- end
28
- end
29
- end
@@ -1,46 +0,0 @@
1
- module Booth
2
- module Webauth
3
- class OptionsForCreate
4
- include ::Booth::MethodObject
5
-
6
- option :webauthn_id
7
- option :username
8
- option :requires_user_verification
9
- option :device_ids_to_exclude, default: -> {}
10
-
11
- def call
12
- verify_setup
13
-
14
- ::WebAuthn::Credential.options_for_create(
15
- user: {
16
- id: webauthn_id,
17
- name: username
18
- # Also supports `display_name: "..."`
19
- },
20
- authenticator_selection: { user_verification: },
21
- # Use the following instead, if you want to force device-internal authenticators and forbid USB etc.
22
- # authenticator_selection: { user_verification:, authenticator_attachment: 'platform' },
23
- exclude: device_ids_to_exclude
24
- )
25
- end
26
-
27
- private
28
-
29
- # See https://caniuse.com/mdn-api_publickeycredentialrequestoptions_userverification
30
- # Can be turned off with `:discouraged`
31
- def user_verification
32
- requires_user_verification ? :required : :discouraged
33
- end
34
-
35
- def requires_user_verification?
36
- !!requires_user_verification
37
- end
38
-
39
- def verify_setup
40
- return if WebAuthn.configuration.rp_name.present?
41
-
42
- raise ::Booth::Errors::MissingRelyingParty
43
- end
44
- end
45
- end
46
- end
@@ -1,29 +0,0 @@
1
- module Booth
2
- module Webauth
3
- class OptionsForGet
4
- include ::Booth::MethodObject
5
-
6
- option :allowed_device_ids
7
- option :requires_user_verification
8
-
9
- def call
10
- WebAuthn::Credential.options_for_get(
11
- allow: allowed_device_ids,
12
- user_verification: user_verification_value
13
- )
14
- end
15
-
16
- private
17
-
18
- # See https://caniuse.com/mdn-api_publickeycredentialrequestoptions_userverification
19
- # Can be turned off with `:discouraged`
20
- def user_verification_value
21
- requires_user_verification ? :required : :discouraged
22
- end
23
-
24
- def requires_user_verification?
25
- !!requires_user_verification
26
- end
27
- end
28
- end
29
- end
@@ -1,20 +0,0 @@
1
- # Don't change anything in this file.
2
-
3
- class CreateBoothModeTypes < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
4
- def up
5
- execute <<-SQL
6
- CREATE TYPE booth_credential_mode AS ENUM
7
- ('first_time',
8
- 'username_and_password',
9
- 'username_password_and_otp',
10
- 'username_password_and_webauth',
11
- 'username_and_webauth');
12
- SQL
13
- end
14
-
15
- def down
16
- execute <<-SQL
17
- DROP TYPE booth_credential_mode;
18
- SQL
19
- end
20
- end