authnz_eleven 0.1.0

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 (237) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +384 -0
  5. data/lib/authnz_eleven/identity.rb +210 -0
  6. data/lib/authnz_eleven/principal.rb +201 -0
  7. data/lib/authnz_eleven/principals.rb +111 -0
  8. data/lib/authnz_eleven/version.rb +5 -0
  9. data/lib/authnz_eleven/wizard.rb +570 -0
  10. data/lib/authnz_eleven.rb +15 -0
  11. data/lib/generators/authnz_eleven/USAGE +26 -0
  12. data/lib/generators/authnz_eleven/authnz_eleven_generator.rb +3040 -0
  13. data/lib/generators/authnz_eleven/templates/config/forbidden_usernames.LICENSE +22 -0
  14. data/lib/generators/authnz_eleven/templates/config/forbidden_usernames.txt +558 -0
  15. data/lib/generators/authnz_eleven/templates/config/initializers/authentication.rb.tt +275 -0
  16. data/lib/generators/authnz_eleven/templates/config/initializers/omniauth.rb.tt +9 -0
  17. data/lib/generators/authnz_eleven/templates/config/initializers/team_slug.rb.tt +24 -0
  18. data/lib/generators/authnz_eleven/templates/config/initializers/webauthn.rb.tt +14 -0
  19. data/lib/generators/authnz_eleven/templates/config/locales/authentication.en.yml.tt +11 -0
  20. data/lib/generators/authnz_eleven/templates/controllers/active_teams_controller.rb.tt +6 -0
  21. data/lib/generators/authnz_eleven/templates/controllers/admin/dashboard_controller.rb.tt +4 -0
  22. data/lib/generators/authnz_eleven/templates/controllers/admin/sessions_controller.rb.tt +17 -0
  23. data/lib/generators/authnz_eleven/templates/controllers/admin/users_controller.rb.tt +91 -0
  24. data/lib/generators/authnz_eleven/templates/controllers/admin_controller.rb.tt +6 -0
  25. data/lib/generators/authnz_eleven/templates/controllers/base_controller.rb.tt +11 -0
  26. data/lib/generators/authnz_eleven/templates/controllers/concerns/api_token_authentication.rb.tt +21 -0
  27. data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/easy_dev_login.rb.tt +47 -0
  28. data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/registering.rb.tt +25 -0
  29. data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/sudoable.rb.tt +117 -0
  30. data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/webauthn_ceremony.rb.tt +51 -0
  31. data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication.rb.tt +421 -0
  32. data/lib/generators/authnz_eleven/templates/controllers/concerns/authorization.rb.tt +48 -0
  33. data/lib/generators/authnz_eleven/templates/controllers/concerns/captchable.rb.tt +11 -0
  34. data/lib/generators/authnz_eleven/templates/controllers/impersonations_controller.rb.tt +8 -0
  35. data/lib/generators/authnz_eleven/templates/controllers/invitations_controller.rb.tt +65 -0
  36. data/lib/generators/authnz_eleven/templates/controllers/settings/api_tokens_controller.rb.tt +54 -0
  37. data/lib/generators/authnz_eleven/templates/controllers/settings/authentications/events_controller.rb.tt +5 -0
  38. data/lib/generators/authnz_eleven/templates/controllers/settings/dashboard_controller.rb.tt +4 -0
  39. data/lib/generators/authnz_eleven/templates/controllers/settings/email_verifications_controller.rb.tt +15 -0
  40. data/lib/generators/authnz_eleven/templates/controllers/settings/emails_controller.rb.tt +93 -0
  41. data/lib/generators/authnz_eleven/templates/controllers/settings/invitations_controller.rb.tt +61 -0
  42. data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/authenticators_controller.rb.tt +87 -0
  43. data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/recovery_codes_controller.rb.tt +27 -0
  44. data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/security_keys_controller.rb.tt +86 -0
  45. data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/sms_controller.rb.tt +84 -0
  46. data/lib/generators/authnz_eleven/templates/controllers/settings/omniauth_identities_controller.rb.tt +27 -0
  47. data/lib/generators/authnz_eleven/templates/controllers/settings/passkeys_controller.rb.tt +70 -0
  48. data/lib/generators/authnz_eleven/templates/controllers/settings/passwords_controller.rb.tt +74 -0
  49. data/lib/generators/authnz_eleven/templates/controllers/settings/phone_verifications_controller.rb.tt +39 -0
  50. data/lib/generators/authnz_eleven/templates/controllers/settings/phones_controller.rb.tt +88 -0
  51. data/lib/generators/authnz_eleven/templates/controllers/settings/sessions_controller.rb.tt +29 -0
  52. data/lib/generators/authnz_eleven/templates/controllers/settings/users_controller.rb.tt +42 -0
  53. data/lib/generators/authnz_eleven/templates/controllers/settings_controller.rb.tt +5 -0
  54. data/lib/generators/authnz_eleven/templates/controllers/teams_controller.rb.tt +38 -0
  55. data/lib/generators/authnz_eleven/templates/controllers/users/credential_enrollments/passkeys_controller.rb.tt +36 -0
  56. data/lib/generators/authnz_eleven/templates/controllers/users/credential_enrollments/passwords_controller.rb.tt +28 -0
  57. data/lib/generators/authnz_eleven/templates/controllers/users/credential_enrollments_controller.rb.tt +14 -0
  58. data/lib/generators/authnz_eleven/templates/controllers/users/easy_dev_logins_controller.rb.tt +19 -0
  59. data/lib/generators/authnz_eleven/templates/controllers/users/email_verifications_controller.rb.tt +36 -0
  60. data/lib/generators/authnz_eleven/templates/controllers/users/magic_link_controller.rb.tt +53 -0
  61. data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/recovery_codes_controller.rb.tt +30 -0
  62. data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/security_keys_controller.rb.tt +34 -0
  63. data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/sms_controller.rb.tt +45 -0
  64. data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/totps_controller.rb.tt +42 -0
  65. data/lib/generators/authnz_eleven/templates/controllers/users/omniauth/registrations_controller.rb.tt +26 -0
  66. data/lib/generators/authnz_eleven/templates/controllers/users/omniauth_controller.rb.tt +139 -0
  67. data/lib/generators/authnz_eleven/templates/controllers/users/passkeys/sessions_controller.rb.tt +18 -0
  68. data/lib/generators/authnz_eleven/templates/controllers/users/password_resets_controller.rb.tt +64 -0
  69. data/lib/generators/authnz_eleven/templates/controllers/users/registrations/email_verifications_controller.rb.tt +53 -0
  70. data/lib/generators/authnz_eleven/templates/controllers/users/registrations/phone_verifications_controller.rb.tt +37 -0
  71. data/lib/generators/authnz_eleven/templates/controllers/users/registrations_controller.rb.tt +112 -0
  72. data/lib/generators/authnz_eleven/templates/controllers/users/sessions_controller.rb.tt +52 -0
  73. data/lib/generators/authnz_eleven/templates/controllers/users/sms_sessions_controller.rb.tt +53 -0
  74. data/lib/generators/authnz_eleven/templates/controllers/users/sudos_controller.rb.tt +42 -0
  75. data/lib/generators/authnz_eleven/templates/erb/admin/dashboard/index.html.erb.tt +9 -0
  76. data/lib/generators/authnz_eleven/templates/erb/admin/sessions/index.html.erb.tt +27 -0
  77. data/lib/generators/authnz_eleven/templates/erb/admin/users/index.html.erb.tt +32 -0
  78. data/lib/generators/authnz_eleven/templates/erb/admin/users/show.html.erb.tt +70 -0
  79. data/lib/generators/authnz_eleven/templates/erb/invitations/show.html.erb.tt +9 -0
  80. data/lib/generators/authnz_eleven/templates/erb/settings/api_tokens/create.html.erb.tt +7 -0
  81. data/lib/generators/authnz_eleven/templates/erb/settings/api_tokens/index.html.erb.tt +29 -0
  82. data/lib/generators/authnz_eleven/templates/erb/settings/authentications/events/index.html.erb.tt +18 -0
  83. data/lib/generators/authnz_eleven/templates/erb/settings/dashboard/index.html.erb.tt +75 -0
  84. data/lib/generators/authnz_eleven/templates/erb/settings/emails/edit.html.erb.tt +68 -0
  85. data/lib/generators/authnz_eleven/templates/erb/settings/invitations/new.html.erb.tt +31 -0
  86. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/authenticators/new.html.erb.tt +57 -0
  87. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/recovery_codes/create.html.erb.tt +14 -0
  88. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/recovery_codes/index.html.erb.tt +13 -0
  89. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/security_keys/edit.html.erb.tt +14 -0
  90. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/security_keys/index.html.erb.tt +20 -0
  91. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/security_keys/new.html.erb.tt +17 -0
  92. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/sms/new.html.erb.tt +35 -0
  93. data/lib/generators/authnz_eleven/templates/erb/settings/omniauth_identities/index.html.erb.tt +28 -0
  94. data/lib/generators/authnz_eleven/templates/erb/settings/passkeys/edit.html.erb.tt +14 -0
  95. data/lib/generators/authnz_eleven/templates/erb/settings/passkeys/index.html.erb.tt +21 -0
  96. data/lib/generators/authnz_eleven/templates/erb/settings/passkeys/new.html.erb.tt +13 -0
  97. data/lib/generators/authnz_eleven/templates/erb/settings/passwords/edit.html.erb.tt +68 -0
  98. data/lib/generators/authnz_eleven/templates/erb/settings/phone_verifications/new.html.erb.tt +22 -0
  99. data/lib/generators/authnz_eleven/templates/erb/settings/phones/edit.html.erb.tt +58 -0
  100. data/lib/generators/authnz_eleven/templates/erb/settings/sessions/index.html.erb.tt +29 -0
  101. data/lib/generators/authnz_eleven/templates/erb/settings/users/show.html.erb.tt +28 -0
  102. data/lib/generators/authnz_eleven/templates/erb/shared/_authnz_eleven_nav.html.erb.tt +24 -0
  103. data/lib/generators/authnz_eleven/templates/erb/shared/_authnz_eleven_sudo_challenge.html.erb.tt +43 -0
  104. data/lib/generators/authnz_eleven/templates/erb/shared/_captcha.html.erb.tt +8 -0
  105. data/lib/generators/authnz_eleven/templates/erb/teams/index.html.erb.tt +34 -0
  106. data/lib/generators/authnz_eleven/templates/erb/teams/new.html.erb.tt +22 -0
  107. data/lib/generators/authnz_eleven/templates/erb/teams/show.html.erb.tt +5 -0
  108. data/lib/generators/authnz_eleven/templates/erb/user_mailer/email_change_verification.html.erb.tt +7 -0
  109. data/lib/generators/authnz_eleven/templates/erb/user_mailer/invitation.html.erb.tt +11 -0
  110. data/lib/generators/authnz_eleven/templates/erb/user_mailer/magic_link.html.erb.tt +5 -0
  111. data/lib/generators/authnz_eleven/templates/erb/user_mailer/password_reset.html.erb.tt +6 -0
  112. data/lib/generators/authnz_eleven/templates/erb/user_mailer/registration_email_verification.html.erb.tt +7 -0
  113. data/lib/generators/authnz_eleven/templates/erb/user_mailer/security_notification.html.erb.tt +15 -0
  114. data/lib/generators/authnz_eleven/templates/erb/users/credential_enrollments/passkeys/new.html.erb.tt +24 -0
  115. data/lib/generators/authnz_eleven/templates/erb/users/credential_enrollments/passwords/new.html.erb.tt +21 -0
  116. data/lib/generators/authnz_eleven/templates/erb/users/credential_enrollments/show.html.erb.tt +21 -0
  117. data/lib/generators/authnz_eleven/templates/erb/users/email_verifications/show.html.erb.tt +9 -0
  118. data/lib/generators/authnz_eleven/templates/erb/users/magic_link/edit.html.erb.tt +9 -0
  119. data/lib/generators/authnz_eleven/templates/erb/users/magic_link/new.html.erb.tt +22 -0
  120. data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/recovery_codes/new.html.erb.tt +20 -0
  121. data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/security_keys/new.html.erb.tt +17 -0
  122. data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/sms/new.html.erb.tt +25 -0
  123. data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/totps/new.html.erb.tt +21 -0
  124. data/lib/generators/authnz_eleven/templates/erb/users/omniauth/registrations/new.html.erb.tt +28 -0
  125. data/lib/generators/authnz_eleven/templates/erb/users/password_resets/edit.html.erb.tt +26 -0
  126. data/lib/generators/authnz_eleven/templates/erb/users/password_resets/new.html.erb.tt +20 -0
  127. data/lib/generators/authnz_eleven/templates/erb/users/registrations/email_verifications/new.html.erb.tt +11 -0
  128. data/lib/generators/authnz_eleven/templates/erb/users/registrations/email_verifications/show.html.erb.tt +34 -0
  129. data/lib/generators/authnz_eleven/templates/erb/users/registrations/new.html.erb.tt +87 -0
  130. data/lib/generators/authnz_eleven/templates/erb/users/registrations/phone_verifications/new.html.erb.tt +46 -0
  131. data/lib/generators/authnz_eleven/templates/erb/users/sessions/new.html.erb.tt +88 -0
  132. data/lib/generators/authnz_eleven/templates/erb/users/sms_sessions/edit.html.erb.tt +30 -0
  133. data/lib/generators/authnz_eleven/templates/erb/users/sms_sessions/new.html.erb.tt +22 -0
  134. data/lib/generators/authnz_eleven/templates/erb/users/sudos/new.html.erb.tt +9 -0
  135. data/lib/generators/authnz_eleven/templates/javascript/controllers/webauthn_controller.js.tt +142 -0
  136. data/lib/generators/authnz_eleven/templates/jobs/authnz_eleven_deferred_job.rb.tt +9 -0
  137. data/lib/generators/authnz_eleven/templates/jobs/authnz_eleven_mail_delivery_job.rb.tt +3 -0
  138. data/lib/generators/authnz_eleven/templates/jobs/sms_delivery_job.rb.tt +11 -0
  139. data/lib/generators/authnz_eleven/templates/lib/captcha/null.rb.tt +9 -0
  140. data/lib/generators/authnz_eleven/templates/lib/captcha/turnstile.rb.tt +39 -0
  141. data/lib/generators/authnz_eleven/templates/lib/captcha.rb.tt +15 -0
  142. data/lib/generators/authnz_eleven/templates/lib/sms/log.rb.tt +8 -0
  143. data/lib/generators/authnz_eleven/templates/lib/sms/twilio.rb.tt +70 -0
  144. data/lib/generators/authnz_eleven/templates/lib/sms.rb.tt +15 -0
  145. data/lib/generators/authnz_eleven/templates/mailers/user_mailer.rb.tt +98 -0
  146. data/lib/generators/authnz_eleven/templates/migrations/add_guest_to_users_migration.rb.tt +6 -0
  147. data/lib/generators/authnz_eleven/templates/migrations/create_api_tokens_migration.rb.tt +14 -0
  148. data/lib/generators/authnz_eleven/templates/migrations/create_events_migration.rb.tt +26 -0
  149. data/lib/generators/authnz_eleven/templates/migrations/create_invitations_migration.rb.tt +20 -0
  150. data/lib/generators/authnz_eleven/templates/migrations/create_memberships_migration.rb.tt +12 -0
  151. data/lib/generators/authnz_eleven/templates/migrations/create_omniauth_identities_migration.rb.tt +16 -0
  152. data/lib/generators/authnz_eleven/templates/migrations/create_password_histories_migration.rb.tt +11 -0
  153. data/lib/generators/authnz_eleven/templates/migrations/create_pending_registrations_migration.rb.tt +31 -0
  154. data/lib/generators/authnz_eleven/templates/migrations/create_recovery_codes_migration.rb.tt +11 -0
  155. data/lib/generators/authnz_eleven/templates/migrations/create_sessions_migration.rb.tt +19 -0
  156. data/lib/generators/authnz_eleven/templates/migrations/create_teams_migration.rb.tt +9 -0
  157. data/lib/generators/authnz_eleven/templates/migrations/create_users_migration.rb.tt +70 -0
  158. data/lib/generators/authnz_eleven/templates/migrations/create_webauthn_credentials_migration.rb.tt +16 -0
  159. data/lib/generators/authnz_eleven/templates/models/api_token.rb.tt +24 -0
  160. data/lib/generators/authnz_eleven/templates/models/concerns/adminable.rb.tt +15 -0
  161. data/lib/generators/authnz_eleven/templates/models/concerns/authenticatable.rb.tt +151 -0
  162. data/lib/generators/authnz_eleven/templates/models/concerns/bannable.rb.tt +24 -0
  163. data/lib/generators/authnz_eleven/templates/models/concerns/deadboltable.rb.tt +39 -0
  164. data/lib/generators/authnz_eleven/templates/models/concerns/email_verifiable.rb.tt +52 -0
  165. data/lib/generators/authnz_eleven/templates/models/concerns/eventable.rb.tt +22 -0
  166. data/lib/generators/authnz_eleven/templates/models/concerns/guestable.rb.tt +30 -0
  167. data/lib/generators/authnz_eleven/templates/models/concerns/invitable.rb.tt +12 -0
  168. data/lib/generators/authnz_eleven/templates/models/concerns/magic_linkable.rb.tt +34 -0
  169. data/lib/generators/authnz_eleven/templates/models/concerns/password_historical.rb.tt +28 -0
  170. data/lib/generators/authnz_eleven/templates/models/concerns/password_policy.rb.tt +38 -0
  171. data/lib/generators/authnz_eleven/templates/models/concerns/password_rotatable.rb.tt +15 -0
  172. data/lib/generators/authnz_eleven/templates/models/concerns/phone_verifiable.rb.tt +50 -0
  173. data/lib/generators/authnz_eleven/templates/models/concerns/principals.rb.tt +67 -0
  174. data/lib/generators/authnz_eleven/templates/models/concerns/recoverable.rb.tt +19 -0
  175. data/lib/generators/authnz_eleven/templates/models/concerns/second_factor.rb.tt +52 -0
  176. data/lib/generators/authnz_eleven/templates/models/concerns/sms_challengeable.rb.tt +38 -0
  177. data/lib/generators/authnz_eleven/templates/models/concerns/team_member.rb.tt +20 -0
  178. data/lib/generators/authnz_eleven/templates/models/current.rb.tt +28 -0
  179. data/lib/generators/authnz_eleven/templates/models/event.rb.tt +32 -0
  180. data/lib/generators/authnz_eleven/templates/models/invitation.rb.tt +122 -0
  181. data/lib/generators/authnz_eleven/templates/models/membership.rb.tt +16 -0
  182. data/lib/generators/authnz_eleven/templates/models/omniauth_identity.rb.tt +12 -0
  183. data/lib/generators/authnz_eleven/templates/models/password_history.rb.tt +3 -0
  184. data/lib/generators/authnz_eleven/templates/models/pending_registration.rb.tt +216 -0
  185. data/lib/generators/authnz_eleven/templates/models/recovery_code.rb.tt +41 -0
  186. data/lib/generators/authnz_eleven/templates/models/session.rb.tt +46 -0
  187. data/lib/generators/authnz_eleven/templates/models/team.rb.tt +12 -0
  188. data/lib/generators/authnz_eleven/templates/models/user.rb.tt +59 -0
  189. data/lib/generators/authnz_eleven/templates/models/webauthn_credential.rb.tt +12 -0
  190. data/lib/generators/authnz_eleven/templates/tasks/authnz_eleven.rake.tt +41 -0
  191. data/lib/generators/authnz_eleven/templates/test_unit/controllers/account_existence_parity_test.rb.tt +158 -0
  192. data/lib/generators/authnz_eleven/templates/test_unit/controllers/admin/access_test.rb.tt +139 -0
  193. data/lib/generators/authnz_eleven/templates/test_unit/controllers/admin/multi_factor_authentication_test.rb.tt +65 -0
  194. data/lib/generators/authnz_eleven/templates/test_unit/controllers/api_token_authentication_test.rb.tt +45 -0
  195. data/lib/generators/authnz_eleven/templates/test_unit/controllers/credential_enrollment_test.rb.tt +84 -0
  196. data/lib/generators/authnz_eleven/templates/test_unit/controllers/guest_access_test.rb.tt +113 -0
  197. data/lib/generators/authnz_eleven/templates/test_unit/controllers/invitations_controller_test.rb.tt +230 -0
  198. data/lib/generators/authnz_eleven/templates/test_unit/controllers/passkey_ceremony_test.rb.tt +126 -0
  199. data/lib/generators/authnz_eleven/templates/test_unit/controllers/password_rotation_test.rb.tt +67 -0
  200. data/lib/generators/authnz_eleven/templates/test_unit/controllers/security_key_ceremony_test.rb.tt +218 -0
  201. data/lib/generators/authnz_eleven/templates/test_unit/controllers/security_notifications_test.rb.tt +39 -0
  202. data/lib/generators/authnz_eleven/templates/test_unit/controllers/session_lifecycle_test.rb.tt +102 -0
  203. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/api_tokens_controller_test.rb.tt +61 -0
  204. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/authentications/events_controller_test.rb.tt +17 -0
  205. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/email_verifications_controller_test.rb.tt +39 -0
  206. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/invitations_controller_test.rb.tt +157 -0
  207. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/management_test.rb.tt +256 -0
  208. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/multi_factor_authentication/management_test.rb.tt +222 -0
  209. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/omniauth_identities_controller_test.rb.tt +57 -0
  210. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/phone_verifications_controller_test.rb.tt +146 -0
  211. data/lib/generators/authnz_eleven/templates/test_unit/controllers/teams_controller_test.rb.tt +83 -0
  212. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/email_verifications_controller_test.rb.tt +74 -0
  213. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/magic_link_controller_test.rb.tt +90 -0
  214. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/multi_factor_authentication/challenge_test.rb.tt +143 -0
  215. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/omniauth/registrations_controller_test.rb.tt +138 -0
  216. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/omniauth_controller_test.rb.tt +97 -0
  217. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/password_resets_controller_test.rb.tt +58 -0
  218. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/registrations/email_verifications_controller_test.rb.tt +102 -0
  219. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/registrations/phone_verifications_controller_test.rb.tt +69 -0
  220. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/registrations_controller_test.rb.tt +180 -0
  221. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/sessions_controller_test.rb.tt +124 -0
  222. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/sms_sessions_controller_test.rb.tt +111 -0
  223. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/sudos_controller_test.rb.tt +110 -0
  224. data/lib/generators/authnz_eleven/templates/test_unit/fixtures/memberships.yml.tt +13 -0
  225. data/lib/generators/authnz_eleven/templates/test_unit/fixtures/teams.yml.tt +2 -0
  226. data/lib/generators/authnz_eleven/templates/test_unit/fixtures/users.yml.tt +68 -0
  227. data/lib/generators/authnz_eleven/templates/test_unit/mailers/user_mailer_test.rb.tt +88 -0
  228. data/lib/generators/authnz_eleven/templates/test_unit/models/api_token_test.rb.tt +38 -0
  229. data/lib/generators/authnz_eleven/templates/test_unit/models/password_history_test.rb.tt +51 -0
  230. data/lib/generators/authnz_eleven/templates/test_unit/models/session_test.rb.tt +16 -0
  231. data/lib/generators/authnz_eleven/templates/test_unit/models/sms_challengeable_test.rb.tt +108 -0
  232. data/lib/generators/authnz_eleven/templates/test_unit/models/user/guestable_test.rb.tt +78 -0
  233. data/lib/generators/authnz_eleven/templates/test_unit/models/user/recovery_codes_test.rb.tt +32 -0
  234. data/lib/generators/authnz_eleven/templates/test_unit/models/user/sign_in_methods_test.rb.tt +56 -0
  235. data/lib/generators/authnz_eleven/templates/test_unit/models/user_test.rb.tt +145 -0
  236. data/lib/generators/authnz_eleven/wizard_generator.rb +52 -0
  237. metadata +309 -0
