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,73 +0,0 @@
1
- # Copyright 2018 Bukowskis https://github.com/bukowskis/method_object
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining
4
- # a copy of this software and associated documentation files (the
5
- # "Software"), to deal in the Software without restriction, including
6
- # without limitation the rights to use, copy, modify, merge, publish,
7
- # distribute, sublicense, and/or sell copies of the Software, and to
8
- # permit persons to whom the Software is furnished to do so, subject to
9
- # the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be
12
- # included in all copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
-
22
- module Booth
23
- module MethodObject
24
- def self.included(base)
25
- base.extend Dry::Initializer
26
- base.extend ClassMethods
27
- base.send(:private_class_method, :new)
28
- end
29
-
30
- module ClassMethods
31
- def call(*args, **kwargs, &)
32
- __check_for_unknown_options(*args, **kwargs)
33
-
34
- if kwargs.empty?
35
- # Preventing `Passing the keyword argument as the last hash parameter is deprecated`
36
- new(*args).call(&)
37
- else
38
- new(*args, **kwargs).call(&)
39
- end
40
- end
41
-
42
- # Overriding the implementation of `#param` in the `dry-initializer` gem.
43
- # Because of the positioning of multiple params, params can never be omitted in a method object.
44
- def param(name, type = nil, **opts, &)
45
- raise ArgumentError, "Default value for param not allowed - #{name}" if opts.key? :default
46
- raise ArgumentError, "Optional params not supported - #{name}" if opts.fetch(:optional, false)
47
-
48
- super
49
- end
50
-
51
- def __check_for_unknown_options(*args, **kwargs)
52
- return if __defined_options.empty?
53
-
54
- # Checking params
55
- opts = args.drop(__defined_params.length).first || kwargs
56
- raise ArgumentError, "Unexpected argument #{opts}" unless opts.is_a? Hash
57
-
58
- # Checking options
59
- unknown_options = opts.keys - __defined_options
60
- message = "Key(s) #{unknown_options} not found in #{__defined_options}"
61
- raise KeyError, message if unknown_options.any?
62
- end
63
-
64
- def __defined_options
65
- dry_initializer.options.map(&:source)
66
- end
67
-
68
- def __defined_params
69
- dry_initializer.params.map(&:source)
70
- end
71
- end
72
- end
73
- end
data/lib/booth/mode.rb DELETED
@@ -1,22 +0,0 @@
1
- module Booth
2
- module Mode
3
- def self.find(symbol)
4
- return if symbol.to_s == 'first_time'
5
-
6
- all.detect { _1.id.to_s == symbol.to_s } || raise("Unknown Booth Mode: #{symbol.inspect}")
7
- end
8
-
9
- def self.wrap(input)
10
- Array(input).map { find(_1) }.sort
11
- end
12
-
13
- def self.all
14
- [
15
- ::Booth::Modes::UsernameAndWebauth, # Recommended for convenience
16
- ::Booth::Modes::UsernamePasswordAndWebauth, # Most secure
17
- ::Booth::Modes::UsernamePasswordAndOtp, # If you don't have a hardware key
18
- ::Booth::Modes::UsernameAndPassword, # Discouraged
19
- ]
20
- end
21
- end
22
- end
@@ -1,50 +0,0 @@
1
- module Booth
2
- module Models
3
- module Concerns
4
- module Modeable
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- enum mode: { first_time: 'first_time',
9
- username_and_password: 'username_and_password',
10
- username_password_and_otp: 'username_password_and_otp',
11
- username_password_and_webauth: 'username_password_and_webauth',
12
- username_and_webauth: 'username_and_webauth' },
13
- _prefix: true
14
-
15
- validates :mode, presence: true, inclusion: { in: modes.keys }
16
- validates :mode, inclusion: { in: -> { ['first_time'] + _1.allowed_modes } }
17
-
18
- before_validation :ensure_mode
19
- end
20
-
21
- def requires_user_verification?
22
- # Passwordless login should always require user presence
23
- !mode_username_password_and_webauth?
24
- end
25
-
26
- def allows_mode_username_and_password?
27
- allowed_modes.include?('username_and_password')
28
- end
29
-
30
- def allows_mode_username_password_and_otp?
31
- allowed_modes.include?('username_password_and_otp')
32
- end
33
-
34
- def allows_mode_username_password_and_webauth?
35
- allowed_modes.include?('username_password_and_webauth')
36
- end
37
-
38
- def allows_mode_username_and_webauth?
39
- allowed_modes.include?('username_and_webauth')
40
- end
41
-
42
- private
43
-
44
- def ensure_mode
45
- self.mode ||= 'first_time'
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,37 +0,0 @@
1
- module Booth
2
- module Models
3
- module Concerns
4
- module Otpable
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- # See https://github.com/heapsource/active_model_otp/blob/master/lib/active_model/one_time_password.rb
9
- has_one_time_password length: otp_digits
10
- end
11
-
12
- class_methods do
13
- def otp_digits
14
- 6
15
- end
16
- end
17
-
18
- def authenticate_otp(code)
19
- super(code, drift: 30.seconds)
20
- end
21
-
22
- def otp_provisioning_url
23
- raise "Expected #{self} to respond to #scope" unless respond_to?(:scope)
24
-
25
- provisioning_uri(username, issuer: ::Booth.config.otp_issuer(scope:), digits: otp_digits)
26
- end
27
-
28
- def otp_provisioning_svg
29
- qr = ::RQRCode::QRCode.new(otp_provisioning_url, level: :l)
30
- # See https://whomwah.github.io/rqrcode/
31
- qr.as_svg use_path: false,
32
- viewbox: true, fill: 'fff', offset: 5, module_size: 8, svg_attributes: { 'data-booth' => :otpqr }
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,58 +0,0 @@
1
- module Booth
2
- module Models
3
- module Concerns
4
- module Passwordable
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- # See https://github.com/rails/rails/blob/master/activemodel/lib/active_model/secure_password.rb
9
- has_secure_password
10
-
11
- define_method('password=') do |unencrypted_password|
12
- # We auto-assign a random password before validation.
13
- # If a non-empty password was explicitly assigned, then we should not generate a random password.
14
- @apparently_wants_no_password = unencrypted_password.blank?
15
-
16
- # See https://github.com/rails/rails/issues/34348#issuecomment-615856794
17
- super(unencrypted_password.presence)
18
- end
19
-
20
- validates :password, length: { minimum: password_minlength }, allow_blank: true
21
- # Don't say anything about breaches if the password was already too short to be accepted.
22
- validates :password, not_pwned: {
23
- on_error: :valid,
24
- request_options: {
25
- read_timeout: 3,
26
- open_timeout: 1
27
- },
28
- },
29
- allow_blank: true,
30
- if: proc { _1.errors.empty? }
31
-
32
- before_validation :ensure_initial_password
33
- end
34
-
35
- class_methods do
36
- def password_minlength
37
- 8
38
- end
39
-
40
- # See https://support.1password.com/compatible-website-design
41
- # See https://developer.apple.com/password-rules
42
- def passwordrules
43
- "minlength: #{password_minlength}; maxlength: #{::ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED};"
44
- end
45
- end
46
-
47
- private
48
-
49
- def ensure_initial_password
50
- return if password_digest.present?
51
- return if @apparently_wants_no_password
52
-
53
- self.password = ::SecureRandom.alphanumeric(::ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED)
54
- end
55
- end
56
- end
57
- end
58
- end
@@ -1,23 +0,0 @@
1
- module Booth
2
- module Models
3
- module Contests
4
- module Scopes
5
- class RecentlyCreated
6
- include ::Booth::MethodObject
7
-
8
- param :contest
9
-
10
- def self.scope(base)
11
- base.where.not(created_at: nil)
12
- .where('created_at > ?', ::Booth::Models::Contest.lifespan.ago)
13
- end
14
-
15
- def call
16
- contest.created_at.present? &&
17
- contest.created_at > ::Booth::Models::Contest.lifespan.ago
18
- end
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,32 +0,0 @@
1
- module Booth
2
- module Models
3
- module Contests
4
- module Scopes
5
- class RecentlyResponded
6
- include ::Booth::MethodObject
7
-
8
- param :contest
9
-
10
- def self.scope(base)
11
- base.where.not(created_at: nil, responded_at: nil)
12
- .where('created_at > ?', lifespan.ago)
13
- .where('responded_at > ?', lifespan.ago)
14
- end
15
-
16
- def call
17
- contest.created_at.present? &&
18
- contest.responded_at.present? &&
19
- contest.created_at > lifespan.ago &&
20
- contest.responded_at > lifespan.ago
21
- end
22
-
23
- private
24
-
25
- def lifespan
26
- contest.class.lifespan
27
- end
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,41 +0,0 @@
1
- module Booth
2
- module Models
3
- class PasswordReset < ::Booth::Models::ApplicationRecord
4
- include ::Booth::Logging
5
- include ::Booth::Models::Concerns::Passwordable
6
-
7
- self.table_name = 'booth_password_resets'
8
-
9
- def self.lifetime
10
- ::Booth.config.password_reset_window
11
- end
12
-
13
- belongs_to :credential, class_name: '::Booth::Models::Credential'
14
-
15
- validates :creator_ip, presence: true
16
-
17
- # See https://github.com/rails/rails/blob/main/activerecord/lib/active_record/secure_token.rb
18
- has_secure_token :secret_key, length: 30
19
-
20
- def step
21
- ::Booth::PasswordResets::Step.call(self)
22
- end
23
-
24
- def completed?
25
- password_chosen_at.present? && password_confirmed_at.present?
26
- end
27
-
28
- def revoked?
29
- revoked_at.present?
30
- end
31
-
32
- def recently_created?
33
- created_at > self.class.lifetime.ago
34
- end
35
-
36
- def other_password_resets_of_this_credential
37
- credential.password_resets.where.not(id:)
38
- end
39
- end
40
- end
41
- end
@@ -1,32 +0,0 @@
1
- module Booth
2
- module Models
3
- class Recovery < ::Booth::Models::ApplicationRecord
4
- include ::Booth::Logging
5
-
6
- self.table_name = 'booth_recoveries'
7
-
8
- before_validation :normalize_email
9
-
10
- def consumed?
11
- consumed_at.present?
12
- end
13
-
14
- def revoked?
15
- revoked_at.present?
16
- end
17
-
18
- def other_recoveries_with_this_scope_and_email
19
- self.class
20
- .where(scope:)
21
- .where(email:)
22
- .where.not(id:)
23
- end
24
-
25
- private
26
-
27
- def normalize_email
28
- self.email = ::Booth::Syntaxes::Email.call(email).normalized_email
29
- end
30
- end
31
- end
32
- end
@@ -1,10 +0,0 @@
1
- module Booth
2
- module Models
3
- class Registration < ::Booth::Models::ApplicationRecord
4
- include ::Booth::Logging
5
- include ::Booth::Models::Concerns::ModeEnumerable
6
-
7
- self.table_name = 'booth_registrations'
8
- end
9
- end
10
- end
@@ -1,25 +0,0 @@
1
- module Booth
2
- module Modes
3
- module Base
4
- extend ActiveSupport::Concern
5
-
6
- class_methods do
7
- def id
8
- self.to_s.demodulize.underscore
9
- end
10
-
11
- def title
12
- I18n.t "booth.mode_#{id}_title"
13
- end
14
-
15
- def description
16
- I18n.t "booth.mode_#{id}_description"
17
- end
18
-
19
- def <=>(other)
20
- ::Booth::Mode.all.index(self) <=> ::Booth::Mode.all.index(other)
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,7 +0,0 @@
1
- module Booth
2
- module Modes
3
- module UsernameAndPassword
4
- include ::Booth::Modes::Base
5
- end
6
- end
7
- end
@@ -1,7 +0,0 @@
1
- module Booth
2
- module Modes
3
- module UsernameAndWebauth
4
- include ::Booth::Modes::Base
5
- end
6
- end
7
- end
@@ -1,7 +0,0 @@
1
- module Booth
2
- module Modes
3
- module UsernamePasswordAndOtp
4
- include ::Booth::Modes::Base
5
- end
6
- end
7
- end
@@ -1,7 +0,0 @@
1
- module Booth
2
- module Modes
3
- module UsernamePasswordAndWebauth
4
- include ::Booth::Modes::Base
5
- end
6
- end
7
- end
@@ -1,35 +0,0 @@
1
- module Booth
2
- module Onboardings
3
- class Find
4
- include ::Booth::Logging
5
- include ::Booth::MethodObject
6
-
7
- option :secret_key
8
-
9
- def call
10
- check_secret_key_syntax_action
11
- .on_success { find_onboarding_action }
12
- end
13
-
14
- private
15
-
16
- def check_secret_key_syntax_action
17
- ::Booth::Syntaxes::SecretKey.call(secret_key)
18
- end
19
-
20
- def find_onboarding_action
21
- debug { "Looking for Onboarding with secret key #{secret_key.inspect}" }
22
- onboarding = ::Booth::Models::Onboarding.find_by(secret_key:)
23
-
24
- if onboarding
25
- debug { "Found Onboarding with ID #{onboarding.id.inspect}" }
26
- Tron.success(:found_onboarding, onboarding:)
27
- else
28
- message = "Could not find userland Onboarding with secret key #{secret_key.inspect}"
29
- debug { message }
30
- Tron.failure :onboarding_not_found, public_message: I18n.t('booth.unknown_secret_key')
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,63 +0,0 @@
1
- module Booth
2
- module Onboardings
3
- class PropagateToCredential
4
- include ::Booth::Logging
5
- include ::Booth::MethodObject
6
-
7
- param :onboarding
8
- option :ip
9
- option :agent
10
-
11
- def call
12
- debug { 'Propagating Onboarding to Credential...' }
13
- raise "Expected Onboarding to be valid: #{onboarding.errors.full_messages.to_sentence}" if onboarding.invalid?
14
-
15
- onboarding.transaction do
16
- update_credential!
17
- remove_existing_authenticators!
18
- create_authenticator!
19
- register_audit!
20
- finalize_onboarding!
21
- end
22
- debug { 'Propagation of Onboarding completed' }
23
- end
24
-
25
- private
26
-
27
- def update_credential!
28
- onboarding.credential.update! mode: onboarding.mode,
29
- password_digest: onboarding.password_digest,
30
- otp_secret_key: onboarding.otp_secret_key
31
- end
32
-
33
- def remove_existing_authenticators!
34
- onboarding.credential.authenticators.destroy_all
35
- end
36
-
37
- def create_authenticator!
38
- return unless onboarding.authenticator?
39
-
40
- onboarding.credential.authenticators.create! webauthn_id: onboarding.webauthn_id,
41
- device_id: onboarding.authenticator_id,
42
- nickname: onboarding.authenticator_nickname,
43
- public_key: onboarding.authenticator_public_key,
44
- sign_count: onboarding.authenticator_sign_count,
45
- challenge: onboarding.authenticator_challenge,
46
- supports_user_verification: onboarding.requires_user_verification?,
47
- confirmed_at: Time.current
48
- end
49
-
50
- def register_audit!
51
- ::Booth::Audits::Register::CompletedOnboarding.call(
52
- credential: onboarding.credential,
53
- ip:,
54
- agent:
55
- )
56
- end
57
-
58
- def finalize_onboarding!
59
- onboarding.update! propagated_at: Time.current
60
- end
61
- end
62
- end
63
- end
@@ -1,68 +0,0 @@
1
- module Booth
2
- module Onboardings
3
- class Step
4
- include ::Booth::MethodObject
5
-
6
- param :onboarding
7
-
8
- def call
9
- return :timed_out unless onboarding.recently_created?
10
-
11
- if onboarding.mode_first_time?
12
- mode_first_time
13
- elsif onboarding.mode_username_and_password?
14
- mode_username_and_password
15
- elsif onboarding.mode_username_password_and_otp?
16
- mode_username_password_and_otp
17
- elsif onboarding.mode_username_password_and_webauth?
18
- mode_username_password_and_webauth
19
- elsif onboarding.mode_username_and_webauth?
20
- mode_username_and_webauth
21
- else
22
- raise 'Invalid Onboarding State'
23
- end
24
- end
25
-
26
- private
27
-
28
- def mode_first_time
29
- :choose_mode
30
- end
31
-
32
- def mode_username_and_password
33
- return :choose_password if onboarding.password_chosen_at.blank?
34
- return :completed if onboarding.password_confirmed_at.present?
35
-
36
- :confirm_password
37
- end
38
-
39
- def mode_username_password_and_otp
40
- return mode_username_and_password unless mode_username_and_password == :completed
41
- return :register_otp if onboarding.otp_registered_at.blank?
42
- return :confirm_otp if onboarding.otp_confirmed_at.blank?
43
-
44
- :completed
45
- end
46
-
47
- def mode_username_password_and_webauth
48
- return mode_username_and_password unless mode_username_and_password == :completed
49
-
50
- mode_username_and_webauth
51
- end
52
-
53
- def mode_username_and_webauth
54
- return :register_webauth if onboarding.authenticator_id.blank? ||
55
- onboarding.authenticator_public_key.blank? ||
56
- onboarding.authenticator_sign_count.blank?
57
- return :choose_webauth_nickname if onboarding.authenticator_nickname.blank?
58
- return :confirm_webauth if onboarding.authenticator_confirmed_at.blank?
59
-
60
- :completed
61
- end
62
-
63
- def mode_unknown
64
- :unknown
65
- end
66
- end
67
- end
68
- end
@@ -1,57 +0,0 @@
1
- module Booth
2
- module PasswordResets
3
- class Create
4
- include ::Booth::Logging
5
- include ::Booth::MethodObject
6
-
7
- option :credential
8
- option :email
9
- option :ip
10
- option :agent
11
-
12
- def call
13
- do_check_applicability
14
- .on_success { do_check_email_syntax }
15
- .on_success { do_check_cooldown }
16
- .on_success { do_create_password_reset }
17
- end
18
-
19
- private
20
-
21
- def do_check_applicability
22
- return Tron.success :credential_has_password if credential.applicable_for_password_reset?
23
-
24
- debug { 'This credential has no password to be reset' }
25
- Tron.failure :credential_cannot_reset_password, public_message: I18n.t('booth.password_reset_not_available')
26
- end
27
-
28
- def do_check_email_syntax
29
- check = ::Booth::Syntaxes::Email.call(email)
30
-
31
- check.on_success do
32
- @email_address = check.normalized_email
33
- end
34
-
35
- check
36
- end
37
-
38
- def do_check_cooldown
39
- ::Booth::Cooldowns::PasswordReset.call(credential:)
40
- end
41
-
42
- def do_create_password_reset
43
- password_reset = nil
44
-
45
- ::Booth::Models::PasswordReset.transaction do
46
- password_reset = ::Booth::Models::PasswordReset.create! credential:, creator_ip: ip
47
- ::Booth::Audits::Register::RequestedPasswordReset.call credential:, ip:, agent:
48
- end
49
-
50
- Tron.success :applicable_for_reset, username: credential.username,
51
- email: @email_address,
52
- credential_id: credential.id,
53
- secret_key: password_reset.secret_key
54
- end
55
- end
56
- end
57
- end