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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +384 -0
- data/lib/authnz_eleven/identity.rb +210 -0
- data/lib/authnz_eleven/principal.rb +201 -0
- data/lib/authnz_eleven/principals.rb +111 -0
- data/lib/authnz_eleven/version.rb +5 -0
- data/lib/authnz_eleven/wizard.rb +570 -0
- data/lib/authnz_eleven.rb +15 -0
- data/lib/generators/authnz_eleven/USAGE +26 -0
- data/lib/generators/authnz_eleven/authnz_eleven_generator.rb +3040 -0
- data/lib/generators/authnz_eleven/templates/config/forbidden_usernames.LICENSE +22 -0
- data/lib/generators/authnz_eleven/templates/config/forbidden_usernames.txt +558 -0
- data/lib/generators/authnz_eleven/templates/config/initializers/authentication.rb.tt +275 -0
- data/lib/generators/authnz_eleven/templates/config/initializers/omniauth.rb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/config/initializers/team_slug.rb.tt +24 -0
- data/lib/generators/authnz_eleven/templates/config/initializers/webauthn.rb.tt +14 -0
- data/lib/generators/authnz_eleven/templates/config/locales/authentication.en.yml.tt +11 -0
- data/lib/generators/authnz_eleven/templates/controllers/active_teams_controller.rb.tt +6 -0
- data/lib/generators/authnz_eleven/templates/controllers/admin/dashboard_controller.rb.tt +4 -0
- data/lib/generators/authnz_eleven/templates/controllers/admin/sessions_controller.rb.tt +17 -0
- data/lib/generators/authnz_eleven/templates/controllers/admin/users_controller.rb.tt +91 -0
- data/lib/generators/authnz_eleven/templates/controllers/admin_controller.rb.tt +6 -0
- data/lib/generators/authnz_eleven/templates/controllers/base_controller.rb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/api_token_authentication.rb.tt +21 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/easy_dev_login.rb.tt +47 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/registering.rb.tt +25 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/sudoable.rb.tt +117 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/webauthn_ceremony.rb.tt +51 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication.rb.tt +421 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/authorization.rb.tt +48 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/captchable.rb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/controllers/impersonations_controller.rb.tt +8 -0
- data/lib/generators/authnz_eleven/templates/controllers/invitations_controller.rb.tt +65 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/api_tokens_controller.rb.tt +54 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/authentications/events_controller.rb.tt +5 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/dashboard_controller.rb.tt +4 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/email_verifications_controller.rb.tt +15 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/emails_controller.rb.tt +93 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/invitations_controller.rb.tt +61 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/authenticators_controller.rb.tt +87 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/recovery_codes_controller.rb.tt +27 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/security_keys_controller.rb.tt +86 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/sms_controller.rb.tt +84 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/omniauth_identities_controller.rb.tt +27 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/passkeys_controller.rb.tt +70 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/passwords_controller.rb.tt +74 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/phone_verifications_controller.rb.tt +39 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/phones_controller.rb.tt +88 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/sessions_controller.rb.tt +29 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/users_controller.rb.tt +42 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings_controller.rb.tt +5 -0
- data/lib/generators/authnz_eleven/templates/controllers/teams_controller.rb.tt +38 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/credential_enrollments/passkeys_controller.rb.tt +36 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/credential_enrollments/passwords_controller.rb.tt +28 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/credential_enrollments_controller.rb.tt +14 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/easy_dev_logins_controller.rb.tt +19 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/email_verifications_controller.rb.tt +36 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/magic_link_controller.rb.tt +53 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/recovery_codes_controller.rb.tt +30 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/security_keys_controller.rb.tt +34 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/sms_controller.rb.tt +45 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/totps_controller.rb.tt +42 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/omniauth/registrations_controller.rb.tt +26 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/omniauth_controller.rb.tt +139 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/passkeys/sessions_controller.rb.tt +18 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/password_resets_controller.rb.tt +64 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/registrations/email_verifications_controller.rb.tt +53 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/registrations/phone_verifications_controller.rb.tt +37 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/registrations_controller.rb.tt +112 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/sessions_controller.rb.tt +52 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/sms_sessions_controller.rb.tt +53 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/sudos_controller.rb.tt +42 -0
- data/lib/generators/authnz_eleven/templates/erb/admin/dashboard/index.html.erb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/erb/admin/sessions/index.html.erb.tt +27 -0
- data/lib/generators/authnz_eleven/templates/erb/admin/users/index.html.erb.tt +32 -0
- data/lib/generators/authnz_eleven/templates/erb/admin/users/show.html.erb.tt +70 -0
- data/lib/generators/authnz_eleven/templates/erb/invitations/show.html.erb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/api_tokens/create.html.erb.tt +7 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/api_tokens/index.html.erb.tt +29 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/authentications/events/index.html.erb.tt +18 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/dashboard/index.html.erb.tt +75 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/emails/edit.html.erb.tt +68 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/invitations/new.html.erb.tt +31 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/authenticators/new.html.erb.tt +57 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/recovery_codes/create.html.erb.tt +14 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/recovery_codes/index.html.erb.tt +13 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/security_keys/edit.html.erb.tt +14 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/security_keys/index.html.erb.tt +20 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/security_keys/new.html.erb.tt +17 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/sms/new.html.erb.tt +35 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/omniauth_identities/index.html.erb.tt +28 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/passkeys/edit.html.erb.tt +14 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/passkeys/index.html.erb.tt +21 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/passkeys/new.html.erb.tt +13 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/passwords/edit.html.erb.tt +68 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/phone_verifications/new.html.erb.tt +22 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/phones/edit.html.erb.tt +58 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/sessions/index.html.erb.tt +29 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/users/show.html.erb.tt +28 -0
- data/lib/generators/authnz_eleven/templates/erb/shared/_authnz_eleven_nav.html.erb.tt +24 -0
- data/lib/generators/authnz_eleven/templates/erb/shared/_authnz_eleven_sudo_challenge.html.erb.tt +43 -0
- data/lib/generators/authnz_eleven/templates/erb/shared/_captcha.html.erb.tt +8 -0
- data/lib/generators/authnz_eleven/templates/erb/teams/index.html.erb.tt +34 -0
- data/lib/generators/authnz_eleven/templates/erb/teams/new.html.erb.tt +22 -0
- data/lib/generators/authnz_eleven/templates/erb/teams/show.html.erb.tt +5 -0
- data/lib/generators/authnz_eleven/templates/erb/user_mailer/email_change_verification.html.erb.tt +7 -0
- data/lib/generators/authnz_eleven/templates/erb/user_mailer/invitation.html.erb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/erb/user_mailer/magic_link.html.erb.tt +5 -0
- data/lib/generators/authnz_eleven/templates/erb/user_mailer/password_reset.html.erb.tt +6 -0
- data/lib/generators/authnz_eleven/templates/erb/user_mailer/registration_email_verification.html.erb.tt +7 -0
- data/lib/generators/authnz_eleven/templates/erb/user_mailer/security_notification.html.erb.tt +15 -0
- data/lib/generators/authnz_eleven/templates/erb/users/credential_enrollments/passkeys/new.html.erb.tt +24 -0
- data/lib/generators/authnz_eleven/templates/erb/users/credential_enrollments/passwords/new.html.erb.tt +21 -0
- data/lib/generators/authnz_eleven/templates/erb/users/credential_enrollments/show.html.erb.tt +21 -0
- data/lib/generators/authnz_eleven/templates/erb/users/email_verifications/show.html.erb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/erb/users/magic_link/edit.html.erb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/erb/users/magic_link/new.html.erb.tt +22 -0
- data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/recovery_codes/new.html.erb.tt +20 -0
- data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/security_keys/new.html.erb.tt +17 -0
- data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/sms/new.html.erb.tt +25 -0
- data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/totps/new.html.erb.tt +21 -0
- data/lib/generators/authnz_eleven/templates/erb/users/omniauth/registrations/new.html.erb.tt +28 -0
- data/lib/generators/authnz_eleven/templates/erb/users/password_resets/edit.html.erb.tt +26 -0
- data/lib/generators/authnz_eleven/templates/erb/users/password_resets/new.html.erb.tt +20 -0
- data/lib/generators/authnz_eleven/templates/erb/users/registrations/email_verifications/new.html.erb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/erb/users/registrations/email_verifications/show.html.erb.tt +34 -0
- data/lib/generators/authnz_eleven/templates/erb/users/registrations/new.html.erb.tt +87 -0
- data/lib/generators/authnz_eleven/templates/erb/users/registrations/phone_verifications/new.html.erb.tt +46 -0
- data/lib/generators/authnz_eleven/templates/erb/users/sessions/new.html.erb.tt +88 -0
- data/lib/generators/authnz_eleven/templates/erb/users/sms_sessions/edit.html.erb.tt +30 -0
- data/lib/generators/authnz_eleven/templates/erb/users/sms_sessions/new.html.erb.tt +22 -0
- data/lib/generators/authnz_eleven/templates/erb/users/sudos/new.html.erb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/javascript/controllers/webauthn_controller.js.tt +142 -0
- data/lib/generators/authnz_eleven/templates/jobs/authnz_eleven_deferred_job.rb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/jobs/authnz_eleven_mail_delivery_job.rb.tt +3 -0
- data/lib/generators/authnz_eleven/templates/jobs/sms_delivery_job.rb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/lib/captcha/null.rb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/lib/captcha/turnstile.rb.tt +39 -0
- data/lib/generators/authnz_eleven/templates/lib/captcha.rb.tt +15 -0
- data/lib/generators/authnz_eleven/templates/lib/sms/log.rb.tt +8 -0
- data/lib/generators/authnz_eleven/templates/lib/sms/twilio.rb.tt +70 -0
- data/lib/generators/authnz_eleven/templates/lib/sms.rb.tt +15 -0
- data/lib/generators/authnz_eleven/templates/mailers/user_mailer.rb.tt +98 -0
- data/lib/generators/authnz_eleven/templates/migrations/add_guest_to_users_migration.rb.tt +6 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_api_tokens_migration.rb.tt +14 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_events_migration.rb.tt +26 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_invitations_migration.rb.tt +20 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_memberships_migration.rb.tt +12 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_omniauth_identities_migration.rb.tt +16 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_password_histories_migration.rb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_pending_registrations_migration.rb.tt +31 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_recovery_codes_migration.rb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_sessions_migration.rb.tt +19 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_teams_migration.rb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_users_migration.rb.tt +70 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_webauthn_credentials_migration.rb.tt +16 -0
- data/lib/generators/authnz_eleven/templates/models/api_token.rb.tt +24 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/adminable.rb.tt +15 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/authenticatable.rb.tt +151 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/bannable.rb.tt +24 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/deadboltable.rb.tt +39 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/email_verifiable.rb.tt +52 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/eventable.rb.tt +22 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/guestable.rb.tt +30 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/invitable.rb.tt +12 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/magic_linkable.rb.tt +34 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/password_historical.rb.tt +28 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/password_policy.rb.tt +38 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/password_rotatable.rb.tt +15 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/phone_verifiable.rb.tt +50 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/principals.rb.tt +67 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/recoverable.rb.tt +19 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/second_factor.rb.tt +52 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/sms_challengeable.rb.tt +38 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/team_member.rb.tt +20 -0
- data/lib/generators/authnz_eleven/templates/models/current.rb.tt +28 -0
- data/lib/generators/authnz_eleven/templates/models/event.rb.tt +32 -0
- data/lib/generators/authnz_eleven/templates/models/invitation.rb.tt +122 -0
- data/lib/generators/authnz_eleven/templates/models/membership.rb.tt +16 -0
- data/lib/generators/authnz_eleven/templates/models/omniauth_identity.rb.tt +12 -0
- data/lib/generators/authnz_eleven/templates/models/password_history.rb.tt +3 -0
- data/lib/generators/authnz_eleven/templates/models/pending_registration.rb.tt +216 -0
- data/lib/generators/authnz_eleven/templates/models/recovery_code.rb.tt +41 -0
- data/lib/generators/authnz_eleven/templates/models/session.rb.tt +46 -0
- data/lib/generators/authnz_eleven/templates/models/team.rb.tt +12 -0
- data/lib/generators/authnz_eleven/templates/models/user.rb.tt +59 -0
- data/lib/generators/authnz_eleven/templates/models/webauthn_credential.rb.tt +12 -0
- data/lib/generators/authnz_eleven/templates/tasks/authnz_eleven.rake.tt +41 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/account_existence_parity_test.rb.tt +158 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/admin/access_test.rb.tt +139 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/admin/multi_factor_authentication_test.rb.tt +65 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/api_token_authentication_test.rb.tt +45 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/credential_enrollment_test.rb.tt +84 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/guest_access_test.rb.tt +113 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/invitations_controller_test.rb.tt +230 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/passkey_ceremony_test.rb.tt +126 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/password_rotation_test.rb.tt +67 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/security_key_ceremony_test.rb.tt +218 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/security_notifications_test.rb.tt +39 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/session_lifecycle_test.rb.tt +102 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/api_tokens_controller_test.rb.tt +61 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/authentications/events_controller_test.rb.tt +17 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/email_verifications_controller_test.rb.tt +39 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/invitations_controller_test.rb.tt +157 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/management_test.rb.tt +256 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/multi_factor_authentication/management_test.rb.tt +222 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/omniauth_identities_controller_test.rb.tt +57 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/phone_verifications_controller_test.rb.tt +146 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/teams_controller_test.rb.tt +83 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/email_verifications_controller_test.rb.tt +74 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/magic_link_controller_test.rb.tt +90 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/multi_factor_authentication/challenge_test.rb.tt +143 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/omniauth/registrations_controller_test.rb.tt +138 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/omniauth_controller_test.rb.tt +97 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/password_resets_controller_test.rb.tt +58 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/registrations/email_verifications_controller_test.rb.tt +102 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/registrations/phone_verifications_controller_test.rb.tt +69 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/registrations_controller_test.rb.tt +180 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/sessions_controller_test.rb.tt +124 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/sms_sessions_controller_test.rb.tt +111 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/sudos_controller_test.rb.tt +110 -0
- data/lib/generators/authnz_eleven/templates/test_unit/fixtures/memberships.yml.tt +13 -0
- data/lib/generators/authnz_eleven/templates/test_unit/fixtures/teams.yml.tt +2 -0
- data/lib/generators/authnz_eleven/templates/test_unit/fixtures/users.yml.tt +68 -0
- data/lib/generators/authnz_eleven/templates/test_unit/mailers/user_mailer_test.rb.tt +88 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/api_token_test.rb.tt +38 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/password_history_test.rb.tt +51 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/session_test.rb.tt +16 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/sms_challengeable_test.rb.tt +108 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/user/guestable_test.rb.tt +78 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/user/recovery_codes_test.rb.tt +32 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/user/sign_in_methods_test.rb.tt +56 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/user_test.rb.tt +145 -0
- data/lib/generators/authnz_eleven/wizard_generator.rb +52 -0
- metadata +309 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require "net/http"
|
|
2
|
+
|
|
3
|
+
module Captcha
|
|
4
|
+
module Turnstile
|
|
5
|
+
VERIFY_URL = "https://challenges.cloudflare.com/turnstile/v0/siteverify".freeze
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def verify(token, ip: nil)
|
|
9
|
+
return false if token.blank?
|
|
10
|
+
|
|
11
|
+
response = Net::HTTP.post_form(URI(VERIFY_URL), {
|
|
12
|
+
"secret" => secret_key,
|
|
13
|
+
"response" => token,
|
|
14
|
+
"remoteip" => ip
|
|
15
|
+
}.compact)
|
|
16
|
+
response.is_a?(Net::HTTPSuccess) && JSON.parse(response.body)["success"] == true
|
|
17
|
+
rescue => e
|
|
18
|
+
Rails.logger.error("[captcha] Turnstile verification error: #{e.class}: #{e.message}")
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def site_key
|
|
23
|
+
ENV["TURNSTILE_SITE_KEY"] || Rails.application.credentials.dig(:turnstile, :site_key) || test_key("1x00000000000000000000AA")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def secret_key
|
|
27
|
+
ENV["TURNSTILE_SECRET_KEY"] || Rails.application.credentials.dig(:turnstile, :secret_key) || test_key("1x0000000000000000000000000000000AA")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def test_key(key)
|
|
33
|
+
raise "Set TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY (or turnstile.site_key / turnstile.secret_key in credentials)" unless Rails.env.local?
|
|
34
|
+
|
|
35
|
+
key
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require "net/http"
|
|
2
|
+
|
|
3
|
+
module Sms
|
|
4
|
+
module Twilio
|
|
5
|
+
class DeliveryError < StandardError; end
|
|
6
|
+
class TransientError < DeliveryError; end
|
|
7
|
+
|
|
8
|
+
API_HOST = "api.twilio.com".freeze
|
|
9
|
+
API_VERSION = "2010-04-01".freeze
|
|
10
|
+
OPEN_TIMEOUT = 5
|
|
11
|
+
READ_TIMEOUT = 10
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def deliver(to:, body:)
|
|
15
|
+
raise DeliveryError, "Twilio credentials are not configured" if account_sid.blank? || auth_token.blank?
|
|
16
|
+
|
|
17
|
+
response = post(to: to, body: body)
|
|
18
|
+
return true if response.is_a?(Net::HTTPSuccess)
|
|
19
|
+
raise TransientError, "Twilio responded #{response.code}" if retryable?(response)
|
|
20
|
+
|
|
21
|
+
raise DeliveryError, "Twilio responded #{response.code} (error #{error_code(response)})"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def post(to:, body:)
|
|
27
|
+
uri = URI("https://#{API_HOST}/#{API_VERSION}/Accounts/#{account_sid}/Messages.json")
|
|
28
|
+
params = { "To" => to, "Body" => body }.merge(sender_param)
|
|
29
|
+
|
|
30
|
+
request = Net::HTTP::Post.new(uri)
|
|
31
|
+
request.basic_auth(account_sid, auth_token)
|
|
32
|
+
request.set_form_data(params)
|
|
33
|
+
|
|
34
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true,
|
|
35
|
+
open_timeout: OPEN_TIMEOUT, read_timeout: READ_TIMEOUT) { |http| http.request(request) }
|
|
36
|
+
rescue Net::ReadTimeout
|
|
37
|
+
raise DeliveryError, "Twilio did not answer in time; the message may have been sent"
|
|
38
|
+
rescue Net::OpenTimeout, Net::WriteTimeout, SocketError, SystemCallError => e
|
|
39
|
+
raise TransientError, "Twilio unreachable (#{e.class})"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def retryable?(response) = response.is_a?(Net::HTTPServerError) || response.code == "429"
|
|
43
|
+
|
|
44
|
+
def error_code(response)
|
|
45
|
+
JSON.parse(response.body)["code"]
|
|
46
|
+
rescue StandardError
|
|
47
|
+
"unknown"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def sender_param
|
|
51
|
+
if messaging_service_sid.present?
|
|
52
|
+
{ "MessagingServiceSid" => messaging_service_sid }
|
|
53
|
+
elsif from_number.present?
|
|
54
|
+
{ "From" => from_number }
|
|
55
|
+
else
|
|
56
|
+
raise DeliveryError, "Set TWILIO_MESSAGING_SERVICE_SID or TWILIO_FROM_NUMBER"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def account_sid = credential("TWILIO_ACCOUNT_SID", :account_sid)
|
|
61
|
+
def auth_token = credential("TWILIO_AUTH_TOKEN", :auth_token)
|
|
62
|
+
def messaging_service_sid = credential("TWILIO_MESSAGING_SERVICE_SID", :messaging_service_sid)
|
|
63
|
+
def from_number = credential("TWILIO_FROM_NUMBER", :from_number)
|
|
64
|
+
|
|
65
|
+
def credential(env_key, credentials_key)
|
|
66
|
+
ENV[env_key].presence || Rails.application.credentials.dig(:twilio, credentials_key)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Sms
|
|
2
|
+
class << self
|
|
3
|
+
def deliver_later(to:, body:)
|
|
4
|
+
SmsDeliveryJob.perform_later(to: to, body: body)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def deliver(to:, body:)
|
|
8
|
+
provider.deliver(to: to, body: body)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def provider
|
|
12
|
+
Rails.env.production? ? Twilio : Log
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
class <%= identity.mailer_class %> < ApplicationMailer
|
|
2
|
+
self.delivery_job = AuthnzElevenMailDeliveryJob
|
|
3
|
+
<%- if security_notifications? -%>
|
|
4
|
+
|
|
5
|
+
SECURITY_NOTIFICATION_SUBJECTS = {
|
|
6
|
+
password_set: "Your password was set",
|
|
7
|
+
password_changed: "Your password was changed",
|
|
8
|
+
password_reset: "Your password was reset",
|
|
9
|
+
password_removed: "Your password was removed",
|
|
10
|
+
passkey_added: "A passkey was added",
|
|
11
|
+
passkey_removed: "A passkey was removed",
|
|
12
|
+
security_key_added: "A security key was added",
|
|
13
|
+
security_key_removed: "A security key was removed",
|
|
14
|
+
authenticator_enabled: "An authenticator was enabled",
|
|
15
|
+
authenticator_disabled: "An authenticator was disabled",
|
|
16
|
+
sms_mfa_enabled: "SMS two-factor authentication was enabled",
|
|
17
|
+
sms_mfa_disabled: "SMS two-factor authentication was disabled",
|
|
18
|
+
recovery_codes_generated: "Your recovery codes were generated",
|
|
19
|
+
connected_account_linked: "A connected account was linked",
|
|
20
|
+
connected_account_unlinked: "A connected account was unlinked",
|
|
21
|
+
email_changed: "Your email address was changed",
|
|
22
|
+
phone_changed: "Your phone number was changed",
|
|
23
|
+
session_revoked: "A session was signed out",
|
|
24
|
+
<%- if api_tokens? -%>
|
|
25
|
+
api_token_created: "An API token was created",
|
|
26
|
+
api_token_revoked: "An API token was revoked",
|
|
27
|
+
<%- end -%>
|
|
28
|
+
sessions_revoked_by_admin: "An administrator signed out your sessions",
|
|
29
|
+
second_factor_reset_by_admin: "An administrator reset your two-factor authentication",
|
|
30
|
+
deadbolt_cleared_by_admin: "An administrator restored password sign-in",
|
|
31
|
+
account_suspended: "An administrator suspended your account",
|
|
32
|
+
account_restored: "An administrator restored your account",
|
|
33
|
+
account_deleted: "Your account was deleted"
|
|
34
|
+
}.freeze
|
|
35
|
+
|
|
36
|
+
def self.send_security_notification(event, to: <%= identity.current_class %>.user.email)
|
|
37
|
+
return if to.blank?
|
|
38
|
+
|
|
39
|
+
with(
|
|
40
|
+
to:, event:, occurred_at: Time.current,
|
|
41
|
+
ip_address: <%= identity.current_class %>.ip_address,
|
|
42
|
+
user_agent: <%= identity.current_class %>.user_agent
|
|
43
|
+
).security_notification.deliver_later
|
|
44
|
+
end
|
|
45
|
+
<%- end -%>
|
|
46
|
+
<%- if email_gated_registration? -%>
|
|
47
|
+
|
|
48
|
+
def registration_email_verification
|
|
49
|
+
@signed_id = params[:signed_id]
|
|
50
|
+
|
|
51
|
+
mail to: params[:to], subject: "Verify your email to finish signing up"
|
|
52
|
+
end
|
|
53
|
+
<%- end -%>
|
|
54
|
+
<%- if email_change_verification? -%>
|
|
55
|
+
|
|
56
|
+
def email_change_verification
|
|
57
|
+
@signed_id = params[:signed_id]
|
|
58
|
+
|
|
59
|
+
mail to: params[:to], subject: "Verify your new email address"
|
|
60
|
+
end
|
|
61
|
+
<%- end -%>
|
|
62
|
+
<%- if recoverable? -%>
|
|
63
|
+
|
|
64
|
+
def password_reset
|
|
65
|
+
@signed_id = params[:signed_id]
|
|
66
|
+
|
|
67
|
+
mail to: params[:to], subject: "Reset your password"
|
|
68
|
+
end
|
|
69
|
+
<%- end -%>
|
|
70
|
+
<%- if magic_link? -%>
|
|
71
|
+
|
|
72
|
+
def magic_link
|
|
73
|
+
@signed_id = params[:signed_id]
|
|
74
|
+
|
|
75
|
+
mail to: params[:to], subject: "Your sign in link"
|
|
76
|
+
end
|
|
77
|
+
<%- end -%>
|
|
78
|
+
<%- if security_notifications? -%>
|
|
79
|
+
|
|
80
|
+
def security_notification
|
|
81
|
+
@subject = SECURITY_NOTIFICATION_SUBJECTS.fetch(params[:event].to_sym)
|
|
82
|
+
@occurred_at = params[:occurred_at]
|
|
83
|
+
@ip_address = params[:ip_address]
|
|
84
|
+
@user_agent = params[:user_agent]
|
|
85
|
+
|
|
86
|
+
mail to: params[:to], subject: @subject
|
|
87
|
+
end
|
|
88
|
+
<%- end -%>
|
|
89
|
+
<%- if email_invitable? -%>
|
|
90
|
+
|
|
91
|
+
def invitation
|
|
92
|
+
@invitation = params[:invitation]
|
|
93
|
+
@signed_id = params[:signed_id]
|
|
94
|
+
|
|
95
|
+
mail to: params[:to], subject: "You've been invited to #{<%= identity.config_reference %>.app_name}"
|
|
96
|
+
end
|
|
97
|
+
<%- end -%>
|
|
98
|
+
end
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
add_column :<%= identity.table %>, :guest, :boolean, null: false, default: false
|
|
4
|
+
add_index :<%= identity.table %>, :guest, where: "guest", name: "index_<%= identity.table %>_on_guest"
|
|
5
|
+
end
|
|
6
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= identity.api_tokens_table %><%= primary_key_options %> do |t|
|
|
4
|
+
t.references :<%= identity.singular %>, null: false, foreign_key: true<%= reference_type_options %>
|
|
5
|
+
t.string :name, null: false
|
|
6
|
+
t.string :token_digest, null: false
|
|
7
|
+
t.datetime :expires_at, null: false
|
|
8
|
+
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
add_index :<%= identity.api_tokens_table %>, :token_digest, unique: true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= identity.events_table %><%= primary_key_options %> do |t|
|
|
4
|
+
t.string :event_type, null: false
|
|
5
|
+
|
|
6
|
+
t.references :subject, polymorphic: true, null: false, index: false<%= reference_type_options %>
|
|
7
|
+
t.references :actor, polymorphic: true, index: false<%= reference_type_options %>
|
|
8
|
+
t.references :context, polymorphic: true, index: false<%= reference_type_options %>
|
|
9
|
+
|
|
10
|
+
t.datetime :occurred_at, null: false
|
|
11
|
+
t.json :data
|
|
12
|
+
|
|
13
|
+
t.string :ip_address
|
|
14
|
+
t.string :user_agent
|
|
15
|
+
t.string :request_id
|
|
16
|
+
|
|
17
|
+
t.timestamps
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
add_index :<%= identity.events_table %>, :event_type
|
|
21
|
+
add_index :<%= identity.events_table %>, :request_id
|
|
22
|
+
add_index :<%= identity.events_table %>, [ :subject_type, :subject_id, :occurred_at ]
|
|
23
|
+
add_index :<%= identity.events_table %>, [ :context_type, :context_id, :occurred_at ]
|
|
24
|
+
add_index :<%= identity.events_table %>, [ :actor_type, :actor_id ]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= identity.invitations_table %><%= primary_key_options %> do |t|
|
|
4
|
+
t.string :sent_to, null: false, index: true
|
|
5
|
+
<%- if multi_channel_invitable? -%>
|
|
6
|
+
t.string :sent_to_channel, null: false
|
|
7
|
+
<%- end -%>
|
|
8
|
+
t.datetime :expires_at
|
|
9
|
+
t.datetime :accepted_at
|
|
10
|
+
|
|
11
|
+
t.references :inviter, null: false, foreign_key: { to_table: :<%= identity.table %> }<%= reference_type_options %>
|
|
12
|
+
t.references :invitee, foreign_key: { to_table: :<%= identity.table %> }<%= reference_type_options %>
|
|
13
|
+
<%- if teams? -%>
|
|
14
|
+
t.references :team, foreign_key: <%= team_foreign_key %><%= reference_type_options %>
|
|
15
|
+
<%- end -%>
|
|
16
|
+
|
|
17
|
+
t.timestamps
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= identity.memberships_table %><%= primary_key_options %> do |t|
|
|
4
|
+
t.references :<%= identity.singular %>, null: false, foreign_key: true<%= reference_type_options %>
|
|
5
|
+
t.references :team, null: false, foreign_key: <%= team_foreign_key %><%= reference_type_options %>
|
|
6
|
+
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
add_index :<%= identity.memberships_table %>, [ :<%= identity.foreign_key %>, :team_id ], unique: true
|
|
11
|
+
end
|
|
12
|
+
end
|
data/lib/generators/authnz_eleven/templates/migrations/create_omniauth_identities_migration.rb.tt
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= identity.omniauth_identities_table %><%= primary_key_options %> do |t|
|
|
4
|
+
t.references :<%= identity.singular %>, null: false, foreign_key: true<%= reference_type_options %>
|
|
5
|
+
t.string :provider, null: false
|
|
6
|
+
t.string :uid, null: false
|
|
7
|
+
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# A provider UID is meaningful and unique only within its provider.
|
|
12
|
+
add_index :<%= identity.omniauth_identities_table %>, %i[provider uid], unique: true
|
|
13
|
+
|
|
14
|
+
add_index :<%= identity.omniauth_identities_table %>, %i[<%= identity.foreign_key %> provider]
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/generators/authnz_eleven/templates/migrations/create_password_histories_migration.rb.tt
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= identity.password_histories_table %><%= primary_key_options %> do |t|
|
|
4
|
+
t.references :<%= identity.singular %>, null: false, foreign_key: true<%= reference_type_options %>
|
|
5
|
+
# Each row stores an old password digest; Authenticatable prunes rows to the configured history depth.
|
|
6
|
+
t.string :password_digest, null: false
|
|
7
|
+
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
data/lib/generators/authnz_eleven/templates/migrations/create_pending_registrations_migration.rb.tt
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= identity.pending_registrations_table %><%= primary_key_options %> do |t|
|
|
4
|
+
<%- principals.each do |principal| -%>
|
|
5
|
+
# Do not add principal uniqueness here: an unverified or abandoned claim must not reserve another person's identifier.
|
|
6
|
+
t.string :<%= principal.column %>
|
|
7
|
+
<%- end -%>
|
|
8
|
+
<%- if password? -%>
|
|
9
|
+
t.string :password_digest
|
|
10
|
+
<%- end -%>
|
|
11
|
+
<%- if email_verifiable? -%>
|
|
12
|
+
t.datetime :email_verified_at
|
|
13
|
+
<%- end -%>
|
|
14
|
+
<%- if phone_verifiable? -%>
|
|
15
|
+
t.datetime :phone_verified_at
|
|
16
|
+
<%- end -%>
|
|
17
|
+
<%- if pre_account_passkey_enrollment? -%>
|
|
18
|
+
t.string :webauthn_handle
|
|
19
|
+
<%- end -%>
|
|
20
|
+
<%- if pending_registration_holds_provider? -%>
|
|
21
|
+
t.string :provider
|
|
22
|
+
t.string :uid
|
|
23
|
+
<%- end -%>
|
|
24
|
+
<%- if invitable? -%>
|
|
25
|
+
t.references :invitation, null: <%= !invite_only? %>, foreign_key: { to_table: :<%= identity.invitations_table %>, on_delete: :cascade }<%= reference_type_options %>
|
|
26
|
+
<%- end -%>
|
|
27
|
+
|
|
28
|
+
t.timestamps
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= identity.recovery_codes_table %><%= primary_key_options %> do |t|
|
|
4
|
+
t.references :<%= identity.singular %>, null: false, foreign_key: true<%= reference_type_options %>
|
|
5
|
+
# Store only the digest; redemption deletes the row rather than marking it used.
|
|
6
|
+
t.string :code_digest, null: false
|
|
7
|
+
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= identity.sessions_table %><%= primary_key_options %> do |t|
|
|
4
|
+
t.references :<%= identity.singular %>, null: false, foreign_key: true<%= reference_type_options %>
|
|
5
|
+
<%- if session_carries_team? -%>
|
|
6
|
+
t.references :team, foreign_key: <%= team_foreign_key %><%= reference_type_options %>
|
|
7
|
+
<%- end -%>
|
|
8
|
+
t.string :via
|
|
9
|
+
t.string :user_agent
|
|
10
|
+
<%- if sudoable? -%>
|
|
11
|
+
# Persist sudo recency on the session record so signing out clears it.
|
|
12
|
+
t.datetime :sudo_at
|
|
13
|
+
<%- end -%>
|
|
14
|
+
t.string :ip_address
|
|
15
|
+
|
|
16
|
+
t.timestamps
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= identity.table %><%= primary_key_options %> do |t|
|
|
4
|
+
<%- principals.each do |principal| -%>
|
|
5
|
+
<%- if principal.type == :phone -%>
|
|
6
|
+
<%- if guest_excused_from_phone? || (guestable? && principal.required?) -%>
|
|
7
|
+
# Nullable because guests and incomplete accounts may not have a phone yet.
|
|
8
|
+
<%- end -%>
|
|
9
|
+
<%= principal.migration_field(nullable: phone_column_nullable?) %>
|
|
10
|
+
<%- else -%>
|
|
11
|
+
<%= principal.migration_field %>
|
|
12
|
+
<%- end -%>
|
|
13
|
+
<%- end -%>
|
|
14
|
+
<%- principals.select(&:functional_unique_index?).each do |principal| -%>
|
|
15
|
+
t.index "(LOWER(<%= principal.column %>))", unique: true, name: "index_<%= identity.table %>_on_lower_<%= principal.column %>"
|
|
16
|
+
<%- end -%>
|
|
17
|
+
<%- if password? -%>
|
|
18
|
+
<%- if password_nullable? -%>
|
|
19
|
+
# Nullable because there are multiple sign in methods.
|
|
20
|
+
t.string :password_digest
|
|
21
|
+
<%- else -%>
|
|
22
|
+
t.string :password_digest, null: false
|
|
23
|
+
<%- end -%>
|
|
24
|
+
<%- end -%>
|
|
25
|
+
<%- if password_rotatable? -%>
|
|
26
|
+
t.datetime :password_changed_at
|
|
27
|
+
<%- end -%>
|
|
28
|
+
<%- if deadboltable? -%>
|
|
29
|
+
t.integer :failed_attempts, null: false, default: 0
|
|
30
|
+
t.datetime :deadbolted_at
|
|
31
|
+
<%- end -%>
|
|
32
|
+
<%- if bannable? -%>
|
|
33
|
+
t.datetime :banned_at
|
|
34
|
+
t.datetime :banned_until
|
|
35
|
+
<%- end -%>
|
|
36
|
+
<%- if email_change_verification? -%>
|
|
37
|
+
|
|
38
|
+
t.string :pending_email
|
|
39
|
+
<%- end -%>
|
|
40
|
+
<%- if phone_change_verification? -%>
|
|
41
|
+
|
|
42
|
+
t.string :pending_phone
|
|
43
|
+
<%- end -%>
|
|
44
|
+
<%- if totp? -%>
|
|
45
|
+
|
|
46
|
+
t.datetime :totp_enrolled_at
|
|
47
|
+
t.string :totp_secret, null: false
|
|
48
|
+
t.datetime :totp_last_used_at
|
|
49
|
+
<%- end -%>
|
|
50
|
+
<%- if sms_second_factor? -%>
|
|
51
|
+
|
|
52
|
+
t.datetime :sms_enrolled_at
|
|
53
|
+
<%- end -%>
|
|
54
|
+
<%- if adminable? -%>
|
|
55
|
+
|
|
56
|
+
t.boolean :admin, null: false, default: false
|
|
57
|
+
<%- end -%>
|
|
58
|
+
<%- if webauthn_credentials? -%>
|
|
59
|
+
|
|
60
|
+
t.string :webauthn_handle, null: false
|
|
61
|
+
<%- end -%>
|
|
62
|
+
<%- if last_seenable? -%>
|
|
63
|
+
|
|
64
|
+
t.datetime :last_seen_at
|
|
65
|
+
<%- end -%>
|
|
66
|
+
|
|
67
|
+
t.timestamps
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
data/lib/generators/authnz_eleven/templates/migrations/create_webauthn_credentials_migration.rb.tt
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= identity.webauthn_credentials_table %><%= primary_key_options %> do |t|
|
|
4
|
+
t.references :<%= identity.singular %>, null: false, foreign_key: true<%= reference_type_options %>
|
|
5
|
+
t.string :name, null: false
|
|
6
|
+
t.string :external_id, null: false, index: { unique: true }
|
|
7
|
+
t.string :public_key, null: false
|
|
8
|
+
t.bigint :sign_count, null: false, default: 0
|
|
9
|
+
<%- if passkey_and_security_keys? -%>
|
|
10
|
+
t.integer :authentication_factor, null: false
|
|
11
|
+
<%- end -%>
|
|
12
|
+
|
|
13
|
+
t.timestamps
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
class <%= identity.api_token_class %> < ApplicationRecord
|
|
2
|
+
belongs_to :<%= identity.singular %>
|
|
3
|
+
<%- if identity.namespaced? -%>
|
|
4
|
+
|
|
5
|
+
alias_method :user, :<%= identity.singular %>
|
|
6
|
+
<%- end -%>
|
|
7
|
+
|
|
8
|
+
validates :name, presence: true, length: { maximum: 100 }
|
|
9
|
+
validates :token_digest, presence: true, uniqueness: true
|
|
10
|
+
validates :expires_at, presence: true
|
|
11
|
+
|
|
12
|
+
def self.issue!(user:, name:)
|
|
13
|
+
secret = "ae11_#{SecureRandom.urlsafe_base64(32)}"
|
|
14
|
+
token = user.api_tokens.create!(name:, token_digest: Digest::SHA256.hexdigest(secret), expires_at: 90.days.from_now)
|
|
15
|
+
[ token, secret ]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.authenticate(secret)
|
|
19
|
+
return unless secret&.start_with?("ae11_") && secret.bytesize == 48
|
|
20
|
+
|
|
21
|
+
token = includes(:<%= identity.singular %>).find_by(token_digest: Digest::SHA256.hexdigest(secret))
|
|
22
|
+
token if token && token.expires_at.future?<%= " && !token.user.guest?" if guestable? %>
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module <%= identity.class_name %>::Adminable
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
attr_readonly :admin # Prevent mass assignment or ordinary updates from elevating an existing account.
|
|
6
|
+
|
|
7
|
+
scope :admins, -> { where(admin: true) }
|
|
8
|
+
end
|
|
9
|
+
<%- if admin_mfa_gate? -%>
|
|
10
|
+
|
|
11
|
+
def admin_mfa_enrolled?
|
|
12
|
+
!admin? || second_factor_enrolled?
|
|
13
|
+
end
|
|
14
|
+
<%- end -%>
|
|
15
|
+
end
|