@@ -0,0 +1,20 @@
1
+ module <%= identity.class_name %>::TeamMember
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ has_many :memberships, dependent: :destroy
6
+ has_many :teams, through: :memberships
7
+ end
8
+
9
+ def on_a_team?
10
+ memberships.any?
11
+ end
12
+
13
+ def primary_team
14
+ teams.first
15
+ end
16
+
17
+ def member_of?(team)
18
+ memberships.exists?(team:)
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ class <%= identity.current_class %> < ActiveSupport::CurrentAttributes
2
+ attribute :session
3
+ attribute :user_agent, :ip_address, :request_id
4
+ <%- if impersonatable? -%>
5
+ attribute :impersonated_user
6
+
7
+ def true_user
8
+ session&.<%= identity.singular %>
9
+ end
10
+
11
+ def user
12
+ impersonated_user || true_user
13
+ end
14
+
15
+ def user=(user)
16
+ self.impersonated_user = user
17
+ end
18
+ <%- else -%>
19
+
20
+ def user
21
+ session&.<%= identity.singular %>
22
+ end
23
+ <%- end -%>
24
+ <%- if teams? -%>
25
+
26
+ attribute :team
27
+ <%- end -%>
28
+ end
@@ -0,0 +1,32 @@
1
+ class <%= identity.event_class %> < ApplicationRecord
2
+ belongs_to :subject, polymorphic: true
3
+ belongs_to :actor, polymorphic: true, optional: true
4
+ belongs_to :context, polymorphic: true, optional: true
5
+
6
+ scope :chronological, -> { order(occurred_at: :asc, id: :asc) }
7
+ scope :recent_first, -> { order(occurred_at: :desc, id: :desc) }
8
+
9
+ def self.record!(
10
+ event_type:,
11
+ subject:,
12
+ actor: nil,
13
+ context: nil,
14
+ occurred_at: nil,
15
+ data: nil,
16
+ ip_address: nil,
17
+ user_agent: nil,
18
+ request_id: nil
19
+ )
20
+ create!(
21
+ event_type: event_type,
22
+ subject: subject,
23
+ actor: actor || <%= identity.current_class %>.user,
24
+ context: context || subject.try(:event_context) || subject,
25
+ occurred_at: occurred_at || Time.current,
26
+ data: data || {},
27
+ ip_address: ip_address || <%= identity.current_class %>.ip_address,
28
+ user_agent: user_agent || <%= identity.current_class %>.user_agent,
29
+ request_id: request_id || <%= identity.current_class %>.request_id
30
+ )
31
+ end
32
+ end
@@ -0,0 +1,122 @@
1
+ class <%= identity.invitation_class %> < ApplicationRecord
2
+ belongs_to :inviter, class_name: "<%= identity.class_name %>", inverse_of: :sent_invitations
3
+ belongs_to :invitee, class_name: "<%= identity.class_name %>", optional: true, inverse_of: :<%= teams? ? "accepted_invitations" : "invitation" %>
4
+ <%- if teams? -%>
5
+ belongs_to :team, optional: true
6
+ <%- end -%>
7
+
8
+ generates_token_for :invitation, expires_in: <%= identity.config_reference %>.tokens.invitation do
9
+ <%- if multi_channel_invitable? -%>
10
+ [ sent_to, sent_to_channel, accepted_at ]
11
+ <%- else -%>
12
+ [ sent_to, accepted_at ]
13
+ <%- end -%>
14
+ end
15
+
16
+ <%- if multi_channel_invitable? -%>
17
+ normalizes :sent_to, with: -> {
18
+ it.include?("@") ? it.strip.downcase : Phonelib.parse(it, <%= identity.config_reference %>.phone.default_country).to_s
19
+ }
20
+
21
+ before_validation do
22
+ self.sent_to_channel = sent_to&.include?("@") ? "email" : "phone" if will_save_change_to_sent_to?
23
+ end
24
+
25
+ validates :sent_to, presence: true
26
+ validates :sent_to_channel, inclusion: { in: %w[email phone] }
27
+ validates :sent_to, <%= principals.email.validation %>, if: :email_addressed?
28
+ validates :sent_to, phone: { countries: <%= identity.config_reference %>.phone.allowed_countries, format: :e164 }, if: :phone_addressed?
29
+ <%- elsif email_invitable? -%>
30
+ normalizes :sent_to, with: <%= principals.email.normalization(config_reference: identity.config_reference) %>
31
+ validates :sent_to, presence: true, <%= principals.email.validation %>
32
+ <%- else -%>
33
+ normalizes :sent_to, with: <%= principals.phone.normalization(config_reference: identity.config_reference) %>
34
+ validates :sent_to, presence: true
35
+ validates :sent_to, phone: { countries: <%= identity.config_reference %>.phone.allowed_countries, format: :e164 }
36
+ <%- end -%>
37
+ <%- if encrypted_pii? -%>
38
+
39
+ encrypts :sent_to, deterministic: true
40
+ <%- end -%>
41
+ <%- if coy? -%>
42
+
43
+ def offer_unless_redundant
44
+ offer unless redundant?
45
+ end
46
+ <%- else -%>
47
+
48
+ validate on: :create do
49
+ <%- if teams? -%>
50
+ errors.add(:base, team ? "#{sent_to} is already a member of #{team.name}." : "#{sent_to} already has an account.") if redundant?
51
+ <%- else -%>
52
+ errors.add(:base, "#{sent_to} already has an account.") if redundant?
53
+ <%- end -%>
54
+ end
55
+ <%- end -%>
56
+
57
+ def offer
58
+ update!(expires_at: <%= identity.config_reference %>.tokens.invitation.from_now)
59
+ signed_id = generate_token_for(:invitation)
60
+ <%- if multi_channel_invitable? -%>
61
+
62
+ if email_addressed?
63
+ <%= identity.mailer_class %>.with(invitation: self, signed_id:, to: sent_to).invitation.deliver_later
64
+ else
65
+ Sms.deliver_later(to: sent_to, body: sms_message(signed_id))
66
+ end
67
+ <%- elsif email_invitable? -%>
68
+ <%= identity.mailer_class %>.with(invitation: self, signed_id:, to: sent_to).invitation.deliver_later
69
+ <%- else -%>
70
+ Sms.deliver_later(to: sent_to, body: sms_message(signed_id))
71
+ <%- end -%>
72
+ signed_id
73
+ end
74
+
75
+ def accepted?
76
+ accepted_at.present?
77
+ end
78
+
79
+ <%- unless multi_channel_invitable? -%>
80
+ def sent_to_channel = "<%= email_invitable? ? "email" : "phone" %>"
81
+
82
+ <%- end -%>
83
+ def email_addressed? = sent_to_channel == "email"
84
+ def phone_addressed? = sent_to_channel == "phone"
85
+
86
+ def addressed_to?(user)
87
+ sent_to == user[sent_to_channel]
88
+ end
89
+
90
+ def redundant?
91
+ <%- if teams? -%>
92
+ (team ? team.<%= identity.plural %> : <%= identity.class_name %>).exists?(sent_to_channel => sent_to)
93
+ <%- else -%>
94
+ <%= identity.class_name %>.exists?(sent_to_channel => sent_to)
95
+ <%- end -%>
96
+ end
97
+
98
+ def accept(user)
99
+ return false unless addressed_to?(user)
100
+
101
+ with_lock do
102
+ return false if accepted?
103
+
104
+ update!(invitee: user, accepted_at: Time.current)
105
+ <%- if teams? -%>
106
+ team.memberships.find_or_create_by!(<%= identity.singular %>: user) if team
107
+ <%- end -%>
108
+ end
109
+
110
+ true
111
+ end
112
+ <%- if phone_invitable? -%>
113
+
114
+ private
115
+
116
+ def sms_message(signed_id)
117
+ url = Rails.application.routes.url_helpers.<%= identity.namespaced? ? "#{identity.route_scope}_accept_invitation_url" : "accept_invitation_url" %>(
118
+ token: signed_id, **Rails.application.config.action_mailer.default_url_options)
119
+ "#{inviter.<%= principals.display.column %>} invited you to #{<%= identity.config_reference %>.app_name}: #{url}"
120
+ end
121
+ <%- end -%>
122
+ end
@@ -0,0 +1,16 @@
1
+ class <%= identity.membership_class %> < ApplicationRecord
2
+ belongs_to :<%= identity.singular %>
3
+ belongs_to :team
4
+
5
+ validates :<%= identity.foreign_key %>, uniqueness: { scope: :team_id }
6
+ <%- if session_carries_team? -%>
7
+
8
+ after_destroy :clear_active_team_from_sessions
9
+
10
+ private
11
+
12
+ def clear_active_team_from_sessions
13
+ <%= identity.session_class %>.where(<%= identity.foreign_key %>:, team_id:).update_all(team_id: nil)
14
+ end
15
+ <%- end -%>
16
+ end
@@ -0,0 +1,12 @@
1
+ class <%= identity.omniauth_identity_class %> < ApplicationRecord
2
+ belongs_to :<%= identity.singular %>
3
+
4
+ validates :provider, presence: true
5
+ validates :uid, presence: true, uniqueness: { scope: :provider }
6
+
7
+ # The provider named by a sign-in, e.g. :google for google_oauth2.
8
+ def self.via(provider) = provider.to_s.split("_").first.downcase.to_sym
9
+
10
+ def via = self.class.via(provider)
11
+ def provider_name = via.to_s.titleize
12
+ end
@@ -0,0 +1,3 @@
1
+ class <%= identity.password_history_class %> < ApplicationRecord
2
+ belongs_to :<%= identity.singular %>
3
+ end
@@ -0,0 +1,216 @@
1
+ class <%= identity.pending_registration_class %> < ApplicationRecord
2
+ include <%= identity.principals_concern %>
3
+ <%- if password? -%>
4
+ include <%= identity.password_policy_concern %>
5
+ <%- end -%>
6
+ <%- if sms? -%>
7
+ include <%= identity.class_name %>::SmsChallengeable
8
+ <%- end -%>
9
+ <%- if invitable? -%>
10
+
11
+ belongs_to :invitation<%= invite_only? ? "" : ", optional: true" %>
12
+ <%- end -%>
13
+
14
+ Completion = Struct.new(:status, :user, :via)
15
+ <%- if pre_account_passkey_enrollment? -%>
16
+
17
+ attr_accessor :passkey
18
+ <%- end -%>
19
+
20
+ <%- if email_gated_registration? -%>
21
+ generates_token_for :registration_email_verification, expires_in: <%= identity.config_reference %>.tokens.registration_email_verification do
22
+ [ email, email_verified_at ]
23
+ end
24
+ <%- end -%>
25
+
26
+ scope :expired, -> { where(updated_at: ...<%= identity.config_reference %>.tokens.pending_registration.ago) }
27
+ scope :unexpired, -> { where(updated_at: <%= identity.config_reference %>.tokens.pending_registration.ago..) }
28
+ <%- if pre_account_passkey_enrollment? -%>
29
+
30
+ before_validation on: :create do
31
+ self.webauthn_handle ||= WebAuthn.generate_user_id
32
+ end
33
+ <%- end -%>
34
+ <%- if invitable? && (email_verifiable? || phone_verifiable?) -%>
35
+
36
+ before_create :verify_invited_channel
37
+ <%- end -%>
38
+ <%- if principals.username -%>
39
+
40
+ validate :username_not_taken
41
+ <%- end -%>
42
+ <%- if guest_excused_from_phone? -%>
43
+
44
+ def guest? = false
45
+ <%- end -%>
46
+ <%- if password_nullable? && !password_blank_rows_all? -%>
47
+
48
+ def password_may_be_blank? = <%= staged_password_blank_rows_expression %>
49
+ <%- end -%>
50
+
51
+ def next_step
52
+ <%- if omniauth_registration? -%>
53
+ return :complete_profile if <%= omniauth_profile_missing_condition %>
54
+ <%- end -%>
55
+ <%- if email_verifiable? -%>
56
+ return :verify_email if email.present? && email_verified_at.blank?
57
+ <%- end -%>
58
+ <%- if phone_gated_registration? -%>
59
+ return :verify_phone if phone.present? && phone_verified_at.blank?
60
+ <%- end -%>
61
+ <%- if credential_staged_registration? -%>
62
+ return :enroll_credential unless sign_in_methods.any?
63
+ <%- end -%>
64
+ :complete
65
+ end
66
+
67
+ def sign_in_methods
68
+ methods = []
69
+ <%- if password? -%>
70
+ methods << :password if password_digest.present?
71
+ <%- end -%>
72
+ <%- if pre_account_passkey_enrollment? -%>
73
+ methods << :passkey if passkey
74
+ <%- end -%>
75
+ <%- if pending_registration_holds_provider? -%>
76
+ methods << :omniauth if provider.present?
77
+ <%- end -%>
78
+ <%- if magic_link? -%>
79
+ methods << :magic_link if email.present?<%= email_verifiable? ? " && email_verified_at.present?" : "" %>
80
+ <%- end -%>
81
+ <%- if sms_code? -%>
82
+ methods << :sms_code if phone.present?<%= phone_verifiable? ? " && phone_verified_at.present?" : "" %>
83
+ <%- end -%>
84
+ methods
85
+ end
86
+
87
+ def complete
88
+ return Completion.new(:invalid) unless valid?
89
+
90
+ user = <%= identity.class_name %>.new(account_attributes)
91
+ <%- if pre_account_passkey_enrollment? -%>
92
+ user.webauthn_credentials.build(**passkey, name: "Passkey"<%= factor_attr(:first_factor) %>) if passkey
93
+ <%- end -%>
94
+ <%- if pending_registration_holds_provider? -%>
95
+ user.omniauth_identities.build(provider:, uid:) if provider
96
+ <%- end -%>
97
+
98
+ unless user.save
99
+ errors.merge!(user.errors)
100
+ return collision_result || Completion.new(:invalid)
101
+ end
102
+
103
+ <%- if invitable? -%>
104
+ invitation&.accept(user)
105
+ <%- end -%>
106
+ destroy!
107
+ <%- if pending_registration_holds_provider? -%>
108
+ Completion.new(:created, user, provider ? <%= identity.omniauth_identity_class %>.via(provider) : :registration)
109
+ <%- else -%>
110
+ Completion.new(:created, user, :registration)
111
+ <%- end -%>
112
+ rescue ActiveRecord::RecordNotUnique
113
+ collision_result || raise
114
+ end
115
+ <%- if email_gated_registration? -%>
116
+
117
+ def request_email_verification
118
+ touch # keeps :expired from reaping this while verification is in progress
119
+ return if <%= identity.class_name %>.exists?(email: email)
120
+
121
+ signed_id = generate_token_for(:registration_email_verification)
122
+ <%= identity.mailer_class %>.with(signed_id: signed_id, to: email).registration_email_verification.deliver_later
123
+ end
124
+ <%- end -%>
125
+ <%- if phone_gated_registration? -%>
126
+
127
+ def request_phone_verification
128
+ touch # keeps :expired from reaping this while verification is in progress
129
+ return if <%= identity.class_name %>.exists?(phone: phone)
130
+
131
+ issue_sms_challenge
132
+ end
133
+ <%- end -%>
134
+
135
+ private
136
+ <%- if invitable? && (email_verifiable? || phone_verifiable?) -%>
137
+
138
+ def verify_invited_channel
139
+ return unless invitation&.addressed_to?(self)
140
+
141
+ <%- if multi_channel_invitable? && email_verifiable? && phone_verifiable? -%>
142
+ if invitation.email_addressed?
143
+ self.email_verified_at = Time.current
144
+ <%- if principals.phone.optional? -%>
145
+ self.phone = nil
146
+ <%- end -%>
147
+ else
148
+ self.phone_verified_at = Time.current
149
+ <%- if principals.email.optional? -%>
150
+ self.email = nil
151
+ <%- end -%>
152
+ end
153
+ <%- elsif email_invitable? && email_verifiable? -%>
154
+ self.email_verified_at = Time.current
155
+ <%- elsif phone_invitable? && phone_verifiable? -%>
156
+ self.phone_verified_at = Time.current
157
+ <%- end -%>
158
+ end
159
+ <%- end -%>
160
+
161
+ def account_attributes
162
+ attributes = {}
163
+ <%- principals.each do |principal| -%>
164
+ <%- if principal.type == :email && email_verifiable? -%>
165
+ attributes[:email] = email if email_verified_at.present?
166
+ <%- elsif principal.type == :phone && phone_verifiable? -%>
167
+ attributes[:phone] = phone if phone_verified_at.present?
168
+ <%- else -%>
169
+ attributes[:<%= principal.column %>] = <%= principal.column %>
170
+ <%- end -%>
171
+ <%- end -%>
172
+ <%- if password? -%>
173
+ attributes[:password_digest] = password_digest
174
+ <%- end -%>
175
+ <%- if pre_account_passkey_enrollment? -%>
176
+ attributes[:webauthn_handle] = webauthn_handle
177
+ <%- end -%>
178
+ attributes
179
+ end
180
+
181
+ def collision_result
182
+ <%- if pending_registration_holds_provider? -%>
183
+ if provider.present? && <%= identity.omniauth_identity_class %>.exists?(provider:, uid:)
184
+ destroy!
185
+ return Completion.new(:taken)
186
+ end
187
+ <%- end -%>
188
+ <%- if principals.username -%>
189
+ if username.present? && <%= identity.class_name %>.where("LOWER(username) = ?", username.downcase).exists?
190
+ errors.add(:username, "has already been taken")
191
+ return Completion.new(:invalid)
192
+ end
193
+ <%- end -%>
194
+ <%- if principals.email -%>
195
+ if email.present? && <%= identity.class_name %>.exists?(email: email)
196
+ destroy!
197
+ return Completion.new(:taken)
198
+ end
199
+ <%- end -%>
200
+ <%- if principals.phone -%>
201
+ if <%= phone_verifiable? ? "phone_verified_at.present?" : "phone.present?" %> && <%= identity.class_name %>.exists?(phone: phone)
202
+ destroy!
203
+ return Completion.new(:taken)
204
+ end
205
+ <%- end -%>
206
+ nil
207
+ end
208
+ <%- if principals.username -%>
209
+
210
+ def username_not_taken
211
+ return if username.blank?
212
+
213
+ errors.add(:username, "has already been taken") if <%= identity.class_name %>.where("LOWER(username) = ?", username.downcase).exists?
214
+ end
215
+ <%- end -%>
216
+ end
@@ -0,0 +1,41 @@
1
+ class <%= identity.recovery_code_class %> < ApplicationRecord
2
+ belongs_to :<%= identity.singular %>
3
+
4
+ class << self
5
+ def regenerate_for(user)
6
+ codes = Array.new(<%= identity.config_reference %>.second_factor.recovery_code_count) { generate_code }
7
+
8
+ transaction do
9
+ user.recovery_codes.delete_all
10
+ user.recovery_codes.create!(codes.map { |code| { code: code } })
11
+ end
12
+
13
+ codes
14
+ end
15
+
16
+ def redeem(user:, code:)
17
+ return false unless user.recovery_codes.where(code_digest: digest(code)).delete_all == 1
18
+
19
+ <%- if trackable? -%>
20
+ user.record_event(
21
+ "<%= identity.singular %>.recovery_code_used",
22
+ actor: user,
23
+ data: { remaining: user.recovery_codes.count }
24
+ )
25
+ <%- end -%>
26
+ true
27
+ end
28
+
29
+ def generate_code
30
+ SecureRandom.alphanumeric(<%= identity.config_reference %>.second_factor.recovery_code_length).downcase
31
+ end
32
+
33
+ def digest(code)
34
+ OpenSSL::HMAC.hexdigest("SHA256", Rails.application.secret_key_base, code.to_s) # An HMAC prevents a database leak from exposing recovery codes that can be used to bypass the second factor.
35
+ end
36
+ end
37
+
38
+ def code=(plaintext)
39
+ self.code_digest = self.class.digest(plaintext)
40
+ end
41
+ end
@@ -0,0 +1,46 @@
1
+ class <%= identity.session_class %> < ApplicationRecord
2
+ belongs_to :<%= identity.singular %>
3
+ <%- if session_carries_team? -%>
4
+ belongs_to :team, optional: true
5
+ <%- end -%>
6
+ <%- if identity.namespaced? -%>
7
+
8
+ alias_method :user, :<%= identity.singular %>
9
+ <%- end -%>
10
+ <%- if timeoutable? -%>
11
+
12
+ def self.idle_timeout
13
+ <%= identity.config_reference %>.session.idle_timeout
14
+ end
15
+
16
+ scope :active, -> { where(updated_at: idle_timeout.ago..) }
17
+ <%- if guestable? -%>
18
+ scope :timed_out, -> { where(updated_at: ...idle_timeout.ago).where.not(<%= identity.singular %>: <%= identity.class_name %>.guests) }
19
+ <%- else -%>
20
+ scope :timed_out, -> { where(updated_at: ...idle_timeout.ago) }
21
+ <%- end -%>
22
+ <%- end -%>
23
+
24
+ before_create do
25
+ self.user_agent = <%= identity.current_class %>.user_agent
26
+ self.ip_address = <%= identity.current_class %>.ip_address
27
+ end
28
+ <%- if trackable? -%>
29
+
30
+ after_create do
31
+ <%- if guestable? -%>
32
+ <%= identity.singular %>.record_event("<%= identity.singular %>.signed_in", actor: <%= identity.singular %>, data: { via:, user_agent:, ip_address: }) unless <%= identity.singular %>.guest?
33
+ <%- else -%>
34
+ <%= identity.singular %>.record_event("<%= identity.singular %>.signed_in", actor: <%= identity.singular %>, data: { via:, user_agent:, ip_address: })
35
+ <%- end -%>
36
+ end
37
+
38
+ after_destroy do
39
+ <%- if guestable? -%>
40
+ <%= identity.singular %>.record_event("<%= identity.singular %>.signed_out", actor: <%= identity.singular %>, data: { via:, user_agent:, ip_address: }) unless <%= identity.singular %>.guest?
41
+ <%- else -%>
42
+ <%= identity.singular %>.record_event("<%= identity.singular %>.signed_out", actor: <%= identity.singular %>, data: { via:, user_agent:, ip_address: })
43
+ <%- end -%>
44
+ end
45
+ <%- end -%>
46
+ end
@@ -0,0 +1,12 @@
1
+ class <%= identity.team_class %> < ApplicationRecord
2
+ has_many :memberships, dependent: :destroy
3
+ has_many :<%= identity.plural %>, through: :memberships
4
+ <%- if invitable? -%>
5
+ has_many :invitations, dependent: :destroy
6
+ <%- end -%>
7
+ <%- if session_carries_team? -%>
8
+ has_many :sessions, dependent: :nullify
9
+ <%- end -%>
10
+
11
+ validates :name, presence: true
12
+ end
@@ -0,0 +1,59 @@
1
+ class <%= identity.class_name %> < ApplicationRecord
2
+ include <%= identity.principals_concern %>
3
+ include Authenticatable
4
+ <%- if password? -%>
5
+ include <%= identity.password_policy_concern %>
6
+ <%- end -%>
7
+ <%- if password_rotatable? -%>
8
+ include PasswordRotatable
9
+ <%- end -%>
10
+ <%- if password_historical? -%>
11
+ include PasswordHistorical
12
+ <%- end -%>
13
+ <%- if recoverable? -%>
14
+ include Recoverable
15
+ <%- end -%>
16
+ <%- if magic_link? -%>
17
+ include MagicLinkable
18
+ <%- end -%>
19
+ <%- if second_factor? -%>
20
+ include SecondFactor
21
+ <%- end -%>
22
+ <%- if deadboltable? -%>
23
+ include Deadboltable
24
+ <%- end -%>
25
+ <%- if bannable? -%>
26
+ include Bannable
27
+ <%- end -%>
28
+ <%- if sms? -%>
29
+ include SmsChallengeable
30
+ <%- end -%>
31
+ <%- if email_change_verification? -%>
32
+ include EmailVerifiable
33
+ <%- end -%>
34
+ <%- if phone_change_verification? -%>
35
+ include PhoneVerifiable
36
+ <%- end -%>
37
+ <%- if teams? -%>
38
+ include TeamMember
39
+ <%- end -%>
40
+ <%- if adminable? -%>
41
+ include Adminable
42
+ <%- end -%>
43
+ <%- if trackable? -%>
44
+ include Eventable
45
+ <%- end -%>
46
+ <%- if guestable? -%>
47
+ include Guestable
48
+ <%- end -%>
49
+ <%- if invitable? -%>
50
+ include Invitable
51
+ <%- end -%>
52
+ <%- unique_principals = principals.reject { |principal| principal.channel? && coy? } -%>
53
+ <%- if unique_principals.any? -%>
54
+
55
+ <%- unique_principals.each do |principal| -%>
56
+ validates :<%= principal.column %>, uniqueness: <%= principal.channel? ? "true, allow_nil: true" : "{ case_sensitive: false }" %>
57
+ <%- end -%>
58
+ <%- end -%>
59
+ end
@@ -0,0 +1,12 @@
1
+ class <%= identity.webauthn_credential_class %> < ApplicationRecord
2
+ belongs_to :<%= identity.singular %>
3
+
4
+ validates :name, presence: true
5
+ <%- if passkey_and_security_keys? -%>
6
+
7
+ enum :authentication_factor, { first_factor: 0, second_factor: 1 }
8
+
9
+ scope :passkeys, -> { first_factor }
10
+ scope :second_factors, -> { second_factor }
11
+ <%- end -%>
12
+ end
@@ -0,0 +1,41 @@
1
+ namespace :authnz_eleven do
2
+ <%- if identity.namespaced? -%>
3
+ namespace :<%= identity.task_namespace %> do
4
+ <%- end -%>
5
+ <%- if guestable? -%>
6
+ desc "Delete stale guest users (and their sessions) older than config.guest.ttl"
7
+ task delete_expired_guests: :environment do
8
+ scope = <%= identity.class_name %>.expired_guests
9
+
10
+ count = scope.count
11
+ scope.find_each(&:destroy)
12
+
13
+ puts "Deleted #{count} expired guest user(s)."
14
+ end
15
+ <%- end -%>
16
+ <%- if guestable? && timeoutable? -%>
17
+
18
+ <%- end -%>
19
+ <%- if pending_registration? -%>
20
+ desc "Delete sign-ups abandoned for longer than config.tokens.pending_registration"
21
+ task delete_expired_pending_registrations: :environment do
22
+ count = <%= identity.pending_registration_class %>.expired.destroy_all.size
23
+
24
+ puts "Deleted #{count} expired pending registration(s)."
25
+ end
26
+ <%- end -%>
27
+ <%- if pending_registration? && timeoutable? -%>
28
+
29
+ <%- end -%>
30
+ <%- if timeoutable? -%>
31
+ desc "Delete sessions that have timed out (idle past config.session.idle_timeout)"
32
+ task delete_timed_out_sessions: :environment do
33
+ count = <%= identity.session_class %>.timed_out.delete_all
34
+
35
+ puts "Deleted #{count} timed-out session(s)."
36
+ end
37
+ <%- end -%>
38
+ <%- if identity.namespaced? -%>
39
+ end
40
+ <%- end -%>
41
+ end