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,158 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class <%= identity.module_name %>::AccountExistenceParityTest < ActionDispatch::IntegrationTest
|
|
4
|
+
def queue_adapter_for_test = ActiveJob::QueueAdapters::TestAdapter.new
|
|
5
|
+
|
|
6
|
+
setup { @user = <%= identity.plural %>(:alice) }
|
|
7
|
+
<%- if password? -%>
|
|
8
|
+
|
|
9
|
+
test "a failed sign-in does the same work for any address" do
|
|
10
|
+
assert_same_work(@user.<%= login_field %>, unknown_email) do |login|
|
|
11
|
+
post <%= identity.route_scope %>_sign_in_path, params: { <%= sign_in_field %>: login, password: "wrong-password-0" }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
<%- end -%>
|
|
15
|
+
<%- if recoverable? -%>
|
|
16
|
+
|
|
17
|
+
test "a password reset request does the same work for any address" do
|
|
18
|
+
assert_same_work(@user.email, unknown_email) do |email|
|
|
19
|
+
post <%= identity.route_scope %>_reset_password_path, params: { email: }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
<%- end -%>
|
|
23
|
+
<%- if magic_link? -%>
|
|
24
|
+
|
|
25
|
+
test "a magic link request does the same work for any address" do
|
|
26
|
+
assert_same_work(@user.email, unknown_email) do |email|
|
|
27
|
+
post <%= identity.route_scope %>_magic_link_path, params: { email: }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
<%- end -%>
|
|
31
|
+
<%- if sms_code? -%>
|
|
32
|
+
|
|
33
|
+
test "an SMS sign-in request does the same work for any number" do
|
|
34
|
+
assert_same_work(@user.phone, unknown_phone) do |phone|
|
|
35
|
+
post <%= identity.route_scope %>_sms_sign_in_path, params: { phone: }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
test "an SMS sign-in code check does the same work for any number" do
|
|
40
|
+
works = [ @user.phone, unknown_phone ].map do |phone|
|
|
41
|
+
perform_enqueued_jobs(only: AuthnzElevenDeferredJob) do
|
|
42
|
+
post <%= identity.route_scope %>_sms_sign_in_path, params: { phone: }
|
|
43
|
+
end
|
|
44
|
+
work_of { patch <%= identity.route_scope %>_sms_sign_in_code_path, params: { code: "000000" } }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
assert_equal(*works)
|
|
48
|
+
end
|
|
49
|
+
<%- end -%>
|
|
50
|
+
<%- if email_gated_registration? && !invite_only? -%>
|
|
51
|
+
|
|
52
|
+
test "signing up does the same work for any address" do
|
|
53
|
+
assert_same_work(@user.email, unknown_email) do |email|
|
|
54
|
+
post <%= identity.route_scope %>_sign_up_path, params: sign_up_params(email:)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
test "resending a sign-up link does the same work for any address" do
|
|
59
|
+
works = [ @user.email, unknown_email ].map do |email|
|
|
60
|
+
post <%= identity.route_scope %>_sign_up_path, params: sign_up_params(email:)
|
|
61
|
+
work_of { post <%= identity.route_scope %>_verify_email_path }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
assert_equal(*works)
|
|
65
|
+
end
|
|
66
|
+
<%- end -%>
|
|
67
|
+
<%- if email_change_verification? -%>
|
|
68
|
+
|
|
69
|
+
test "changing your email does the same work for any address" do
|
|
70
|
+
sign_in_as <%= identity.plural %>(:bob)
|
|
71
|
+
assert_same_work(@user.email, unknown_email) do |email|
|
|
72
|
+
patch <%= identity.settings_helper_prefix %>_email_path, params: { email:<%= settings_credential_pair %> }
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
test "resending an email change link does the same work for any address" do
|
|
77
|
+
sign_in_as <%= identity.plural %>(:bob)
|
|
78
|
+
works = [ @user.email, unknown_email ].map do |email|
|
|
79
|
+
patch <%= identity.settings_helper_prefix %>_email_path, params: { email:<%= settings_credential_pair %> }
|
|
80
|
+
work_of { post <%= identity.settings_helper_prefix %>_email_verification_path }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
assert_equal(*works)
|
|
84
|
+
end
|
|
85
|
+
<%- end -%>
|
|
86
|
+
<%- if phone_change_verification? -%>
|
|
87
|
+
|
|
88
|
+
test "changing your phone does the same work for any number" do
|
|
89
|
+
sign_in_as <%= identity.plural %>(:bob)
|
|
90
|
+
assert_same_work(@user.phone, unknown_phone) do |phone|
|
|
91
|
+
patch <%= identity.settings_helper_prefix %>_phone_path, params: { phone:<%= settings_credential_pair %> }
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
test "resending a phone change code does the same work for any number" do
|
|
96
|
+
sign_in_as <%= identity.plural %>(:bob)
|
|
97
|
+
works = [ @user.phone, unknown_phone ].map do |phone|
|
|
98
|
+
patch <%= identity.settings_helper_prefix %>_phone_path, params: { phone:<%= settings_credential_pair %> }
|
|
99
|
+
work_of { post <%= identity.settings_helper_prefix %>_phone_verification_path }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
assert_equal(*works)
|
|
103
|
+
end
|
|
104
|
+
<%- end -%>
|
|
105
|
+
<%- if invitable? -%>
|
|
106
|
+
|
|
107
|
+
test "inviting does the same work for anyone" do
|
|
108
|
+
sign_in_as <%= identity.plural %>(:bob)
|
|
109
|
+
<%- if email_invitable? -%>
|
|
110
|
+
assert_same_work(@user.email, unknown_email) do |sent_to|
|
|
111
|
+
<%- else -%>
|
|
112
|
+
assert_same_work(@user.phone, unknown_phone) do |sent_to|
|
|
113
|
+
<%- end -%>
|
|
114
|
+
post <%= identity.settings_helper_prefix %>_invitation_path, params: { sent_to: }
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
<%- end -%>
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def assert_same_work(known, unknown, &request)
|
|
122
|
+
assert_equal work_of { request.(known) }, work_of { request.(unknown) }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def work_of
|
|
126
|
+
work = []
|
|
127
|
+
record = lambda do |event, *, payload|
|
|
128
|
+
if event == "sql.active_record"
|
|
129
|
+
work << [ without_literals(payload[:sql]), payload[:row_count] ] unless payload[:sql].start_with?("SELECT") || payload[:name] == "SCHEMA"
|
|
130
|
+
elsif !payload[:key].to_s.start_with?("rate-limit:")
|
|
131
|
+
work << [ event, payload[:job]&.class ]
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
ActiveSupport::Notifications.subscribed(record, /\A(sql\.active_record|cache_\w+\.active_support|enqueue\.active_job)\z/) { yield }
|
|
135
|
+
work
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def without_literals(sql) = sql.gsub(/'(?:[^'\\]|\\.)*'|\b\d+\b/, "?")
|
|
139
|
+
|
|
140
|
+
def unknown_email = "nobody-#{SecureRandom.hex(4)}@example.com"
|
|
141
|
+
<%- if principals.phone -%>
|
|
142
|
+
def unknown_phone = "+14158264010"
|
|
143
|
+
<%- end -%>
|
|
144
|
+
<%- if email_change_verification? || phone_change_verification? || invitable? -%>
|
|
145
|
+
|
|
146
|
+
def sign_in_as(user)
|
|
147
|
+
perform_enqueued_jobs(only: AuthnzElevenDeferredJob) do
|
|
148
|
+
<%= test_sign_in_body.gsub(/^(?=.)/, " ") %>
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
<%- end -%>
|
|
152
|
+
<%- if email_gated_registration? && !invite_only? -%>
|
|
153
|
+
|
|
154
|
+
def sign_up_params(**overrides)
|
|
155
|
+
<%= sign_up_fixture_literal(indent: 4) %>.merge(overrides)
|
|
156
|
+
end
|
|
157
|
+
<%- end -%>
|
|
158
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class <%= admin_module %>::AccessTest < ActionDispatch::IntegrationTest
|
|
4
|
+
test "the admin area is a 404 for anonymous visitors" do
|
|
5
|
+
get <%= admin_helper("root_path") %>
|
|
6
|
+
assert_response :not_found
|
|
7
|
+
end
|
|
8
|
+
<%- if password? -%>
|
|
9
|
+
|
|
10
|
+
test "the admin area is a 404 for a signed-in non-admin" do
|
|
11
|
+
sign_in_as <%= identity.plural %>(:bob)
|
|
12
|
+
get <%= admin_helper("root_path") %>
|
|
13
|
+
assert_response :not_found
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
<%- if password_rotatable? -%>
|
|
17
|
+
|
|
18
|
+
test "the admin area is a 404 for a non-admin held at a completion gate" do
|
|
19
|
+
sign_in_as <%= identity.plural %>(:bob)
|
|
20
|
+
<%- if password_rotatable? -%>
|
|
21
|
+
<%= identity.plural %>(:bob).update!(password_changed_at: 1.year.ago)
|
|
22
|
+
<%- end -%>
|
|
23
|
+
|
|
24
|
+
get <%= admin_helper("root_path") %>
|
|
25
|
+
assert_response :not_found
|
|
26
|
+
end
|
|
27
|
+
<%- end -%>
|
|
28
|
+
|
|
29
|
+
test "an admin can reach the dashboard" do
|
|
30
|
+
sign_in_as <%= identity.plural %>(:alice)
|
|
31
|
+
<%- if admin_mfa_gate? -%>
|
|
32
|
+
<%= enroll_second_factor_expr("#{identity.plural}(:alice)") %>
|
|
33
|
+
<%- end -%>
|
|
34
|
+
get <%= admin_helper("root_path") %>
|
|
35
|
+
assert_response :success
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
test "the destructive actions refuse an admin target" do
|
|
39
|
+
sign_in_as <%= identity.plural %>(:alice)
|
|
40
|
+
<%- if admin_mfa_gate? -%>
|
|
41
|
+
<%= enroll_second_factor_expr("#{identity.plural}(:alice)") %>
|
|
42
|
+
<%- end -%>
|
|
43
|
+
|
|
44
|
+
[ <%= identity.plural %>(:carol), <%= identity.plural %>(:alice) ].each do |admin|
|
|
45
|
+
delete <%= admin_helper("user_path") %>(admin)
|
|
46
|
+
assert_redirected_to <%= admin_helper("users_path") %>
|
|
47
|
+
assert <%= identity.class_name %>.exists?(admin.id), "an admin must not be deleted here"
|
|
48
|
+
<%- if bannable? -%>
|
|
49
|
+
|
|
50
|
+
post ban_<%= admin_helper("user_path") %>(admin)
|
|
51
|
+
assert_not admin.reload.banned?, "an admin must not be suspended here"
|
|
52
|
+
<%- end -%>
|
|
53
|
+
<%- if second_factor? -%>
|
|
54
|
+
|
|
55
|
+
<%= enroll_second_factor_expr("admin") %> unless admin.second_factor_enrolled?
|
|
56
|
+
post reset_second_factor_<%= admin_helper("user_path") %>(admin)
|
|
57
|
+
assert admin.reload.second_factor_enrolled?, "an admin's second factor must survive"
|
|
58
|
+
<%- end -%>
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
<%- if impersonatable? && !admin_mfa_gate? -%>
|
|
62
|
+
|
|
63
|
+
test "an admin can impersonate another user, and stop" do
|
|
64
|
+
sign_in_as <%= identity.plural %>(:alice)
|
|
65
|
+
post <%= admin_helper("impersonate_user_path") %>(<%= identity.plural %>(:bob))
|
|
66
|
+
assert_redirected_to <%= identity.nested_helper("root_path") %>
|
|
67
|
+
|
|
68
|
+
get <%= admin_helper("root_path") %>
|
|
69
|
+
assert_response :not_found
|
|
70
|
+
|
|
71
|
+
delete <%= identity.nested_helper("impersonation_path") %>
|
|
72
|
+
assert_redirected_to <%= admin_helper("root_path") %>
|
|
73
|
+
|
|
74
|
+
get <%= admin_helper("root_path") %>
|
|
75
|
+
assert_response :success
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
test "impersonation expires on its own" do
|
|
79
|
+
sign_in_as <%= identity.plural %>(:alice)
|
|
80
|
+
post <%= admin_helper("impersonate_user_path") %>(<%= identity.plural %>(:bob))
|
|
81
|
+
|
|
82
|
+
travel <%= identity.config_reference %>.impersonation.duration + 1.minute do
|
|
83
|
+
get <%= identity.nested_helper("root_path") %>
|
|
84
|
+
assert_redirected_to <%= admin_helper("root_path") %>
|
|
85
|
+
|
|
86
|
+
get <%= admin_helper("root_path") %>
|
|
87
|
+
assert_response :success, "alice should be herself again"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
test "an admin cannot impersonate an admin, including themselves" do
|
|
92
|
+
sign_in_as <%= identity.plural %>(:alice)
|
|
93
|
+
|
|
94
|
+
post <%= admin_helper("impersonate_user_path") %>(<%= identity.plural %>(:carol))
|
|
95
|
+
assert_redirected_to <%= admin_helper("users_path") %>
|
|
96
|
+
|
|
97
|
+
post <%= admin_helper("impersonate_user_path") %>(<%= identity.plural %>(:alice))
|
|
98
|
+
assert_redirected_to <%= admin_helper("users_path") %>
|
|
99
|
+
|
|
100
|
+
get <%= admin_helper("root_path") %>
|
|
101
|
+
assert_response :success, "alice should still be herself, with her admin access intact"
|
|
102
|
+
end
|
|
103
|
+
<%- if trackable? -%>
|
|
104
|
+
|
|
105
|
+
test "impersonation is recorded at both ends" do
|
|
106
|
+
sign_in_as <%= identity.plural %>(:alice)
|
|
107
|
+
post <%= admin_helper("impersonate_user_path") %>(<%= identity.plural %>(:bob))
|
|
108
|
+
delete <%= identity.nested_helper("impersonation_path") %>
|
|
109
|
+
|
|
110
|
+
events = <%= identity.plural %>(:bob).events.chronological.last(2)
|
|
111
|
+
assert_equal [ "<%= identity.singular %>.impersonation_started", "<%= identity.singular %>.impersonation_stopped" ],
|
|
112
|
+
events.map(&:event_type)
|
|
113
|
+
assert_equal [ <%= identity.plural %>(:alice), <%= identity.plural %>(:alice) ], events.map(&:actor)
|
|
114
|
+
end
|
|
115
|
+
<%- end -%>
|
|
116
|
+
|
|
117
|
+
test "a non-admin cannot impersonate" do
|
|
118
|
+
sign_in_as <%= identity.plural %>(:bob)
|
|
119
|
+
post <%= admin_helper("impersonate_user_path") %>(<%= identity.plural %>(:alice))
|
|
120
|
+
assert_response :not_found
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
test "the stop-impersonating route is a 404 when nobody is impersonating" do
|
|
124
|
+
delete <%= identity.nested_helper("impersonation_path") %>
|
|
125
|
+
assert_response :not_found
|
|
126
|
+
|
|
127
|
+
sign_in_as <%= identity.plural %>(:bob)
|
|
128
|
+
delete <%= identity.nested_helper("impersonation_path") %>
|
|
129
|
+
assert_response :not_found
|
|
130
|
+
end
|
|
131
|
+
<%- end -%>
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def sign_in_as(user)
|
|
136
|
+
<%= test_sign_in_body %>
|
|
137
|
+
end
|
|
138
|
+
<%- end -%>
|
|
139
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class <%= admin_module %>::MultiFactorAuthenticationTest < ActionDispatch::IntegrationTest
|
|
4
|
+
setup { @admin = <%= identity.plural %>(:alice) }
|
|
5
|
+
|
|
6
|
+
test "an MFA-less admin is held: an admin page bounces to the enrollment screen" do
|
|
7
|
+
assert_not @admin.second_factor_enrolled?
|
|
8
|
+
sign_in_as @admin
|
|
9
|
+
|
|
10
|
+
get <%= admin_helper("users_path") %>
|
|
11
|
+
assert_redirected_to <%= admin_mfa_hold_path %>
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test "the enrollment screen stays reachable while held" do
|
|
15
|
+
sign_in_as @admin
|
|
16
|
+
<%- if sudo_test_mode == :answer -%>
|
|
17
|
+
post <%= identity.route_scope %>_sudo_path, params: { <%= sudo_test_answer %> }
|
|
18
|
+
<%- end -%>
|
|
19
|
+
|
|
20
|
+
get <%= admin_mfa_hold_path %>
|
|
21
|
+
assert_response :success
|
|
22
|
+
end
|
|
23
|
+
<%- if sudo_test_mode == :answer -%>
|
|
24
|
+
|
|
25
|
+
test "the sudo page stays reachable while held, so the enrollment screen can ask for confirmation" do
|
|
26
|
+
sign_in_as @admin
|
|
27
|
+
|
|
28
|
+
get new_<%= identity.route_scope %>_sudo_path
|
|
29
|
+
assert_response :success
|
|
30
|
+
end
|
|
31
|
+
<%- end -%>
|
|
32
|
+
|
|
33
|
+
test "enrolling a second factor clears the gate for the rest of the admin area" do
|
|
34
|
+
sign_in_as @admin
|
|
35
|
+
get <%= admin_helper("users_path") %>
|
|
36
|
+
assert_redirected_to <%= admin_mfa_hold_path %>, "held before a second factor is enrolled"
|
|
37
|
+
|
|
38
|
+
<%= enroll_second_factor_expr("@admin") %>
|
|
39
|
+
assert @admin.reload.second_factor_enrolled?
|
|
40
|
+
|
|
41
|
+
get <%= admin_helper("users_path") %>
|
|
42
|
+
assert_response :success, "cleared once a second factor is enrolled"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
test "an admin clears every second factor a user holds" do
|
|
47
|
+
stranded = <%= identity.plural %>(:bob)
|
|
48
|
+
<%= enroll_second_factor_expr("stranded") %>
|
|
49
|
+
<%= identity.recovery_code_class %>.regenerate_for(stranded)
|
|
50
|
+
|
|
51
|
+
sign_in_as @admin
|
|
52
|
+
<%= enroll_second_factor_expr("@admin") %>
|
|
53
|
+
post reset_second_factor_<%= admin_helper("user_path") %>(stranded)
|
|
54
|
+
|
|
55
|
+
assert_not stranded.reload.second_factor_enrolled?
|
|
56
|
+
assert_empty stranded.recovery_codes.reload,
|
|
57
|
+
"codes guarding nothing are live sign-in secrets"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def sign_in_as(user)
|
|
63
|
+
<%= test_sign_in_body %>
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class <%= identity.class_name %>ApiTokenProbeController < ActionController::API
|
|
4
|
+
include <%= identity.nested_module("ApiTokenAuthentication") %>
|
|
5
|
+
|
|
6
|
+
def index
|
|
7
|
+
render json: { user_id: current_api_user.id, token_id: current_api_token.id }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class <%= identity.class_name %>ApiTokenAuthenticationTest < ActionController::TestCase
|
|
12
|
+
tests <%= identity.class_name %>ApiTokenProbeController
|
|
13
|
+
|
|
14
|
+
setup do
|
|
15
|
+
@routes = ActionDispatch::Routing::RouteSet.new
|
|
16
|
+
@routes.draw { get "api_token_probe", to: "<%= identity.class_name.underscore %>_api_token_probe#index" }
|
|
17
|
+
@user = <%= identity.plural %>(:bob)
|
|
18
|
+
@token, @secret = <%= identity.api_token_class %>.issue!(user: @user, name: "Integration")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
test "bearer token authenticates without a browser session" do
|
|
22
|
+
@request.headers["Authorization"] = "Bearer #{@secret}"
|
|
23
|
+
get :index
|
|
24
|
+
|
|
25
|
+
assert_response :success
|
|
26
|
+
assert_equal @user.id, response.parsed_body["user_id"]
|
|
27
|
+
assert_equal @token.id, response.parsed_body["token_id"]
|
|
28
|
+
assert_nil <%= identity.current_class %>.session
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
test "missing expired and revoked tokens are rejected" do
|
|
32
|
+
get :index
|
|
33
|
+
assert_response :unauthorized
|
|
34
|
+
|
|
35
|
+
@request.headers["Authorization"] = "Bearer #{@secret}"
|
|
36
|
+
@token.update!(expires_at: 1.second.ago)
|
|
37
|
+
get :index
|
|
38
|
+
assert_response :unauthorized
|
|
39
|
+
|
|
40
|
+
@token.update!(expires_at: 1.day.from_now)
|
|
41
|
+
@token.destroy!
|
|
42
|
+
get :index
|
|
43
|
+
assert_response :unauthorized
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/generators/authnz_eleven/templates/test_unit/controllers/credential_enrollment_test.rb.tt
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class <%= identity.module_name %>::CredentialEnrollmentTest < ActionDispatch::IntegrationTest
|
|
4
|
+
test "an unfinished registration does not create an account" do
|
|
5
|
+
assert_no_difference -> { <%= identity.class_name %>.count } do
|
|
6
|
+
post <%= identity.route_scope %>_sign_up_path, params: sign_up_params
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
<%- if invite_only? -%>
|
|
10
|
+
assert_not <%= identity.pending_registration_class %>.exists?, "admission is required before staging"
|
|
11
|
+
<%- else -%>
|
|
12
|
+
assert <%= identity.pending_registration_class %>.exists?
|
|
13
|
+
<%- end -%>
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
<%- if pre_account_password_enrollment? -%>
|
|
17
|
+
test "password and account commit together" do
|
|
18
|
+
registration = stage_registration_awaiting_credential
|
|
19
|
+
|
|
20
|
+
assert_difference -> { <%= identity.class_name %>.count }, 1 do
|
|
21
|
+
post <%= identity.route_scope %>_registration_password_path,
|
|
22
|
+
params: { password: "Correct horse battery staple 42!", password_confirmation: "Correct horse battery staple 42!" }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
assert_not <%= identity.pending_registration_class %>.exists?(registration.id)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
test "the password step enforces the password rules" do
|
|
29
|
+
stage_registration_awaiting_credential
|
|
30
|
+
|
|
31
|
+
assert_no_difference -> { <%= identity.class_name %>.count } do
|
|
32
|
+
post <%= identity.route_scope %>_registration_password_path, params: { password: "a", password_confirmation: "a" }
|
|
33
|
+
end
|
|
34
|
+
assert_response :unprocessable_entity
|
|
35
|
+
|
|
36
|
+
assert_no_difference -> { <%= identity.class_name %>.count } do
|
|
37
|
+
post <%= identity.route_scope %>_registration_password_path,
|
|
38
|
+
params: { password: "Correct horse battery staple 42!", password_confirmation: "Something else entirely 42!" }
|
|
39
|
+
end
|
|
40
|
+
assert_response :unprocessable_entity
|
|
41
|
+
end
|
|
42
|
+
<%- end -%>
|
|
43
|
+
<%- if pre_account_provider_enrollment? -%>
|
|
44
|
+
|
|
45
|
+
test "connecting a provider from the finish-setup page completes the sign-up" do
|
|
46
|
+
request_validation = OmniAuth.config.request_validation_phase
|
|
47
|
+
OmniAuth.config.request_validation_phase = nil
|
|
48
|
+
OmniAuth.config.test_mode = true
|
|
49
|
+
OmniAuth.config.mock_auth[:developer] = OmniAuth::AuthHash.new(provider: "developer", uid: "finishing-setup")
|
|
50
|
+
stage_registration_awaiting_credential
|
|
51
|
+
|
|
52
|
+
assert_difference -> { <%= identity.class_name %>.count }, 1 do
|
|
53
|
+
post "<%= omniauth_path_prefix %>/developer?registration_intent=1"
|
|
54
|
+
follow_redirect!
|
|
55
|
+
end
|
|
56
|
+
assert <%= identity.omniauth_identity_class %>.exists?(uid: "finishing-setup")
|
|
57
|
+
assert cookies[:<%= identity.cookie_name %>].present?
|
|
58
|
+
ensure
|
|
59
|
+
OmniAuth.config.mock_auth[:developer] = nil
|
|
60
|
+
OmniAuth.config.test_mode = false
|
|
61
|
+
OmniAuth.config.request_validation_phase = request_validation
|
|
62
|
+
end
|
|
63
|
+
<%- end -%>
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
<%- if pre_account_password_enrollment? || pre_account_provider_enrollment? -%>
|
|
67
|
+
|
|
68
|
+
def stage_registration_awaiting_credential
|
|
69
|
+
post <%= identity.route_scope %>_sign_up_path, params: sign_up_params
|
|
70
|
+
registration = <%= identity.pending_registration_class %>.last
|
|
71
|
+
<%- if email_verifiable? -%>
|
|
72
|
+
registration.update!(email_verified_at: Time.current)
|
|
73
|
+
<%- end -%>
|
|
74
|
+
<%- if phone_verifiable? -%>
|
|
75
|
+
registration.update!(phone_verified_at: Time.current)
|
|
76
|
+
<%- end -%>
|
|
77
|
+
registration
|
|
78
|
+
end
|
|
79
|
+
<%- end -%>
|
|
80
|
+
|
|
81
|
+
def sign_up_params
|
|
82
|
+
<%= sign_up_fixture_literal(indent: 4) %>
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class <%= identity.module_name %>::GuestAccessTest < ActionDispatch::IntegrationTest
|
|
4
|
+
<%- if timeoutable? -%>
|
|
5
|
+
include ActiveSupport::Testing::TimeHelpers
|
|
6
|
+
|
|
7
|
+
<%- end -%>
|
|
8
|
+
class ProbeController < <%= identity.controller_superclass %>
|
|
9
|
+
allow_guest_access
|
|
10
|
+
|
|
11
|
+
def show
|
|
12
|
+
render plain: "#{<%= identity.current_class %>.user&.id}:#{<%= identity.current_class %>.user&.guest?}:#{authenticated?}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def sign_in
|
|
16
|
+
<%= sign_in_call("#{identity.class_name}.find(params[:id])", via: :second_factor, may_challenge: false) %>
|
|
17
|
+
head :ok
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
setup { @<%= identity.singular %> = <%= identity.plural %>(:bob) }
|
|
22
|
+
|
|
23
|
+
test "an anonymous visitor is admitted and given a guest identity" do
|
|
24
|
+
with_probe_routes do
|
|
25
|
+
assert_difference -> { <%= identity.class_name %>.guests.count }, +1 do
|
|
26
|
+
get "/guest_probe"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
assert_response :success
|
|
30
|
+
assert_equal "#{<%= identity.class_name %>.guests.last.id}:true:false", response.body
|
|
31
|
+
assert_equal "guest", <%= identity.class_name %>.guests.last.sessions.sole.via,
|
|
32
|
+
"creating a guest does not sign them in"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
test "a guest cookie persists until the guest expires" do
|
|
37
|
+
with_probe_routes do
|
|
38
|
+
get "/guest_probe"
|
|
39
|
+
guest = <%= identity.class_name %>.guests.last
|
|
40
|
+
expires = Time.httpdate(session_cookie_header[/expires=([^;]+)/i, 1])
|
|
41
|
+
|
|
42
|
+
assert_in_delta (guest.created_at + <%= identity.config_reference %>.guest.ttl).to_f,
|
|
43
|
+
expires.to_f, 1
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
test "the same guest is reused across requests" do
|
|
48
|
+
with_probe_routes do
|
|
49
|
+
get "/guest_probe"
|
|
50
|
+
first = response.body
|
|
51
|
+
|
|
52
|
+
assert_no_difference -> { <%= identity.class_name %>.guests.count } do
|
|
53
|
+
get "/guest_probe"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
assert_equal first, response.body
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
<%- if timeoutable? -%>
|
|
60
|
+
|
|
61
|
+
test "a guest outlives the idle timeout that would expire a signed-in session" do
|
|
62
|
+
with_probe_routes do
|
|
63
|
+
get "/guest_probe"
|
|
64
|
+
guest = response.body
|
|
65
|
+
|
|
66
|
+
travel <%= identity.config_reference %>.session.idle_timeout + 1.day do
|
|
67
|
+
get "/guest_probe"
|
|
68
|
+
|
|
69
|
+
assert_equal guest, response.body, "the guest should still be the same row"
|
|
70
|
+
assert_nil flash[:alert], "a guest was never signed in, so nothing expired"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
<%- end -%>
|
|
75
|
+
|
|
76
|
+
test "signing in absorbs the guest and destroys it" do
|
|
77
|
+
with_probe_routes do
|
|
78
|
+
get "/guest_probe"
|
|
79
|
+
guest = <%= identity.class_name %>.guests.last
|
|
80
|
+
|
|
81
|
+
assert_difference -> { <%= identity.class_name %>.guests.count }, -1 do
|
|
82
|
+
post "/guest_probe/sign_in/#{@<%= identity.singular %>.id}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
assert_no_match(/expires=/i, session_cookie_header,
|
|
86
|
+
"signing in must not inherit the guest cookie's persistence")
|
|
87
|
+
assert_nil <%= identity.class_name %>.find_by(id: guest.id),
|
|
88
|
+
"the guest row is destroyed once its state has been handed to the account"
|
|
89
|
+
|
|
90
|
+
get "/guest_probe"
|
|
91
|
+
assert_equal "#{@<%= identity.singular %>.id}:false:true", response.body
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def session_cookie_header
|
|
98
|
+
Array(response.headers["Set-Cookie"])
|
|
99
|
+
.flat_map { |line| line.to_s.split("\n") }
|
|
100
|
+
.find { |line| line.start_with?("<%= identity.cookie_name %>=") }.to_s
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def with_probe_routes
|
|
104
|
+
# Keep requests and assertions inside this block; with_routing uses a separate integration session and discards its cookies afterward.
|
|
105
|
+
with_routing do |set|
|
|
106
|
+
set.draw do
|
|
107
|
+
get "guest_probe", to: ProbeController.action(:show)
|
|
108
|
+
post "guest_probe/sign_in/:id", to: ProbeController.action(:sign_in)
|
|
109
|
+
end
|
|
110
|
+
yield
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|