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,43 +0,0 @@
1
- module Booth
2
- module Audits
3
- module Register
4
- class CorrectPassword
5
- include ::Booth::MethodObject
6
- include ::Booth::Logging
7
-
8
- option :credential
9
- option :ip
10
- option :agent
11
-
12
- def call
13
- ::Booth::Models::Audit.transaction do
14
- register_attempt!
15
- clear_failed_attempts!
16
- end
17
-
18
- nil
19
- end
20
-
21
- private
22
-
23
- def register_attempt!
24
- debug { "Auditing correct password for credential `#{credential.id}` and IP `#{ip}`" }
25
-
26
- ::Booth::Models::Audit.create! credential:,
27
- ip:,
28
- agent:,
29
- event: :entered_correct_password
30
- end
31
-
32
- def clear_failed_attempts!
33
- debug { "Redeeming failed password attempts for credential `#{credential.id}` and IP `#{ip}`" }
34
-
35
- ::Booth::Models::Audit.where(credential:)
36
- .where(ip:)
37
- .where(event: :entered_wrong_password)
38
- .update_all(deleted_at: Time.current) # rubocop:disable Rails/SkipsModelValidations
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,22 +0,0 @@
1
- module Booth
2
- module Audits
3
- module Register
4
- class Logout
5
- include ::Booth::MethodObject
6
-
7
- option :credential
8
- option :ip
9
- option :agent
10
-
11
- def call
12
- ::Booth::Models::Audit.create! credential: credential,
13
- ip: ip,
14
- agent: agent,
15
- event: :logout
16
-
17
- nil
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,22 +0,0 @@
1
- module Booth
2
- module Audits
3
- module Register
4
- class RequestedPasswordReset
5
- include ::Booth::MethodObject
6
-
7
- option :credential
8
- option :ip
9
- option :agent
10
-
11
- def call
12
- ::Booth::Models::Audit.create! credential: credential,
13
- ip: ip,
14
- agent: agent,
15
- event: :requested_password_reset
16
-
17
- nil
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,22 +0,0 @@
1
- module Booth
2
- module Audits
3
- module Register
4
- class WrongOtp
5
- include ::Booth::MethodObject
6
-
7
- option :credential
8
- option :ip
9
- option :agent
10
-
11
- def call
12
- ::Booth::Models::Audit.create! credential: credential,
13
- ip: ip,
14
- agent: agent,
15
- event: :entered_wrong_otp
16
-
17
- nil
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,25 +0,0 @@
1
- module Booth
2
- module Audits
3
- module Register
4
- class WrongPassword
5
- include ::Booth::MethodObject
6
- include ::Booth::Logging
7
-
8
- option :credential
9
- option :ip
10
- option :agent
11
-
12
- def call
13
- debug { 'Auditing wrong password...' }
14
-
15
- ::Booth::Models::Audit.create! credential: credential,
16
- ip: ip,
17
- agent: agent,
18
- event: :entered_wrong_password
19
-
20
- nil
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,34 +0,0 @@
1
- module Booth
2
- module Authenticators
3
- class Confirm
4
- include ::Booth::MethodObject
5
- include ::Booth::Logging
6
-
7
- option :authenticator
8
- option :sign_count
9
-
10
- def call
11
- raise 'Authenticator is already confirmed' if authenticator.confirmed_at
12
-
13
- debug { 'Confirming Authenticator...' }
14
- authenticator.transaction do
15
- authenticator.update! sign_count: sign_count,
16
- confirmed_at: Time.current
17
-
18
- authenticator.credential.update! mode: new_mode
19
- end
20
-
21
- Tron.success :confirmation_successful
22
- rescue ActiveRecord::ActiveRecordError => e
23
- error { e }
24
- Tron.failure :confirmation_failed
25
- end
26
-
27
- private
28
-
29
- def new_mode
30
- ::Booth::Authenticators::CredentialModeAfterConfirmation.call(authenticator:)
31
- end
32
- end
33
- end
34
- end
@@ -1,25 +0,0 @@
1
- module Booth
2
- module Authenticators
3
- class CredentialModeAfterConfirmation
4
- include ::Booth::MethodObject
5
-
6
- option :authenticator
7
-
8
- def call
9
- return :username_and_webauth if credential.mode_username_and_webauth?
10
- return :username_password_and_webauth if credential.mode_username_password_and_webauth?
11
-
12
- return :username_and_webauth if credential.mode_first_time? &&
13
- authenticator.supports_user_verification?
14
-
15
- return :username_password_and_webauth if credential.mode_username_and_password? ||
16
- credential.mode_username_password_and_otp?
17
-
18
- raise "Cannot add webauth for credential #{credential.id} with mode #{credential.mode} " \
19
- "and authenticator #{authenticator.id} (user verification: #{authenticator.supports_user_verification?})"
20
- end
21
-
22
- delegate :credential, to: :authenticator
23
- end
24
- end
25
- end
@@ -1,19 +0,0 @@
1
- module Booth
2
- module Authenticators
3
- class Step
4
- include ::Booth::MethodObject
5
-
6
- param :authenticator
7
-
8
- def call
9
- return :register if authenticator.device_id.blank? ||
10
- authenticator.public_key.blank? ||
11
- authenticator.sign_count.blank?
12
- return :choose_nickname if authenticator.nickname.blank?
13
- return :confirm if authenticator.confirmed_at.blank?
14
-
15
- :completed
16
- end
17
- end
18
- end
19
- end
@@ -1,36 +0,0 @@
1
- module Booth
2
- module Contests
3
- class Get
4
- include ::Booth::Logging
5
- include ::Booth::MethodObject
6
-
7
- option :credential_id
8
-
9
- def call
10
- return Tron.failure :contest_not_found unless contest
11
-
12
- Tron.success :found_recent_contest,
13
- formatted_code: contest.formatted_code,
14
- normalized_code: contest.code,
15
- reason: contest.reason.to_sym,
16
- ip: contest.ip,
17
- agent: contest.agent.presence,
18
- location: contest.location.presence,
19
- recently_responded: contest.recently_responded?,
20
- browser_name: contest.browser_name,
21
- platform_name: contest.platform_name,
22
- browser_image_path: contest.browser_image_path,
23
- platform_image_path: contest.platform_image_path
24
- end
25
-
26
- private
27
-
28
- def contest
29
- return @contest if defined?(@contest)
30
-
31
- @contest = ::Booth::Models::Contest.recently_created_scope
32
- .find_by(credential_id:)
33
- end
34
- end
35
- end
36
- end
@@ -1,78 +0,0 @@
1
- module Booth
2
- module Contests
3
- class Respond
4
- include ::Booth::Logging
5
- include ::Booth::MethodObject
6
-
7
- option :scope
8
- option :contest
9
- option :request
10
-
11
- def call
12
- do_find_contest
13
- .on_success { do_check_timeout }
14
- .on_success { do_check_scope }
15
- .on_success { do_check_already_responded }
16
- .on_success { do_check_code_syntax }
17
- .on_success { do_respond }
18
- end
19
-
20
- private
21
-
22
- delegate :credential, to: :contest, private: true
23
-
24
- def do_find_contest
25
- return Tron.success :contest_exists if contest
26
-
27
- Tron.failure :missing_contest
28
- end
29
-
30
- def do_check_timeout
31
- return Tron.success :contested_recently if contest.recently_created?
32
-
33
- debug { 'This contest timed out' }
34
- Tron.failure :contest_timed_out,
35
- lifespan: contest.lifespan,
36
- public_message: I18n.t('booth.contest_timed_out', lifespan_minutes: contest.lifespan.seconds / 60)
37
- end
38
-
39
- def do_check_scope
40
- ::Booth::Syntaxes::ScopeComparison.call this: scope, that: credential.scope
41
- end
42
-
43
- def do_check_already_responded
44
- return Tron.success :ok_waiting_for_response if contest.responded_at.blank?
45
-
46
- debug { "This contest has already been responded to #{contest.responded_at.inspect}" }
47
- Tron.failure :already_responded, public_message: I18n.t('booth.already_responded_to_contest')
48
- end
49
-
50
- def do_check_code_syntax
51
- check = ::Booth::Syntaxes::ContestCode.call(code_param)
52
-
53
- check.on_success do
54
- @normalized_code = check.normalized_contest_code
55
- end
56
-
57
- check
58
- end
59
-
60
- def do_respond
61
- return Tron.failure :no_response_needed unless contest.reason.to_sym == :login
62
-
63
- if @normalized_code == contest.code
64
- debug { "The code #{@normalized_code} was accepted, persisting positive response..." }
65
- contest.update!(responded_at: Time.current)
66
- return Tron.success :response_code_accepted,
67
- public_message: I18n.t('booth.contest_response_accepted')
68
- end
69
-
70
- Tron.failure :wrong_code, public_message: I18n.t('booth.wrong_response_code')
71
- end
72
-
73
- def code_param
74
- request.params.require(:response).permit(:code)[:code]
75
- end
76
- end
77
- end
78
- end
@@ -1,28 +0,0 @@
1
- module Booth
2
- module Contests
3
- class SetForLogin
4
- include ::Booth::Logging
5
- include ::Booth::MethodObject
6
-
7
- option :credential_id
8
- option :request
9
-
10
- def call
11
- contest = nil
12
-
13
- ::Booth::Models::ApplicationRecord.transaction do
14
- ::Booth::Models::Contest.where(credential_id:).delete_all
15
-
16
- contest = ::Booth::Models::Contest.create!(
17
- credential_id:,
18
- reason: :login,
19
- ip: request.ip,
20
- agent: request.agent
21
- )
22
- end
23
-
24
- Tron.success :contest_created, contest:
25
- end
26
- end
27
- end
28
- end
@@ -1,46 +0,0 @@
1
- module Booth
2
- module Cooldowns
3
- class DistanceOfTime
4
- include ::Booth::MethodObject
5
-
6
- option :from
7
- option :till
8
-
9
- def call
10
- result = []
11
- result.push("#{distance_in_hours} h") if show_hours?
12
- result.push("#{distance_in_minutes} min") if show_minutes?
13
- result.push("#{distance_in_seconds} s") if show_seconds?
14
- result.join(' ')
15
- end
16
-
17
- def show_hours?
18
- distance_in_hours.positive?
19
- end
20
-
21
- def show_minutes?
22
- return false if (((till - from).abs % 3600) / 60) < 1
23
-
24
- distance_in_minutes.nonzero?
25
- end
26
-
27
- def show_seconds?
28
- return true if distance_in_seconds < 60
29
-
30
- distance_in_hours.zero? && distance_in_minutes.zero?
31
- end
32
-
33
- def distance_in_hours
34
- ((till - from).abs / 3600).floor
35
- end
36
-
37
- def distance_in_minutes
38
- (((till - from).abs % 3600) / 60).round
39
- end
40
-
41
- def distance_in_seconds
42
- (till - from).abs.round
43
- end
44
- end
45
- end
46
- end
@@ -1,22 +0,0 @@
1
- module Booth
2
- module Cooldowns
3
- class Otp
4
- include ::Booth::MethodObject
5
-
6
- option :credential
7
-
8
- def call
9
- ::Booth::Cooldowns::Strategies::Global.call scope:,
10
- max_attempts: 10
11
- end
12
-
13
- private
14
-
15
- def scope
16
- ::Booth::Models::Audit.visible_scope
17
- .event_entered_wrong_otp
18
- .where(credential:)
19
- end
20
- end
21
- end
22
- end
@@ -1,44 +0,0 @@
1
- module Booth
2
- module Cooldowns
3
- class Password
4
- include ::Booth::MethodObject
5
-
6
- option :ip
7
- option :credential
8
-
9
- def call
10
- # No limit for logins where hardware tokens are required.
11
- return Tron.success :cool_for_webauth if credential.mode_username_and_webauth?
12
- return Tron.success :cool_for_password_and_webauth if credential.mode_username_password_and_webauth?
13
-
14
- if credential.mode_username_password_and_otp?
15
- do_check_exponentially
16
- else
17
- do_check_globally
18
- end
19
- end
20
-
21
- private
22
-
23
- def do_check_exponentially
24
- ::Booth::Cooldowns::Strategies::Exponential.call scope: base_scope
25
- end
26
-
27
- def do_check_globally
28
- ::Booth::Cooldowns::Strategies::Global.call scope: ip_range_scope, max_attempts: 10
29
- end
30
-
31
- # Scopes
32
-
33
- def ip_range_scope
34
- base_scope.where('ip << ?', "#{ip}/24")
35
- end
36
-
37
- def base_scope
38
- ::Booth::Models::Audit.visible_scope
39
- .event_entered_wrong_password
40
- .where(credential:)
41
- end
42
- end
43
- end
44
- end
@@ -1,24 +0,0 @@
1
- module Booth
2
- module Cooldowns
3
- # Throttles how often you can generate a password reset link.
4
- # Note that this does not reveal whether we know the email address or not,
5
- # because it throttles the attempts per Credential (i.e. username).
6
- class PasswordReset
7
- include ::Booth::MethodObject
8
-
9
- option :credential
10
-
11
- def call
12
- ::Booth::Cooldowns::Strategies::Exponential.call scope:
13
- end
14
-
15
- private
16
-
17
- def scope
18
- ::Booth::Models::Audit.visible_scope
19
- .event_requested_password_reset
20
- .where(credential:)
21
- end
22
- end
23
- end
24
- end
@@ -1,82 +0,0 @@
1
- module Booth
2
- module Cooldowns
3
- module Strategies
4
- class Exponential
5
- include ::Booth::MethodObject
6
- include ::Booth::Logging
7
-
8
- option :scope
9
-
10
- def call
11
- return limit_not_yet_reached! if seconds_to_wait.zero?
12
-
13
- limit_reached!
14
- end
15
-
16
- private
17
-
18
- def limit_reached!
19
- debug { "Wait #{seconds_to_wait}/#{waiting_period} sec for #{number_of_incidents} incidents" }
20
- public_message = I18n.t('booth.try_again_cooldown', distance_of_time_until_cooldown:)
21
-
22
- ::Booth::Cooldowns::Strategies::Result.failure(
23
- public_message:,
24
- attempts_left: 999_999,
25
- cooldown_at:,
26
- number_of_incidents:
27
- )
28
- end
29
-
30
- def limit_not_yet_reached!
31
- debug { 'No need to wait' }
32
-
33
- ::Booth::Cooldowns::Strategies::Result.success(
34
- public_message: nil,
35
- attempts_left: 999_999,
36
- number_of_incidents:
37
- )
38
- end
39
-
40
- # Calculation Helpers
41
-
42
- def cooldown_at
43
- seconds_to_wait.from_now
44
- end
45
-
46
- def seconds_to_wait
47
- return 0 unless newest_timestamp
48
-
49
- candidate = newest_timestamp.to_i + waiting_period - Time.current.to_i
50
- return 0 if candidate.negative?
51
-
52
- candidate
53
- end
54
-
55
- def waiting_period
56
- return 2.years if number_of_incidents > 9
57
-
58
- # This effectively implies less than 10 attempts.
59
- (5**number_of_incidents).seconds
60
- end
61
-
62
- # Queries
63
-
64
- def number_of_incidents
65
- @number_of_incidents ||= scope.count
66
- end
67
-
68
- def newest_timestamp
69
- return @newest_timestamp if defined? @newest_timestamp
70
-
71
- @newest_timestamp = scope.maximum(:created_at)
72
- end
73
-
74
- # Helpers
75
-
76
- def distance_of_time_until_cooldown
77
- ::Booth::Cooldowns::DistanceOfTime.call(from: Time.current, till: cooldown_at)
78
- end
79
- end
80
- end
81
- end
82
- end
@@ -1,62 +0,0 @@
1
- module Booth
2
- module Cooldowns
3
- module Strategies
4
- class Global
5
- include ::Booth::MethodObject
6
- include ::Booth::Logging
7
-
8
- option :scope
9
- option :max_attempts
10
-
11
- def call
12
- if number_of_incidents >= max_attempts
13
- limit_reached!
14
- else
15
- limit_not_yet_reached!
16
- end
17
- end
18
-
19
- private
20
-
21
- def limit_reached!
22
- debug { "Limited globally #{number_of_incidents}/#{max_attempts}" }
23
-
24
- ::Booth::Cooldowns::Strategies::Result.failure(
25
- public_message: I18n.t('booth.permanently_blocked'),
26
- attempts_left:,
27
- cooldown_at: nil,
28
- number_of_incidents:
29
- )
30
- end
31
-
32
- def limit_not_yet_reached!
33
- debug { "Not yet globally limited #{number_of_incidents}/#{max_attempts}" }
34
-
35
- ::Booth::Cooldowns::Strategies::Result.success(
36
- public_message:,
37
- attempts_left:,
38
- number_of_incidents:
39
- )
40
- end
41
-
42
- def public_message
43
- return if number_of_incidents.zero?
44
-
45
- if attempts_left == 1
46
- I18n.t 'booth.last_attempt'
47
- else
48
- I18n.t 'booth.attempts_left', attempts_left:
49
- end
50
- end
51
-
52
- def attempts_left
53
- max_attempts - number_of_incidents
54
- end
55
-
56
- def number_of_incidents
57
- @number_of_incidents ||= scope.count
58
- end
59
- end
60
- end
61
- end
62
- end
@@ -1,22 +0,0 @@
1
- module Booth
2
- module Cooldowns
3
- module Strategies
4
- # All strategies quack the same way.
5
- module Result
6
- def self.failure(number_of_incidents:, public_message:, cooldown_at:, attempts_left:)
7
- Tron.failure :hot, public_message:,
8
- cooldown_at:,
9
- attempts_left:,
10
- number_of_incidents:
11
- end
12
-
13
- def self.success(public_message:, number_of_incidents:, attempts_left:)
14
- Tron.success :cool, number_of_incidents:,
15
- cooldown_at: nil,
16
- public_message:,
17
- attempts_left:
18
- end
19
- end
20
- end
21
- end
22
- end