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,275 @@
1
+ # Generated by authnz_eleven <%= AuthnzEleven::VERSION %> with:
2
+ #
3
+ <%- generation_command_lines.each do |line| -%>
4
+ # <%= line %>
5
+ <%- end -%>
6
+ #
7
+ # Configuration for your generated auth system. Adjust them to suit
8
+ # your needs.
9
+ #
10
+ # <%= identity.config_constant %> is an alias for <%= identity.config_storage %>.
11
+ # Both refer to the same config, so you can use either. The generated code uses
12
+ # <%= identity.config_constant %>.
13
+ <%= identity.config_storage %> = ActiveSupport::OrderedOptions.new
14
+ <%= identity.config_constant %> = <%= identity.config_storage %>
15
+
16
+ <%= identity.config_constant %>.tap do |config|
17
+ # Shown as the issuer in authenticator apps and used in mailer subjects.
18
+ config.app_name = Rails.application.class.module_parent_name
19
+
20
+ # How often to save a session's last activity to the database. No need to
21
+ # write it on every request.
22
+ config.session = ActiveSupport::OrderedOptions.new
23
+ config.session.activity_write_interval = 15.minutes
24
+ <%- if timeoutable? -%>
25
+ # Sign out sessions after they are inactive for this long. They are deleted on the user's
26
+ # next request. For users who never come back, run the included cleanup task:
27
+ # `authnz_eleven:delete_timed_out_sessions`.
28
+ config.session.idle_timeout = 7.days
29
+ <%- end -%>
30
+ <%- if rememberable? -%>
31
+ # How long the "remember me" cookie lasts. Without "remember me", the cookie
32
+ # lasts only until the browser closes.
33
+ config.session.remember_for = 2.weeks
34
+ <%- end -%>
35
+ <%- if max_sessionable? -%>
36
+ # How many devices a <%= identity.singular %> can be signed in on at once.
37
+ # "Remember me" sessions count too. If a new sign-in would exceed the limit,
38
+ <%- if max_sessions_prompt? -%>
39
+ # the user is held at the session list until they sign one out
40
+ # (--max-sessionable=prompt). Set to nil to allow any number of sessions.
41
+ <%- else -%>
42
+ # the least-recently-active session is signed out to make room
43
+ # (--max-sessionable=evict). Set to nil to allow any number of sessions.
44
+ <%- end -%>
45
+ config.session.concurrent_limit = 3
46
+ <%- end -%>
47
+ <%- if guestable? -%>
48
+
49
+ # How long to keep a guest <%= identity.singular %>. This is measured from creation, not last activity.
50
+ # Guests are not authenticated, so we don't "sign them out" due to inactivity.
51
+ <%- if timeoutable? -%>
52
+ # session.idle_timeout does not apply to guests.
53
+ <%- end -%>
54
+ # The guest cookie lives for this long too, including across browser restarts.
55
+ # Run `authnz_eleven:delete_expired_guests` to delete expired guests.
56
+ config.guest = ActiveSupport::OrderedOptions.new
57
+ config.guest.ttl = 30.days
58
+ <%- end -%>
59
+ <%- if impersonatable? -%>
60
+
61
+ # How long an admin can impersonate another <%= identity.singular %>. Activity does
62
+ # not extend this. Once it expires, the next request returns the admin to the
63
+ # admin area as themselves, and that request is not carried out, since it was made
64
+ # as someone else.
65
+ config.impersonation = ActiveSupport::OrderedOptions.new
66
+ config.impersonation.duration = 1.hour
67
+ <%- end -%>
68
+
69
+ <%- if principals.username -%>
70
+ # Usernames nobody may register, like "admin" or "support". Edit
71
+ # config/forbidden_usernames.txt to add or remove names, or replace this with
72
+ # your own list. It must be a Set of downcased strings. Blank lines and "#"
73
+ # comments in the file are ignored.
74
+ #
75
+ # To change the format or the 3–20 character limit, edit the model's `format:`
76
+ # validation. When sign-in accepts multiple identifiers, it checks for "@"
77
+ # first (email), then a letter (username), otherwise it tries a phone number.
78
+ # Keep usernames free of "@" and require at least one letter so they cannot
79
+ # be mistaken for either.
80
+ config.username = ActiveSupport::OrderedOptions.new
81
+ config.username.forbidden = File.readlines(Rails.root.join("config/forbidden_usernames.txt"), chomp: true)
82
+ .map { |line| line.strip.downcase }
83
+ .reject { |line| line.empty? || line.start_with?("#") }
84
+ .to_set
85
+
86
+ <%- end -%>
87
+ <%- if principals.phone -%>
88
+ # Interpret numbers without "+" using this country:
89
+ # "US": (415) 826-4001 → +14158264001
90
+ # "GB": 020 7183 8750 → +442071838750
91
+ # An array, like %w[US GB], tries countries in order.
92
+ # Numbers with "+" already specify their country and ignore this default.
93
+ # This controls parsing, not which countries are allowed.
94
+ # Keep a default to avoid guessing from leading digits. Without one, a
95
+ # number from Gainesville, Florida (Go Gators! 🐊), (352) 555-0166, is parsed
96
+ # as +352 5550166 (Luxembourg).
97
+ config.phone = ActiveSupport::OrderedOptions.new
98
+ config.phone.default_country = %w[US CA]
99
+
100
+ # Accepted phone-number countries. nil allows all countries.
101
+ config.phone.allowed_countries = nil
102
+ # config.phone.allowed_countries = %w[US CA]
103
+
104
+ <%- end -%>
105
+ <%- if password? -%>
106
+ # Password rules.
107
+ config.password = ActiveSupport::OrderedOptions.new
108
+ config.password.minimum_length = 10
109
+ <%- if password_rotatable? -%>
110
+ # Require a new password after this long. On their next request, the user is
111
+ # held at the change-password page until they change it. Set to nil to stop
112
+ # expiring passwords.
113
+ #
114
+ # I do not recommend scheduled rotation unless your compliance policy requires
115
+ # it. NIST SP 800-63B advises against it because it encourages predictable
116
+ # passwords. Prefer longer passwords and breach-checking (--pwned).
117
+ config.password.maximum_age = 90.days
118
+ <%- end -%>
119
+ <%- if password_historical? -%>
120
+ # How many previous passwords to check when refusing password reuse. The
121
+ # current password is also checked. Stored digests are small, so you can raise
122
+ # this if your policy requires a longer history. Set to nil to keep archiving
123
+ # passwords without rejecting reuse.
124
+ config.password.history_depth = 5
125
+ <%- end -%>
126
+ <%- if strong_passwords? -%>
127
+ # Minimum zxcvbn strength score (0–4). 3 is "safely unguessable", and 4 is the
128
+ # strictest. Passwords scoring below this are rejected by the Authenticatable
129
+ # concern.
130
+ config.password.minimum_strength = 3
131
+ <%- end -%>
132
+ <%- if pwned? -%>
133
+
134
+ # Check new passwords against Have I Been Pwned. Enabled only in production
135
+ # because it makes an HTTP request and would reject the test fixture passwords.
136
+ config.password.check_breaches = Rails.env.production?
137
+ <%- end -%>
138
+
139
+ <%- end -%>
140
+ # How long links, codes, and challenges remain valid.
141
+ config.tokens = ActiveSupport::OrderedOptions.new
142
+ <%- if email_gated_registration? -%>
143
+ config.tokens.registration_email_verification = 2.days
144
+ <%- end -%>
145
+ <%- if email_change_verification? -%>
146
+ config.tokens.email_change_verification = 2.days
147
+ <%- end -%>
148
+ <%- if recoverable? -%>
149
+ config.tokens.password_reset = 20.minutes
150
+ <%- end -%>
151
+ <%- if magic_link? -%>
152
+ config.tokens.magic_link = 20.minutes
153
+ <%- end -%>
154
+ <%- if second_factor? -%>
155
+ config.tokens.mfa_challenge = 20.minutes
156
+ <%- end -%>
157
+ <%- if invitable? -%>
158
+ config.tokens.invitation = 7.days
159
+ <%- end -%>
160
+ <%- if sms? -%>
161
+ config.tokens.sms_code = 10.minutes
162
+ <%- end -%>
163
+ <%- if pending_registration? -%>
164
+ # How long an unfinished sign-up can sit before cleanup deletes it. Each step,
165
+ # and each link or code sent, restarts the clock. Keep this longer than the
166
+ # registration links above so cleanup doesn't delete a sign-up whose link is
167
+ # still valid.
168
+ config.tokens.pending_registration = 3.days
169
+ <%- end -%>
170
+ <%- if second_factor? -%>
171
+
172
+ # Second-factor authentication.
173
+ config.second_factor = ActiveSupport::OrderedOptions.new
174
+ <%- if totp? -%>
175
+ config.second_factor.totp_drift_behind = 10
176
+ <%- end -%>
177
+ config.second_factor.recovery_code_count = 10
178
+ config.second_factor.recovery_code_length = 10
179
+
180
+ # Which sign-in methods require a second factor, if the user has enrolled one.
181
+ # Methods not listed are accepted on their own. Passkey sign-in is not listed
182
+ # by default because it is already phishing-resistant.
183
+ #
184
+ # Adjust this to suit your security/convenience trade-off. For example, add
185
+ # :magic_link if access to the user's inbox is not enough for your app.
186
+ #
187
+ <%- if omniauth? -%>
188
+ # For social login, use the provider name, not :omniauth. For example, :google
189
+ # for google_oauth2. Add providers if you don't want to rely on their own MFA.
190
+ # :omniauth itself matches nothing.
191
+ #
192
+ <%- end -%>
193
+ # Do not add :second_factor. It is used after a successful second-factor
194
+ # challenge, so requiring another would cause a redirect loop. :registration
195
+ # is also unnecessary: a newly created account has no second factor yet.
196
+ config.second_factor.needed_after = %i[<%= default_needed_after.join(" ") %>]
197
+ <%- end -%>
198
+
199
+ <%- if deadboltable? -%>
200
+ # How many consecutive failed password sign-ins deadbolt the password door,
201
+ # and how long it stays shut. Once the duration passes, it unlocks and the
202
+ # count starts over. A completed password reset unlocks it early, or you can
203
+ # call `user.undeadbolt!` yourself.
204
+ #
205
+ # Other sign-in methods still work. An attacker guessing at someone's password
206
+ # should not also lock them out of their passkey or magic link.
207
+ #
208
+ # Set maximum_attempts to nil to count failures without locking password
209
+ # sign-in.
210
+ config.deadbolt = ActiveSupport::OrderedOptions.new
211
+ config.deadbolt.maximum_attempts = 10
212
+ config.deadbolt.duration = 1.day
213
+
214
+ <%- end -%>
215
+ <%- if sms? -%>
216
+ # Wrong guesses allowed per texted code. This is separate from the rate limits,
217
+ # which limit how often codes can be sent.
218
+ config.sms = ActiveSupport::OrderedOptions.new
219
+ config.sms.max_attempts = 3
220
+
221
+ <%- end -%>
222
+ # Request limits, written as { to: number_of_requests, within: duration }.
223
+ # Limits sends, not guesses.
224
+ config.rate_limits = ActiveSupport::OrderedOptions.new
225
+ <%- if password? -%>
226
+ config.rate_limits.sign_in = { to: 10, within: 10.minutes }
227
+ <%- end -%>
228
+ <%- if recoverable? -%>
229
+ config.rate_limits.password_reset = { to: 3, within: 1.hour }
230
+ <%- end -%>
231
+ <%- if second_factor? -%>
232
+ <%- if totp? -%>
233
+ config.rate_limits.totp_challenge = { to: 10, within: 5.minutes }
234
+ <%- end -%>
235
+ <%- if sms_second_factor? -%>
236
+ config.rate_limits.sms_second_factor = { to: 5, within: 1.hour }
237
+ <%- end -%>
238
+ config.rate_limits.recovery_code_challenge = { to: 10, within: 1.hour }
239
+ <%- end -%>
240
+ <%- if magic_link? -%>
241
+ config.rate_limits.magic_link = { to: 10, within: 1.hour }
242
+ <%- end -%>
243
+ <%- if email_verifiable? -%>
244
+ # Requests and resends for email confirmation.
245
+ config.rate_limits.email_verification = { to: 5, within: 1.hour }
246
+ <%- end -%>
247
+ <%- if invitable? -%>
248
+ config.rate_limits.invitation = { to: 5, within: 1.hour }
249
+ <%- end -%>
250
+ <%- if phone_verifiable? -%>
251
+ # Requests and resends for phone confirmation. Every text costs money.
252
+ config.rate_limits.phone_verification = { to: 5, within: 1.hour }
253
+ <%- end -%>
254
+ <%- if sms_code? -%>
255
+ # Texted sign-in codes. These typically cost more than emails.
256
+ config.rate_limits.sms_sign_in = { to: 5, within: 1.hour }
257
+ <%- end -%>
258
+ <%- if sudoable? -%>
259
+ # Failed sudo challenges per session. Successful confirmation resets the count.
260
+ config.rate_limits.sudo = { to: 10, within: 10.minutes }
261
+ <%- end -%>
262
+ <%- if sudoable? -%>
263
+
264
+ # How long a sudo confirmation keeps protected pages unlocked.
265
+ config.sudo = ActiveSupport::OrderedOptions.new
266
+ config.sudo.window = 15.minutes
267
+ <%- end -%>
268
+ <%- if password? -%>
269
+ # Submission limits for settings forms that require the current password.
270
+ config.rate_limits.password_challenge = { to: 5, within: 10.minutes }
271
+ <%- end -%>
272
+ <%- if typed_registration? -%>
273
+ config.rate_limits.sign_up = { to: 5, within: 1.hour }
274
+ <%- end -%>
275
+ end
@@ -0,0 +1,9 @@
1
+ OmniAuth.config.request_validation_phase = OmniAuth::AuthenticityTokenProtection.new(key: :_csrf_token)
2
+
3
+ # Replace :developer with your real provider(s), e.g.
4
+ # provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"], scope: "email,profile"
5
+ # provider :github, ENV["GITHUB_ID"], ENV["GITHUB_SECRET"], scope: "user:email"
6
+ Rails.application.config.middleware.use OmniAuth::Builder do
7
+ options path_prefix: "<%= omniauth_path_prefix %>"
8
+ provider :developer unless Rails.env.production?
9
+ end
@@ -0,0 +1,24 @@
1
+ module TeamSlug
2
+ PREFIX = %r{\A/(\d+)(?=/|\z)}
3
+
4
+ class Extractor
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ request = ActionDispatch::Request.new(env)
11
+
12
+ if (match = PREFIX.match(request.path_info)) && (team = Team.find_by(id: match[1]))
13
+ request.script_name = "#{request.script_name}/#{match[1]}"
14
+ request.path_info = match.post_match.presence || "/"
15
+
16
+ Current.team = team
17
+ end
18
+
19
+ @app.call(request.env)
20
+ end
21
+ end
22
+ end
23
+
24
+ Rails.application.config.middleware.insert_after Rack::TempfileReaper, TeamSlug::Extractor
@@ -0,0 +1,14 @@
1
+ WebAuthn.configure do |config|
2
+ # The fully-qualified origin(s) of your app. Set per environment (use your
3
+ # real https origin(s) in production)
4
+ config.allowed_origins = ENV.fetch("WEBAUTHN_ORIGIN", "http://localhost:3000").split(",")
5
+
6
+ # The domain WebAuthn credentials are scoped to (no scheme, port, or path).
7
+ # Set WEBAUTHN_RP_ID in production. Every allowed origin's host must equal
8
+ # this value or be its subdomain; for example, app.example.com and
9
+ # admin.example.com can share an RP ID of example.com.
10
+ config.rp_id = ENV.fetch("WEBAUTHN_RP_ID", "localhost")
11
+
12
+ # Relying-party display name, shown by some authenticators.
13
+ config.rp_name = Rails.application.class.module_parent_name
14
+ end
@@ -0,0 +1,11 @@
1
+ en:
2
+ activerecord:
3
+ attributes:
4
+ <%= identity.singular %>:
5
+ pending_email: "Email"
6
+ pending_phone: "Phone"
7
+ password_challenge: "Current password"
8
+ <%- if invitable? -%>
9
+ <%= identity.invitation_file %>:
10
+ sent_to: "<%= invitation_recipient_label %>"
11
+ <%- end -%>
@@ -0,0 +1,6 @@
1
+ class <%= identity.nested_module("ActiveTeamsController") %> < <%= identity.controller_superclass %>
2
+ def create
3
+ set_session_team!(<%= identity.current_class %>.user.teams.find(params[:team_id]))
4
+ redirect_to <%= identity.nested_helper("root_path") %>, notice: "Switched team."
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ class <%= admin_module %>::DashboardController < <%= admin_controller %>
2
+ def index
3
+ end
4
+ end
@@ -0,0 +1,17 @@
1
+ class <%= admin_module %>::SessionsController < <%= admin_controller %>
2
+ def index
3
+ sessions = <%= identity.session_class %>.includes(:<%= identity.singular %><%= session_carries_team? ? ", :team" : "" %>).order(created_at: :desc)
4
+
5
+ if params[:user_id].present?
6
+ sessions = sessions.where(<%= identity.foreign_key %>: params[:user_id])
7
+ @filtered_user = <%= identity.class_name %>.find(params[:user_id])
8
+ end
9
+
10
+ @sessions = sessions.limit(100)
11
+ end
12
+
13
+ def destroy
14
+ <%= identity.session_class %>.find(params[:id]).destroy!
15
+ redirect_to <%= admin_helper("sessions_path") %>, notice: "Session ended.", status: :see_other
16
+ end
17
+ end
@@ -0,0 +1,91 @@
1
+ class <%= admin_module %>::UsersController < <%= admin_controller %>
2
+ before_action :set_user, only: %i[show destroy destroy_all_sessions<%= " reset_second_factor" if second_factor? %><%= " undeadbolt" if deadboltable? %><%= " ban unban" if bannable? %><%= " impersonate" if impersonatable? %>]
3
+ before_action :refuse_admin_target, only: %i[destroy<%= " reset_second_factor" if second_factor? %><%= " ban" if bannable? %><%= " impersonate" if impersonatable? %>]
4
+
5
+ def index
6
+ @users = <%= identity.class_name %>.order(:<%= principals.display.column %>)
7
+ end
8
+
9
+ def show
10
+ @sessions = @user.sessions.order(created_at: :desc)
11
+ <%- if trackable? -%>
12
+ @events = @user.events.recent_first.limit(100)
13
+ <%- end -%>
14
+ end
15
+
16
+ def destroy
17
+ <%- if security_notifications? -%>
18
+ email = @user.email
19
+ <%- end -%>
20
+ @user.destroy!
21
+ <%- if security_notifications? -%>
22
+ <%= identity.mailer_class %>.send_security_notification(:account_deleted, to: email)
23
+ <%- end -%>
24
+ redirect_to <%= admin_helper("users_path") %>, notice: "User deleted.", status: :see_other
25
+ end
26
+
27
+ def destroy_all_sessions
28
+ count = @user.sessions.count
29
+ @user.sessions.destroy_all
30
+ <%- if security_notifications? -%>
31
+ <%= identity.mailer_class %>.send_security_notification(:sessions_revoked_by_admin, to: @user.email)
32
+ <%- end -%>
33
+ redirect_to <%= admin_helper("user_path") %>(@user), notice: "Ended #{count} session(s) for #{@user.<%= principals.display.column %>}.",
34
+ status: :see_other
35
+ end
36
+ <%- if second_factor? -%>
37
+
38
+ def reset_second_factor
39
+ @user.reset_second_factor!
40
+ <%- if security_notifications? -%>
41
+ <%= identity.mailer_class %>.send_security_notification(:second_factor_reset_by_admin, to: @user.email)
42
+ <%- end -%>
43
+ redirect_to <%= admin_helper("user_path") %>(@user), notice: "Cleared every second factor on #{@user.<%= principals.display.column %>}'s account."
44
+ end
45
+ <%- end -%>
46
+ <%- if deadboltable? -%>
47
+
48
+ def undeadbolt
49
+ @user.undeadbolt!
50
+ <%- if security_notifications? -%>
51
+ <%= identity.mailer_class %>.send_security_notification(:deadbolt_cleared_by_admin, to: @user.email)
52
+ <%- end -%>
53
+ redirect_to <%= admin_helper("user_path") %>(@user), notice: "Cleared the sign-in deadbolt on #{@user.<%= principals.display.column %>}."
54
+ end
55
+ <%- end -%>
56
+ <%- if bannable? -%>
57
+
58
+ def ban
59
+ @user.ban!
60
+ <%- if security_notifications? -%>
61
+ <%= identity.mailer_class %>.send_security_notification(:account_suspended, to: @user.email)
62
+ <%- end -%>
63
+ redirect_to <%= admin_helper("user_path") %>(@user), notice: "Suspended #{@user.<%= principals.display.column %>} and ended their sessions."
64
+ end
65
+
66
+ def unban
67
+ @user.unban!
68
+ <%- if security_notifications? -%>
69
+ <%= identity.mailer_class %>.send_security_notification(:account_restored, to: @user.email)
70
+ <%- end -%>
71
+ redirect_to <%= admin_helper("user_path") %>(@user), notice: "Lifted the suspension on #{@user.<%= principals.display.column %>}."
72
+ end
73
+ <%- end -%>
74
+ <%- if impersonatable? -%>
75
+
76
+ def impersonate
77
+ impersonate_user(@user)
78
+ redirect_to <%= identity.nested_helper("root_path") %>
79
+ end
80
+ <%- end -%>
81
+
82
+ private
83
+
84
+ def set_user
85
+ @user = <%= identity.class_name %>.find(params[:id])
86
+ end
87
+
88
+ def refuse_admin_target
89
+ redirect_to <%= admin_helper("users_path") %>, alert: "That isn't available for an admin account.", status: :see_other if @user.admin?
90
+ end
91
+ end
@@ -0,0 +1,6 @@
1
+ class <%= admin_controller %> < <%= identity.controller_superclass %>
2
+ admins_only
3
+ <%- if teams? -%>
4
+ skip_before_action :require_team
5
+ <%- end -%>
6
+ end
@@ -0,0 +1,11 @@
1
+ class <%= identity.base_controller %> < ActionController::Base
2
+ include <%= identity.authentication_concern %>
3
+ <%- if authorization? -%>
4
+ include <%= identity.authorization_concern %>
5
+ <%- end -%>
6
+ <%- if captchable? -%>
7
+ include Captchable
8
+ <%- end -%>
9
+
10
+ layout "<%= identity.controller_module %>"
11
+ end
@@ -0,0 +1,21 @@
1
+ module <%= identity.nested_module("ApiTokenAuthentication") %>
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ include ActionController::HttpAuthentication::Token::ControllerMethods
6
+ before_action :authenticate_api_token
7
+ end
8
+
9
+ private
10
+
11
+ attr_reader :current_api_token
12
+
13
+ def current_api_user
14
+ current_api_token.user
15
+ end
16
+
17
+ def authenticate_api_token
18
+ @current_api_token = authenticate_with_http_token { |secret, _| <%= identity.api_token_class %>.authenticate(secret) }
19
+ render json: { error: "Unauthorized" }, status: :unauthorized unless @current_api_token
20
+ end
21
+ end
@@ -0,0 +1,47 @@
1
+ module <%= identity.authentication_concern %>::EasyDevLogin
2
+ extend ActiveSupport::Concern
3
+ <%- if second_factor? -%>
4
+
5
+ def self.mfa_required?
6
+ ENV["REQUIRE_DEV_MFA"].present?
7
+ end
8
+
9
+ def self.passwords_required?
10
+ ENV["REQUIRE_DEV_PASSWORD"].present? || mfa_required?
11
+ end
12
+ <%- else -%>
13
+
14
+ def self.passwords_required?
15
+ ENV["REQUIRE_DEV_PASSWORD"].present?
16
+ end
17
+ <%- end -%>
18
+
19
+ included do
20
+ raise "Authentication::EasyDevLogin must never load outside development" unless Rails.env.development?
21
+
22
+ prepend_before_action :easy_dev_login!
23
+ <%- if admin_mfa_gate? -%>
24
+
25
+ skip_before_action :require_admin_mfa unless Authentication::EasyDevLogin.mfa_required?
26
+ <%- end -%>
27
+ end
28
+
29
+ private
30
+
31
+ def easy_dev_login!
32
+ return if Authentication::EasyDevLogin.passwords_required?
33
+ return if find_session_by_cookie.present?
34
+
35
+ email = params[:<%= sign_in_field %>]
36
+ return if email.blank?
37
+
38
+ user = <%= find_by_login_expr("email") %>
39
+ unless user
40
+ Rails.logger.warn("[EASY_DEV_LOGIN] no user found for #{email.inspect}; skipping bypass")
41
+ return
42
+ end
43
+
44
+ Rails.logger.warn("[EASY_DEV_LOGIN] signing in as #{user.<%= principals.display.column %>} (id=#{user.id}). Password bypass is active.")
45
+ <%= sign_in_call("user", via: :easy_dev_login, may_challenge: false) %>
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ module <%= identity.authentication_concern %>::Registering
2
+ extend ActiveSupport::Concern
3
+
4
+ private
5
+
6
+ <%= advance_body %>
7
+
8
+ def finish_registration(result)
9
+ case result.status
10
+ when :created
11
+ session.delete(:<%= pending_registration_session_key %>)
12
+ <%= sign_in_call("result.user", via: "result.via", may_challenge: false) %>
13
+ redirect_to after_authentication_url, status: :see_other
14
+ when :taken
15
+ session.delete(:<%= pending_registration_session_key %>)
16
+ redirect_to <%= identity.route_scope %>_sign_in_path, alert: "You already have an account. Sign in to continue."
17
+ when :invalid
18
+ render_invalid_registration
19
+ end
20
+ end
21
+
22
+ def render_invalid_registration
23
+ render :new, status: :unprocessable_entity
24
+ end
25
+ end