@cosmicdrift/kumiko-bundled-features 1.0.0 → 2.0.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.
- package/package.json +41 -7
- package/src/__tests__/access-invalidation-consumer.integration.test.ts +279 -0
- package/src/__tests__/access-invalidation-stream.integration.test.ts +217 -0
- package/src/admin-shell/__tests__/admin-shell-security.integration.test.ts +200 -0
- package/src/admin-shell/__tests__/admin-shell.boot.test.ts +147 -0
- package/src/admin-shell/__tests__/overview-allowlist.test.ts +43 -0
- package/src/admin-shell/__tests__/overview-screens.boot.test.ts +35 -0
- package/src/admin-shell/constants.ts +11 -0
- package/src/admin-shell/feature.ts +135 -0
- package/src/admin-shell/i18n.ts +36 -0
- package/src/admin-shell/index.ts +17 -0
- package/src/admin-shell/overview-allowlist.ts +37 -0
- package/src/admin-shell/web/client-plugin.tsx +26 -0
- package/src/admin-shell/web/i18n.ts +16 -0
- package/src/admin-shell/web/index.ts +2 -0
- package/src/admin-shell/web/overview-layout.tsx +69 -0
- package/src/admin-shell/web/overview-query.ts +15 -0
- package/src/admin-shell/web/platform-overview-screen.tsx +90 -0
- package/src/admin-shell/web/tenant-overview-screen.tsx +90 -0
- package/src/agent-tools/__tests__/tool-catalog.test.ts +179 -0
- package/src/agent-tools/__tests__/tool-dispatch.integration.test.ts +100 -0
- package/src/agent-tools/__tests__/tool-dispatch.test.ts +126 -0
- package/src/agent-tools/index.ts +9 -0
- package/src/agent-tools/tool-catalog.ts +130 -0
- package/src/agent-tools/tool-dispatch.ts +82 -0
- package/src/agent-tools/types.ts +38 -0
- package/src/audit/__tests__/audit-screens.boot.test.ts +51 -0
- package/src/audit/__tests__/audit-security.integration.test.ts +201 -0
- package/src/audit/__tests__/audit.integration.test.ts +50 -25
- package/src/audit/constants.ts +9 -2
- package/src/audit/feature.ts +33 -1
- package/src/audit/handlers/details.query.ts +47 -0
- package/src/audit/handlers/list.query.ts +3 -2
- package/src/audit/i18n.ts +31 -0
- package/src/audit/web/audit-log-detail-screen.tsx +131 -0
- package/src/audit/web/audit-log-screen.tsx +250 -0
- package/src/audit/web/client-plugin.tsx +22 -0
- package/src/audit/web/i18n.ts +12 -0
- package/src/audit/web/index.ts +5 -0
- package/src/auth-email-password/__tests__/account-lockout-no-redis.integration.test.ts +4 -4
- package/src/auth-email-password/__tests__/account-lockout.integration.test.ts +4 -4
- package/src/auth-email-password/__tests__/account-unlock.integration.test.ts +283 -0
- package/src/auth-email-password/__tests__/auth-claims.integration.test.ts +10 -11
- package/src/auth-email-password/__tests__/auth-user-row.test.ts +36 -0
- package/src/auth-email-password/__tests__/auth.integration.test.ts +33 -6
- package/src/auth-email-password/__tests__/email-verification.integration.test.ts +9 -6
- package/src/auth-email-password/__tests__/feature-options.test.ts +33 -0
- package/src/auth-email-password/__tests__/identity-v3-login.integration.test.ts +3 -3
- package/src/auth-email-password/__tests__/invite-flow-kms.integration.test.ts +283 -0
- package/src/auth-email-password/__tests__/invite-flow.integration.test.ts +35 -1
- package/src/auth-email-password/__tests__/login-mfa-setup-required.integration.test.ts +129 -0
- package/src/auth-email-password/__tests__/multi-roles.integration.test.ts +1 -1
- package/src/auth-email-password/__tests__/password-policy.test.ts +15 -0
- package/src/auth-email-password/__tests__/password-reset.integration.test.ts +19 -6
- package/src/auth-email-password/__tests__/public-routes-rate-limit.integration.test.ts +3 -3
- package/src/auth-email-password/__tests__/seed-admin.integration.test.ts +1 -1
- package/src/auth-email-password/__tests__/self-registration-toggle.integration.test.ts +264 -0
- package/src/auth-email-password/__tests__/session-callbacks.integration.test.ts +20 -4
- package/src/auth-email-password/__tests__/session-strict-mode.integration.test.ts +7 -14
- package/src/auth-email-password/__tests__/signup-flow.integration.test.ts +91 -0
- package/src/auth-email-password/constants.ts +25 -0
- package/src/auth-email-password/email-templates.ts +32 -0
- package/src/auth-email-password/errors.ts +9 -0
- package/src/auth-email-password/feature.ts +59 -11
- package/src/auth-email-password/handlers/change-password.write.ts +14 -13
- package/src/auth-email-password/handlers/confirm-account-unlock.write.ts +59 -0
- package/src/auth-email-password/handlers/confirm-token-flow.ts +34 -105
- package/src/auth-email-password/handlers/invite-accept-with-login.write.ts +12 -5
- package/src/auth-email-password/handlers/invite-accept.write.ts +9 -2
- package/src/auth-email-password/handlers/invite-create.write.ts +2 -2
- package/src/auth-email-password/handlers/invite-signup-complete.write.ts +12 -4
- package/src/auth-email-password/handlers/login.write.ts +86 -16
- package/src/auth-email-password/handlers/logout.write.ts +4 -2
- package/src/auth-email-password/handlers/request-account-unlock.write.ts +38 -0
- package/src/auth-email-password/handlers/reset-password.write.ts +3 -2
- package/src/auth-email-password/handlers/self-registration-status.query.ts +23 -0
- package/src/auth-email-password/handlers/signup-confirm.write.ts +19 -11
- package/src/auth-email-password/handlers/signup-request.write.ts +22 -0
- package/src/auth-email-password/handlers/token-request-handler.ts +4 -2
- package/src/auth-email-password/i18n.ts +52 -0
- package/src/auth-email-password/index.ts +13 -2
- package/src/auth-email-password/lockout-store.ts +10 -5
- package/src/auth-email-password/password-policy.ts +2108 -0
- package/src/auth-email-password/seeding.ts +55 -27
- package/src/auth-email-password/self-registration-toggle.ts +25 -0
- package/src/auth-email-password/signed-token.ts +1 -0
- package/src/auth-email-password/unlock-token.ts +28 -0
- package/src/auth-email-password/web/__tests__/auth-client.test.ts +542 -0
- package/src/auth-email-password/web/__tests__/auth-form-logic.test.ts +44 -0
- package/src/auth-email-password/web/__tests__/auth-gate.test.tsx +194 -6
- package/src/auth-email-password/web/__tests__/client-plugin.test.tsx +34 -0
- package/src/auth-email-password/web/__tests__/confirm-account-unlock-screen.test.tsx +64 -0
- package/src/auth-email-password/web/__tests__/forgot-password-screen.test.tsx +5 -1
- package/src/auth-email-password/web/__tests__/invite-accept-screen.test.tsx +129 -2
- package/src/auth-email-password/web/__tests__/login-screen.test.tsx +149 -5
- package/src/auth-email-password/web/__tests__/request-account-unlock-screen.test.tsx +137 -0
- package/src/auth-email-password/web/__tests__/reset-password-screen.test.tsx +5 -1
- package/src/auth-email-password/web/__tests__/signup-complete-screen.test.tsx +134 -0
- package/src/auth-email-password/web/__tests__/signup-screen.test.tsx +93 -0
- package/src/auth-email-password/web/__tests__/test-utils.tsx +12 -2
- package/src/auth-email-password/web/__tests__/use-url-token.test.tsx +29 -0
- package/src/auth-email-password/web/__tests__/verify-email-screen.test.tsx +5 -1
- package/src/auth-email-password/web/auth-client.ts +96 -18
- package/src/auth-email-password/web/auth-form-logic.ts +30 -0
- package/src/auth-email-password/web/auth-form-primitives.tsx +22 -20
- package/src/auth-email-password/web/auth-gate.tsx +140 -19
- package/src/auth-email-password/web/client-plugin.ts +24 -2
- package/src/auth-email-password/web/confirm-account-unlock-screen.tsx +95 -0
- package/src/auth-email-password/web/forgot-password-screen.tsx +6 -6
- package/src/auth-email-password/web/index.ts +16 -4
- package/src/auth-email-password/web/invite-accept-screen.tsx +5 -5
- package/src/auth-email-password/web/login-screen.tsx +78 -22
- package/src/auth-email-password/web/request-account-unlock-screen.tsx +112 -0
- package/src/auth-email-password/web/reset-password-screen.tsx +15 -18
- package/src/auth-email-password/web/session.tsx +11 -6
- package/src/auth-email-password/web/signup-complete-screen.tsx +13 -15
- package/src/auth-email-password/web/signup-screen.tsx +8 -10
- package/src/auth-email-password/web/use-shell-user.ts +5 -2
- package/src/auth-email-password/web/user-menu.tsx +3 -1
- package/src/auth-email-password/web/verify-email-screen.tsx +10 -14
- package/src/auth-foundation/__tests__/feature.test.ts +46 -0
- package/src/auth-foundation/__tests__/session-store.test.ts +77 -0
- package/src/auth-foundation/__tests__/tenant-providers.test.ts +160 -0
- package/src/auth-foundation/__tests__/token-verifier.test.ts +150 -0
- package/src/auth-foundation/boot-checks.ts +150 -0
- package/src/auth-foundation/feature.ts +157 -0
- package/src/auth-foundation/index.ts +37 -0
- package/src/auth-foundation/resolve-anonymous-access.ts +62 -0
- package/src/auth-foundation/types.ts +170 -0
- package/src/auth-mfa/__tests__/base32.test.ts +28 -0
- package/src/auth-mfa/__tests__/disable-and-regenerate.integration.test.ts +265 -0
- package/src/auth-mfa/__tests__/enable-confirm-preauth.integration.test.ts +321 -0
- package/src/auth-mfa/__tests__/enable-flow.integration.test.ts +322 -0
- package/src/auth-mfa/__tests__/enable-start-preauth.integration.test.ts +237 -0
- package/src/auth-mfa/__tests__/errors-i18n-parity.test.ts +39 -0
- package/src/auth-mfa/__tests__/mfa-preauth-setup-token.test.ts +113 -0
- package/src/auth-mfa/__tests__/no-redis.integration.test.ts +209 -0
- package/src/auth-mfa/__tests__/pii-subject-encryption.integration.test.ts +139 -0
- package/src/auth-mfa/__tests__/reencrypt-job.integration.test.ts +247 -0
- package/src/auth-mfa/__tests__/session-auto-revoke.integration.test.ts +169 -0
- package/src/auth-mfa/__tests__/totp.test.ts +131 -0
- package/src/auth-mfa/__tests__/verify.integration.test.ts +319 -0
- package/src/auth-mfa/base32.ts +51 -0
- package/src/auth-mfa/config.ts +26 -0
- package/src/auth-mfa/constants.ts +45 -0
- package/src/auth-mfa/db/queries.ts +45 -0
- package/src/auth-mfa/errors.ts +66 -0
- package/src/auth-mfa/feature.ts +170 -0
- package/src/auth-mfa/handlers/disable.write.ts +67 -0
- package/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +200 -0
- package/src/auth-mfa/handlers/enable-confirm.write.ts +106 -0
- package/src/auth-mfa/handlers/enable-start-preauth.write.ts +87 -0
- package/src/auth-mfa/handlers/enable-start.write.ts +66 -0
- package/src/auth-mfa/handlers/reencrypt.job.ts +234 -0
- package/src/auth-mfa/handlers/regenerate-recovery.write.ts +77 -0
- package/src/auth-mfa/handlers/status.query.ts +23 -0
- package/src/auth-mfa/handlers/verify.write.ts +179 -0
- package/src/auth-mfa/i18n.ts +12 -0
- package/src/auth-mfa/index.ts +19 -0
- package/src/auth-mfa/mfa-challenge-token.ts +90 -0
- package/src/auth-mfa/mfa-preauth-setup-token.ts +86 -0
- package/src/auth-mfa/mfa-setup-token.ts +96 -0
- package/src/auth-mfa/mfa-status-checker.ts +84 -0
- package/src/auth-mfa/mfa-verify-attempts.ts +90 -0
- package/src/auth-mfa/otpauth-uri.ts +21 -0
- package/src/auth-mfa/recovery-codes.ts +54 -0
- package/src/auth-mfa/schema/user-mfa.ts +60 -0
- package/src/auth-mfa/testing.ts +5 -0
- package/src/auth-mfa/totp.ts +67 -0
- package/src/auth-mfa/verify-factor.ts +58 -0
- package/src/auth-mfa/web/__tests__/mfa-client.test.ts +265 -0
- package/src/auth-mfa/web/__tests__/mfa-enable-screen.test.tsx +182 -0
- package/src/auth-mfa/web/__tests__/mfa-setup-preauth-screen.test.tsx +379 -0
- package/src/auth-mfa/web/__tests__/mfa-verify-screen.test.tsx +104 -0
- package/src/auth-mfa/web/client-plugin.ts +37 -0
- package/src/auth-mfa/web/i18n.ts +136 -0
- package/src/auth-mfa/web/index.ts +27 -0
- package/src/auth-mfa/web/mfa-client.ts +184 -0
- package/src/auth-mfa/web/mfa-disable-dialog.tsx +69 -0
- package/src/auth-mfa/web/mfa-enable-screen.tsx +228 -0
- package/src/auth-mfa/web/mfa-recovery-codes-reveal.tsx +53 -0
- package/src/auth-mfa/web/mfa-regenerate-recovery-dialog.tsx +68 -0
- package/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +267 -0
- package/src/auth-mfa/web/mfa-verify-screen.tsx +117 -0
- package/src/auth-mfa/web/qrcode-browser.d.ts +12 -0
- package/src/auth-mfa-user-data/__tests__/hooks.integration.test.ts +134 -0
- package/src/auth-mfa-user-data/hooks.ts +53 -0
- package/src/auth-mfa-user-data/index.ts +21 -0
- package/src/billing-foundation/__tests__/billing-foundation.integration.test.ts +206 -2
- package/src/billing-foundation/__tests__/subscription-provider-contract.ts +93 -0
- package/src/billing-foundation/entities.ts +28 -4
- package/src/billing-foundation/events.ts +6 -2
- package/src/billing-foundation/feature.ts +9 -1
- package/src/billing-foundation/get-subscription-for-tenant.ts +16 -5
- package/src/billing-foundation/handlers/create-portal-session.write.ts +17 -1
- package/src/billing-foundation/handlers/list-subscriptions.query.ts +15 -1
- package/src/billing-foundation/handlers/process-event.write.ts +39 -3
- package/src/billing-foundation/tenant-destroy-hook.ts +46 -0
- package/src/cap-counter/__tests__/cap-list.boot.test.ts +27 -0
- package/src/cap-counter/__tests__/cap-list.security.integration.test.ts +53 -0
- package/src/cap-counter/__tests__/entity-ddl.test.ts +15 -0
- package/src/cap-counter/__tests__/with-cap-enforcement.integration.test.ts +8 -5
- package/src/cap-counter/constants.ts +2 -0
- package/src/cap-counter/enforce-cap.ts +15 -18
- package/src/cap-counter/entity.ts +3 -3
- package/src/cap-counter/feature.ts +28 -1
- package/src/cap-counter/i18n.ts +12 -0
- package/src/cap-counter/with-cap-enforcement.ts +2 -2
- package/src/channel-email/__tests__/pii-guard.test.ts +56 -0
- package/src/channel-email/email-channel.ts +2 -1
- package/src/channel-email/index.ts +1 -0
- package/src/channel-email/pii-guard.ts +39 -0
- package/src/compliance-profiles/__tests__/profile-picker.boot.test.ts +28 -0
- package/src/compliance-profiles/__tests__/profile-picker.security.integration.test.ts +91 -0
- package/src/compliance-profiles/constants.ts +15 -0
- package/src/compliance-profiles/feature.ts +22 -1
- package/src/compliance-profiles/handlers/set-profile.write.ts +2 -2
- package/src/compliance-profiles/i18n.ts +6 -0
- package/src/compliance-profiles/index.ts +6 -0
- package/src/compliance-profiles/web/client-plugin.tsx +22 -0
- package/src/compliance-profiles/web/compliance-profile-screen.tsx +152 -0
- package/src/compliance-profiles/web/i18n.ts +25 -0
- package/src/compliance-profiles/web/index.ts +6 -0
- package/src/config/__tests__/app-overrides.test.ts +11 -0
- package/src/config/__tests__/backing-secrets.integration.test.ts +22 -0
- package/src/config/__tests__/cascade.integration.test.ts +35 -0
- package/src/config/__tests__/config.integration.test.ts +71 -10
- package/src/config/__tests__/deserialize-value.test.ts +5 -8
- package/src/config/__tests__/inherited-redaction.integration.test.ts +4 -3
- package/src/config/__tests__/pii-encrypted.integration.test.ts +179 -0
- package/src/config/__tests__/reencrypt-job-concurrent-write-race.integration.test.ts +234 -0
- package/src/config/__tests__/reencrypt-job-kek-rotation.integration.test.ts +243 -0
- package/src/config/__tests__/write-helpers.test.ts +23 -0
- package/src/config/db/queries/resolver.ts +3 -3
- package/src/config/feature.ts +15 -8
- package/src/config/handlers/cascade.query.ts +2 -1
- package/src/config/handlers/reencrypt.job.ts +209 -0
- package/src/config/handlers/set.write.ts +55 -3
- package/src/config/handlers/values.query.ts +2 -1
- package/src/config/i18n.ts +9 -0
- package/src/config/index.ts +5 -4
- package/src/config/resolver.ts +75 -61
- package/src/config/table.ts +1 -1
- package/src/config/write-helpers.ts +22 -0
- package/src/crypto-shredding/__tests__/forget-subject.integration.test.ts +148 -0
- package/src/crypto-shredding/constants.ts +5 -0
- package/src/crypto-shredding/feature.ts +18 -0
- package/src/crypto-shredding/handlers/forget-subject.write.ts +91 -0
- package/src/crypto-shredding/index.ts +11 -0
- package/src/custom-fields/__tests__/audit-integration.integration.test.ts +1 -1
- package/src/custom-fields/__tests__/custom-fields.integration.test.ts +92 -53
- package/src/custom-fields/__tests__/field-access.integration.test.ts +25 -0
- package/src/custom-fields/__tests__/parse-serialized-field.test.ts +11 -3
- package/src/custom-fields/__tests__/retention.integration.test.ts +40 -0
- package/src/custom-fields/__tests__/user-data-rights.integration.test.ts +26 -79
- package/src/custom-fields/constants.ts +1 -0
- package/src/custom-fields/db/queries/user-data-rights.ts +0 -20
- package/src/custom-fields/entity.ts +1 -1
- package/src/custom-fields/events.ts +4 -3
- package/src/custom-fields/feature.ts +8 -0
- package/src/custom-fields/handlers/delete-system-field.write.ts +2 -1
- package/src/custom-fields/handlers/delete-tenant-field.write.ts +2 -1
- package/src/custom-fields/handlers/set-custom-field.write.ts +3 -44
- package/src/custom-fields/handlers/update-tenant-field.write.ts +0 -17
- package/src/custom-fields/lib/field-access.ts +8 -1
- package/src/custom-fields/lib/parse-serialized-field.ts +12 -4
- package/src/custom-fields/lib/value-schema.ts +1 -1
- package/src/custom-fields/run-retention.ts +9 -1
- package/src/custom-fields/schemas.ts +9 -9
- package/src/custom-fields/wire-for-entity.ts +110 -78
- package/src/custom-fields/wire-user-data-rights.ts +5 -54
- package/src/data-retention/__tests__/parse-override.test.ts +14 -13
- package/src/data-retention/__tests__/retention-cleanup.integration.test.ts +145 -8
- package/src/data-retention/feature.ts +3 -3
- package/src/data-retention/run-retention-cleanup.ts +102 -19
- package/src/delivery/__tests__/delivery-pii-kms.integration.test.ts +171 -0
- package/src/delivery/__tests__/delivery-screens.boot.test.ts +27 -0
- package/src/delivery/__tests__/delivery-security.integration.test.ts +106 -0
- package/src/delivery/constants.ts +13 -51
- package/src/delivery/feature.ts +29 -5
- package/src/delivery/handlers/log.query.ts +28 -7
- package/src/delivery/i18n.ts +6 -0
- package/src/delivery/index.ts +6 -1
- package/src/delivery/public-names.ts +36 -0
- package/src/delivery/tables.ts +3 -3
- package/src/delivery/web/client-plugin.tsx +20 -0
- package/src/delivery/web/delivery-log-screen.tsx +103 -0
- package/src/delivery/web/i18n.ts +25 -0
- package/src/delivery/web/index.ts +3 -0
- package/src/document-ingest-foundation/__tests__/feature.integration.test.ts +147 -0
- package/src/document-ingest-foundation/__tests__/feature.test.ts +105 -0
- package/src/document-ingest-foundation/__tests__/pages.test.ts +37 -0
- package/src/document-ingest-foundation/entity.ts +43 -0
- package/src/document-ingest-foundation/events.ts +25 -0
- package/src/document-ingest-foundation/feature.ts +126 -0
- package/src/document-ingest-foundation/index.ts +12 -0
- package/src/document-ingest-foundation/pages.ts +34 -0
- package/src/feature-toggles/__tests__/compose-tier-resolver.test.ts +304 -0
- package/src/feature-toggles/__tests__/feature-toggles.integration.test.ts +7 -6
- package/src/feature-toggles/__tests__/toggle-admin.boot.test.ts +31 -0
- package/src/feature-toggles/__tests__/toggle-admin.security.integration.test.ts +69 -0
- package/src/feature-toggles/compose-tier-resolver.ts +134 -0
- package/src/feature-toggles/constants.ts +14 -0
- package/src/feature-toggles/feature.ts +23 -3
- package/src/feature-toggles/global-feature-state-table.ts +20 -1
- package/src/feature-toggles/handlers/list.query.ts +1 -1
- package/src/feature-toggles/handlers/registered.query.ts +1 -1
- package/src/feature-toggles/i18n.ts +6 -0
- package/src/feature-toggles/index.ts +8 -0
- package/src/feature-toggles/toggle-runtime.ts +14 -0
- package/src/feature-toggles/web/client-plugin.tsx +22 -0
- package/src/feature-toggles/web/i18n.ts +29 -0
- package/src/feature-toggles/web/index.ts +6 -0
- package/src/feature-toggles/web/toggle-admin-screen.tsx +136 -0
- package/src/file-foundation/__tests__/feature.test.ts +5 -5
- package/src/file-foundation/__tests__/file-foundation.integration.test.ts +4 -3
- package/src/file-foundation/feature.ts +10 -11
- package/src/files-provider-s3/__tests__/s3-provider.integration.test.ts +27 -2
- package/src/files-provider-s3/s3-provider.ts +12 -1
- package/src/folders/__tests__/folders.integration.test.ts +3 -0
- package/src/folders/web/__tests__/folder-manager.test.tsx +43 -2
- package/src/folders/web/__tests__/tree.test.ts +8 -0
- package/src/folders/web/folder-manager.tsx +2 -3
- package/src/folders/web/i18n.ts +0 -2
- package/src/folders/web/tree.ts +13 -2
- package/src/folders-user-data/__tests__/hooks.integration.test.ts +1 -0
- package/src/folders-user-data/index.ts +4 -4
- package/src/inbound-mail-foundation/__tests__/account-state.test.ts +30 -0
- package/src/inbound-mail-foundation/__tests__/connect-routes.integration.test.ts +307 -0
- package/src/inbound-mail-foundation/__tests__/feature.test.ts +169 -0
- package/src/inbound-mail-foundation/__tests__/inbound-mail-foundation.integration.test.ts +644 -0
- package/src/inbound-mail-foundation/__tests__/inbound-mail-provider-contract.ts +111 -0
- package/src/inbound-mail-foundation/__tests__/retention.integration.test.ts +237 -0
- package/src/inbound-mail-foundation/__tests__/watch-supervisor.integration.test.ts +433 -0
- package/src/inbound-mail-foundation/aggregate-id.ts +40 -0
- package/src/inbound-mail-foundation/connect-routes.ts +230 -0
- package/src/inbound-mail-foundation/constants.ts +80 -0
- package/src/inbound-mail-foundation/db/queries/inbound-projections.ts +38 -0
- package/src/inbound-mail-foundation/entities.ts +172 -0
- package/src/inbound-mail-foundation/events.ts +147 -0
- package/src/inbound-mail-foundation/feature.ts +243 -0
- package/src/inbound-mail-foundation/handlers/account-state.ts +23 -0
- package/src/inbound-mail-foundation/handlers/connect-account.write.ts +112 -0
- package/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +69 -0
- package/src/inbound-mail-foundation/handlers/ingest-message.write.ts +333 -0
- package/src/inbound-mail-foundation/handlers/list-accounts.query.ts +44 -0
- package/src/inbound-mail-foundation/handlers/list-messages.query.ts +58 -0
- package/src/inbound-mail-foundation/handlers/scope-visibility.ts +14 -0
- package/src/inbound-mail-foundation/handlers/update-account.write.ts +78 -0
- package/src/inbound-mail-foundation/index.ts +97 -0
- package/src/inbound-mail-foundation/oauth-state.ts +103 -0
- package/src/inbound-mail-foundation/projection.ts +152 -0
- package/src/inbound-mail-foundation/provider-factory.ts +53 -0
- package/src/inbound-mail-foundation/retention-sweep.ts +168 -0
- package/src/inbound-mail-foundation/tenant-destroy-hook.ts +93 -0
- package/src/inbound-mail-foundation/types.ts +250 -0
- package/src/inbound-mail-foundation/watch-supervisor.ts +482 -0
- package/src/inbound-provider-imap/__tests__/feature.test.ts +115 -0
- package/src/inbound-provider-imap/__tests__/imap-foundation.integration.test.ts +302 -0
- package/src/inbound-provider-imap/__tests__/imap-live.integration.test.ts +206 -0
- package/src/inbound-provider-imap/__tests__/plugin-mocked.test.ts +358 -0
- package/src/inbound-provider-imap/credential-document.ts +51 -0
- package/src/inbound-provider-imap/feature.ts +283 -0
- package/src/inbound-provider-imap/imap-client.ts +158 -0
- package/src/inbound-provider-imap/index.ts +17 -0
- package/src/inbound-provider-inmemory/__tests__/feature.test.ts +52 -0
- package/src/inbound-provider-inmemory/feature.ts +154 -0
- package/src/inbound-provider-inmemory/index.ts +12 -0
- package/src/jobs/__tests__/entity-ddl.test.ts +15 -0
- package/src/jobs/__tests__/job-instant.test.ts +15 -0
- package/src/jobs/__tests__/job-system-user.integration.test.ts +4 -8
- package/src/jobs/__tests__/jobs-events.integration.test.ts +37 -1
- package/src/jobs/__tests__/jobs-pii-kms.integration.test.ts +120 -0
- package/src/jobs/__tests__/jobs-screens.boot.test.ts +36 -0
- package/src/jobs/__tests__/jobs-security.integration.test.ts +108 -0
- package/src/jobs/__tests__/reindex-entity-job.integration.test.ts +169 -0
- package/src/jobs/constants.ts +8 -1
- package/src/jobs/feature.ts +52 -8
- package/src/jobs/handlers/detail.query.ts +6 -0
- package/src/jobs/handlers/list.query.ts +8 -1
- package/src/jobs/handlers/projection-rebuild.job.ts +14 -4
- package/src/jobs/handlers/reindex-entity.job.ts +49 -0
- package/src/jobs/i18n.ts +37 -0
- package/src/jobs/job-instant.ts +9 -0
- package/src/jobs/job-run-table.ts +4 -4
- package/src/jobs/web/client-plugin.tsx +22 -0
- package/src/jobs/web/i18n.ts +12 -0
- package/src/jobs/web/index.ts +4 -0
- package/src/jobs/web/job-run-detail-screen.tsx +180 -0
- package/src/jobs/web/job-runs-screen.tsx +146 -0
- package/src/ledger/__tests__/ledger.integration.test.ts +8 -3
- package/src/ledger/handlers/confirm-schedule-period.write.ts +17 -4
- package/src/ledger/recurring.ts +17 -8
- package/src/ledger/web/index.ts +3 -2
- package/src/legal-pages/__tests__/legal-pages.integration.test.ts +55 -0
- package/src/legal-pages/feature.ts +24 -31
- package/src/legal-pages/index.ts +1 -0
- package/src/mail-foundation/__tests__/feature.test.ts +5 -6
- package/src/mail-foundation/__tests__/mail-foundation.integration.test.ts +4 -3
- package/src/mail-foundation/__tests__/mail-transport-contract.ts +58 -0
- package/src/mail-foundation/feature.ts +26 -24
- package/src/mail-transport-inmemory/__tests__/feature.test.ts +23 -0
- package/src/mail-transport-smtp/__tests__/feature.test.ts +65 -0
- package/src/managed-pages/__tests__/branding-coerce.test.ts +2 -6
- package/src/managed-pages/__tests__/i18n-surface.boot.test.ts +30 -0
- package/src/managed-pages/__tests__/managed-pages.integration.test.ts +137 -0
- package/src/managed-pages/feature.ts +45 -47
- package/src/managed-pages/handlers/branding.query.ts +5 -1
- package/src/managed-pages/handlers/by-tenant-published.query.ts +52 -0
- package/src/managed-pages/i18n.ts +66 -0
- package/src/managed-pages/screens/page-screens.ts +5 -1
- package/src/managed-pages/web/client-plugin.tsx +22 -0
- package/src/managed-pages/web/i18n.ts +24 -0
- package/src/managed-pages/web/index.ts +3 -0
- package/src/page-render/__tests__/layout.test.ts +63 -0
- package/src/page-render/__tests__/markdown.test.ts +29 -0
- package/src/page-render/branding.ts +4 -3
- package/src/page-render/index.ts +1 -1
- package/src/page-render/layout.ts +18 -3
- package/src/page-render/markdown.ts +1 -9
- package/src/page-render/web.ts +13 -0
- package/src/personal-access-tokens/__tests__/feature.test.ts +42 -0
- package/src/personal-access-tokens/__tests__/pat.integration.test.ts +239 -0
- package/src/personal-access-tokens/__tests__/scopes.test.ts +42 -0
- package/src/personal-access-tokens/constants.ts +33 -0
- package/src/personal-access-tokens/feature.ts +105 -0
- package/src/personal-access-tokens/handlers/available-scopes.query.ts +21 -0
- package/src/personal-access-tokens/handlers/create.write.ts +47 -0
- package/src/personal-access-tokens/handlers/list.query.ts +50 -0
- package/src/personal-access-tokens/handlers/revoke.write.ts +37 -0
- package/src/personal-access-tokens/hash.ts +17 -0
- package/src/personal-access-tokens/i18n.ts +5 -0
- package/src/personal-access-tokens/index.ts +31 -0
- package/src/personal-access-tokens/resolver.ts +58 -0
- package/src/personal-access-tokens/roles.ts +29 -0
- package/src/personal-access-tokens/schema/api-token.ts +63 -0
- package/src/personal-access-tokens/scopes.ts +43 -0
- package/src/personal-access-tokens/web/client-plugin.tsx +29 -0
- package/src/personal-access-tokens/web/i18n.ts +69 -0
- package/src/personal-access-tokens/web/index.ts +7 -0
- package/src/personal-access-tokens/web/pat-tokens-screen.tsx +265 -0
- package/src/readiness/__tests__/readiness.integration.test.ts +4 -3
- package/src/renderer-simple/__tests__/resolve-variables.test.ts +37 -0
- package/src/renderer-simple/resolve-variables.ts +3 -1
- package/src/secrets/__tests__/rotate.integration.test.ts +115 -18
- package/src/secrets/handlers/rotate.job.ts +77 -98
- package/src/secrets/secrets-context.ts +7 -38
- package/src/secrets/table.ts +7 -8
- package/src/seo/__tests__/llms-txt.test.ts +38 -0
- package/src/seo/__tests__/robots-txt.test.ts +18 -0
- package/src/seo/__tests__/schema-builders.test.ts +57 -0
- package/src/seo/__tests__/seo.integration.test.ts +350 -0
- package/src/seo/__tests__/sitemap.test.ts +38 -0
- package/src/seo/constants.ts +60 -0
- package/src/seo/feature.ts +316 -0
- package/src/seo/handlers/seo-config.query.ts +35 -0
- package/src/seo/index.ts +19 -0
- package/src/seo/llms-txt.ts +33 -0
- package/src/seo/robots-txt.ts +13 -0
- package/src/seo/schema-builders.ts +57 -0
- package/src/seo/sitemap.ts +33 -0
- package/src/sessions/__tests__/auto-revoke-binding.test.ts +71 -0
- package/src/sessions/__tests__/cleanup.integration.test.ts +2 -1
- package/src/sessions/__tests__/password-auto-revoke.integration.test.ts +11 -7
- package/src/sessions/__tests__/rebuild-survival.integration.test.ts +18 -7
- package/src/sessions/__tests__/session-store-registration.test.ts +21 -0
- package/src/sessions/__tests__/sessions-screens.boot.test.ts +55 -0
- package/src/sessions/__tests__/sessions.integration.test.ts +272 -20
- package/src/sessions/__tests__/test-helpers.ts +16 -3
- package/src/sessions/constants.ts +5 -0
- package/src/sessions/db/queries/cleanup.ts +2 -2
- package/src/sessions/feature.ts +175 -26
- package/src/sessions/handlers/detail.query.ts +36 -0
- package/src/sessions/handlers/list.query.ts +14 -9
- package/src/sessions/handlers/mine.query.ts +13 -8
- package/src/sessions/handlers/revoke-all-for-user.write.ts +51 -12
- package/src/sessions/handlers/revoke-all-others.write.ts +19 -1
- package/src/sessions/handlers/revoke.write.ts +13 -0
- package/src/sessions/i18n.ts +23 -0
- package/src/sessions/index.ts +9 -2
- package/src/sessions/schema/user-session.ts +8 -2
- package/src/sessions/session-callbacks.ts +99 -14
- package/src/sessions/session-checker-fail-open.test.ts +77 -0
- package/src/sessions/session-revoked-event.ts +29 -0
- package/src/sessions/testing.ts +14 -1
- package/src/shared/__tests__/chunked-entity-migration.test.ts +145 -0
- package/src/shared/chunked-entity-migration.ts +89 -0
- package/src/shared/decrypt-stored-pii.ts +26 -0
- package/src/shared/encrypt-for-direct-write.ts +23 -0
- package/src/shared/entities-of.ts +17 -0
- package/src/shared/grace-period.test.ts +26 -0
- package/src/shared/grace-period.ts +20 -0
- package/src/{auth-email-password/__tests__ → shared}/identity-v3-hash.test.ts +2 -2
- package/src/shared/index.ts +16 -0
- package/src/shared/map-with-concurrency.test.ts +104 -0
- package/src/shared/map-with-concurrency.ts +38 -0
- package/src/shared/password-hashing.test.ts +35 -0
- package/src/{auth-email-password → shared}/password-hashing.ts +11 -0
- package/src/shared/system-query.ts +5 -0
- package/src/{auth-email-password → shared}/token-burn-store.ts +1 -1
- package/src/subscription-mollie/__tests__/feature.test.ts +97 -0
- package/src/subscription-mollie/__tests__/mollie-foundation.integration.test.ts +12 -1
- package/src/subscription-mollie/__tests__/verify-webhook.test.ts +21 -0
- package/src/subscription-mollie/verify-webhook.ts +1 -0
- package/src/subscription-stripe/__tests__/feature.test.ts +127 -1
- package/src/subscription-stripe/__tests__/runtime.test.ts +14 -24
- package/src/subscription-stripe/__tests__/stripe-foundation.integration.test.ts +15 -5
- package/src/subscription-stripe/__tests__/verify-webhook.test.ts +20 -0
- package/src/subscription-stripe/feature.ts +2 -0
- package/src/subscription-stripe/verify-webhook.ts +3 -2
- package/src/tags/__tests__/schemas.test.ts +34 -0
- package/src/tags/__tests__/tags.integration.test.ts +12 -1
- package/src/tags/entity.ts +6 -1
- package/src/tags/feature.ts +2 -0
- package/src/tags/i18n.ts +5 -0
- package/src/tags/schemas.ts +9 -2
- package/src/tags/web/__tests__/tag-manager.test.tsx +333 -0
- package/src/tags/web/__tests__/tag-section.test.tsx +47 -1
- package/src/tags/web/tag-manager.tsx +1 -1
- package/src/tags/web/tag-section.tsx +6 -3
- package/src/template-resolver/testing.ts +2 -1
- package/src/tenant/__tests__/members-query-kms.integration.test.ts +98 -0
- package/src/tenant/__tests__/members-screens.boot.test.ts +51 -0
- package/src/tenant/__tests__/multi-tenant.integration.test.ts +1 -1
- package/src/tenant/__tests__/tenant-security.integration.test.ts +310 -0
- package/src/tenant/__tests__/tenant.integration.test.ts +8 -4
- package/src/tenant/constants.ts +5 -0
- package/src/tenant/feature.ts +19 -0
- package/src/tenant/handlers/active-tenant-ids.query.ts +8 -2
- package/src/tenant/handlers/cancel-invitation.write.ts +2 -2
- package/src/tenant/handlers/invitations.query.ts +21 -4
- package/src/tenant/handlers/members.query.ts +41 -6
- package/src/tenant/i18n.ts +28 -0
- package/src/tenant/index.ts +7 -2
- package/src/tenant/invitation-table.ts +3 -2
- package/src/tenant/schema/tenant.ts +27 -2
- package/src/tenant/screens.ts +3 -2
- package/src/tenant/seeding.ts +136 -3
- package/src/tenant/web/client-plugin.tsx +20 -0
- package/src/tenant/web/i18n.ts +43 -0
- package/src/tenant/web/index.ts +4 -0
- package/src/tenant/web/members-screen.tsx +270 -0
- package/src/tenant-lifecycle/__tests__/stages.test.ts +22 -0
- package/src/tenant-lifecycle/__tests__/tenant-lifecycle.integration.test.ts +471 -0
- package/src/tenant-lifecycle/boot-checks.ts +45 -0
- package/src/tenant-lifecycle/constants.ts +56 -0
- package/src/tenant-lifecycle/events.ts +46 -0
- package/src/tenant-lifecycle/feature.ts +112 -0
- package/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +79 -0
- package/src/tenant-lifecycle/handlers/request-destruction.write.ts +83 -0
- package/src/tenant-lifecycle/index.ts +7 -0
- package/src/tenant-lifecycle/lib/revoke-tenant-sessions.ts +16 -0
- package/src/tenant-lifecycle/lifecycle-gate.ts +65 -0
- package/src/tenant-lifecycle/run-tenant-destroy.ts +368 -0
- package/src/tenant-lifecycle/stages.ts +206 -0
- package/src/tenant-settings/__tests__/tenant-settings.integration.test.ts +115 -0
- package/src/tenant-settings/config.ts +44 -0
- package/src/tenant-settings/constants.ts +16 -0
- package/src/tenant-settings/feature.ts +49 -0
- package/src/tenant-settings/index.ts +8 -0
- package/src/tenant-settings/tenant-defaults.ts +67 -0
- package/src/text-content/web/__tests__/editor-read-only.test.tsx +33 -6
- package/src/text-content/web/__tests__/group-blocks.test.ts +28 -3
- package/src/text-content/web/client-plugin.tsx +126 -104
- package/src/tier-engine/__tests__/auto-default-tier.integration.test.ts +1 -1
- package/src/tier-engine/__tests__/resolver.integration.test.ts +1 -1
- package/src/tier-engine/__tests__/tier-engine.integration.test.ts +7 -4
- package/src/tier-engine/__tests__/trial-config-validation.test.ts +30 -0
- package/src/tier-engine/feature.ts +13 -6
- package/src/tier-engine/i18n.ts +8 -0
- package/src/tier-engine/web/tier-admin-screen.tsx +26 -19
- package/src/user/__tests__/stream-tenant-backfill.integration.test.ts +186 -0
- package/src/user/__tests__/user.integration.test.ts +26 -0
- package/src/user/constants.ts +1 -0
- package/src/user/db/queries/stream-tenant-backfill.ts +112 -0
- package/src/user/feature.ts +3 -0
- package/src/user/handlers/find-for-auth.query.ts +8 -6
- package/src/user/i18n.ts +20 -0
- package/src/user/index.ts +4 -0
- package/src/user/schema/user.ts +6 -2
- package/src/user/screens.ts +3 -15
- package/src/user/seeding.ts +8 -0
- package/src/user-data-rights/COMPLIANCE.md +21 -1
- package/src/user-data-rights/__tests__/anonymous-deletion-kms.integration.test.ts +145 -0
- package/src/user-data-rights/__tests__/anonymous-deletion.integration.test.ts +3 -0
- package/src/user-data-rights/__tests__/audit-log.integration.test.ts +67 -0
- package/src/user-data-rights/__tests__/boot-checks.test.ts +170 -0
- package/src/user-data-rights/__tests__/cross-data-matrix.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/download-by-token-resolver-trust.integration.test.ts +169 -0
- package/src/user-data-rights/__tests__/download.integration.test.ts +101 -33
- package/src/user-data-rights/__tests__/email-templates.test.ts +15 -0
- package/src/user-data-rights/__tests__/export-encrypted-fields.integration.test.ts +154 -0
- package/src/user-data-rights/__tests__/export-job-idempotency.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/export-job-list-cross-tenant.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/file-binary-forget-cleanup.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/file-binary-forget-failure.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/file-retention.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/file-storage-unification.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/forget-cleanup-hook-ordering.integration.test.ts +41 -62
- package/src/user-data-rights/__tests__/forget-hook-registry.integration.test.ts +110 -0
- package/src/user-data-rights/__tests__/inspector-screens.boot.test.ts +14 -0
- package/src/user-data-rights/__tests__/mail-default-bridge.integration.test.ts +19 -2
- package/src/user-data-rights/__tests__/read-users-rebuild-survives-lifecycle.integration.test.ts +25 -14
- package/src/user-data-rights/__tests__/request-cancel-deletion.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/request-deletion-callback.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/request-export.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/restriction-flow.integration.test.ts +172 -19
- package/src/user-data-rights/__tests__/run-export-jobs-cron-context.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/run-export-jobs.integration.test.ts +257 -26
- package/src/user-data-rights/__tests__/run-forget-cleanup.integration.test.ts +339 -5
- package/src/user-data-rights/__tests__/run-user-export.integration.test.ts +77 -1
- package/src/user-data-rights/__tests__/tenant-model-erasure.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/user-data-rights.integration.test.ts +9 -1
- package/src/user-data-rights/boot-checks.ts +70 -0
- package/src/user-data-rights/db/queries/__tests__/forget-cleanup.integration.test.ts +69 -0
- package/src/user-data-rights/db/queries/forget-cleanup.ts +9 -2
- package/src/user-data-rights/email-templates.ts +6 -6
- package/src/user-data-rights/feature.ts +162 -43
- package/src/user-data-rights/handlers/cancel-deletion.write.ts +8 -11
- package/src/user-data-rights/handlers/deletion-grace-period.ts +4 -1
- package/src/user-data-rights/handlers/download-by-job.query.ts +26 -18
- package/src/user-data-rights/handlers/download-by-token.query.ts +23 -15
- package/src/user-data-rights/handlers/lift-restriction.write.ts +44 -19
- package/src/user-data-rights/handlers/list-download-attempts.query.ts +1 -0
- package/src/user-data-rights/handlers/my-audit-log.query.ts +1 -0
- package/src/user-data-rights/handlers/request-deletion-by-email.write.ts +3 -1
- package/src/user-data-rights/handlers/restrict-account.write.ts +58 -7
- package/src/user-data-rights/handlers/run-forget-cleanup.write.ts +2 -0
- package/src/user-data-rights/i18n.ts +35 -32
- package/src/user-data-rights/lib/is-admin-actor.ts +19 -0
- package/src/user-data-rights/lib/storage-provider-resolver.ts +6 -5
- package/src/user-data-rights/lib/update-user-lifecycle.ts +8 -24
- package/src/user-data-rights/run-export-jobs.ts +55 -19
- package/src/user-data-rights/run-forget-cleanup.ts +112 -7
- package/src/user-data-rights/run-user-export.ts +70 -4
- package/src/user-data-rights/schema/download-token.ts +5 -3
- package/src/user-data-rights/schema/export-job.ts +2 -0
- package/src/user-data-rights/screens.ts +2 -1
- package/src/user-data-rights/web/__tests__/privacy-center-screen.test.tsx +1 -0
- package/src/user-data-rights/web/__tests__/public-deletion-gate.test.tsx +7 -1
- package/src/user-data-rights/web/client-plugin.tsx +6 -1
- package/src/user-data-rights/web/i18n.ts +30 -10
- package/src/user-data-rights/web/privacy-center-screen.tsx +5 -3
- package/src/user-data-rights-defaults/__tests__/bundled-entities.userdata-hooks.integration.test.ts +455 -0
- package/src/user-data-rights-defaults/__tests__/feature-mounted.test.ts +24 -0
- package/src/user-data-rights-defaults/__tests__/user-data-rights-defaults.integration.test.ts +33 -9
- package/src/user-data-rights-defaults/feature.ts +78 -1
- package/src/user-data-rights-defaults/hooks/api-token.userdata-hook.ts +37 -0
- package/src/user-data-rights-defaults/hooks/config-value.userdata-hook.ts +51 -0
- package/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +37 -0
- package/src/user-data-rights-defaults/hooks/feature-mounted.ts +10 -0
- package/src/user-data-rights-defaults/hooks/in-app-message.userdata-hook.ts +38 -0
- package/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +35 -0
- package/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +54 -0
- package/src/user-data-rights-defaults/hooks/tenant-invitation.userdata-hook.ts +112 -0
- package/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +42 -0
- package/src/user-data-rights-defaults/index.ts +26 -0
- package/src/user-profile/__tests__/change-email.integration.test.ts +7 -1
- package/src/user-profile/__tests__/profile-screen.test.tsx +5 -0
- package/src/user-profile/feature.ts +4 -1
- package/src/user-profile/handlers/change-email.write.ts +15 -10
- package/src/user-profile/web/profile-screen.tsx +15 -3
- package/src/auth-email-password/stream-tenant.ts +0 -31
- package/src/feature-toggles/db/queries/toggle-state.ts +0 -25
- package/src/legal-pages/security-headers.ts +0 -1
- package/src/sessions/schema/index.ts +0 -5
- package/src/tenant/schema/index.ts +0 -5
- package/src/user/schema/index.ts +0 -5
- /package/src/{auth-email-password → shared}/identity-v3-hash.ts +0 -0
|
@@ -23,12 +23,12 @@ import {
|
|
|
23
23
|
import { createConfigFeature } from "../../config/feature";
|
|
24
24
|
import { createConfigResolver } from "../../config/resolver";
|
|
25
25
|
import { configValuesTable } from "../../config/table";
|
|
26
|
+
import { verifyPassword } from "../../shared";
|
|
26
27
|
import { createTenantFeature } from "../../tenant/feature";
|
|
27
28
|
import { tenantMembershipsTable } from "../../tenant/membership-table";
|
|
28
29
|
import { tenantEntity, tenantTable } from "../../tenant/schema/tenant";
|
|
29
30
|
import { createUserFeature } from "../../user/feature";
|
|
30
31
|
import { userEntity, userTable } from "../../user/schema/user";
|
|
31
|
-
import { verifyPassword } from "../password-hashing";
|
|
32
32
|
import { seedAdmin } from "../testing";
|
|
33
33
|
|
|
34
34
|
let stack: TestStack;
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// Runtime on/off switch for self-signup (#self-registration-toggle). Pins:
|
|
2
|
+
// 1. Default (no override row) → signup-request works.
|
|
3
|
+
// 2. runtime.apply(off) → signup-request still 200 (silent no-op, anti-enumeration), no mail sent.
|
|
4
|
+
// 3. runtime.apply(on) → works again.
|
|
5
|
+
// Mirrors samples/recipes/feature-toggles' runtime.apply() pattern — flips the
|
|
6
|
+
// in-memory snapshot directly instead of going through the set-handler/HTTP,
|
|
7
|
+
// since that wiring is already covered by feature-toggles' own tests.
|
|
8
|
+
|
|
9
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
10
|
+
import {
|
|
11
|
+
createFeatureTogglesFeature,
|
|
12
|
+
GlobalFeatureToggleRuntime,
|
|
13
|
+
globalFeatureStateTable,
|
|
14
|
+
} from "@cosmicdrift/kumiko-bundled-features/feature-toggles";
|
|
15
|
+
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
16
|
+
import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
17
|
+
import {
|
|
18
|
+
setupTestStack,
|
|
19
|
+
type TestStack,
|
|
20
|
+
unsafeCreateEntityTable,
|
|
21
|
+
unsafePushTables,
|
|
22
|
+
} from "@cosmicdrift/kumiko-framework/stack";
|
|
23
|
+
import { createLateBoundHolder } from "@cosmicdrift/kumiko-framework/testing";
|
|
24
|
+
import { createChannelEmailFeature, createInMemoryTransport } from "../../channel-email";
|
|
25
|
+
import { createConfigFeature } from "../../config";
|
|
26
|
+
import { createConfigResolver } from "../../config/resolver";
|
|
27
|
+
import { configValuesTable } from "../../config/table";
|
|
28
|
+
import { createDeliveryFeature, createDeliveryTestContext } from "../../delivery";
|
|
29
|
+
import { notificationPreferencesTable } from "../../delivery/tables";
|
|
30
|
+
import { createRendererFoundationFeature } from "../../renderer-foundation/feature";
|
|
31
|
+
import { createRendererSimpleFeature, simpleRenderer } from "../../renderer-simple";
|
|
32
|
+
import { createTemplateResolverFeature } from "../../template-resolver/feature";
|
|
33
|
+
import { createTenantFeature } from "../../tenant";
|
|
34
|
+
import { tenantMembershipsTable } from "../../tenant/membership-table";
|
|
35
|
+
import { tenantEntity } from "../../tenant/schema/tenant";
|
|
36
|
+
import { createUserFeature } from "../../user/feature";
|
|
37
|
+
import { userEntity, userTable } from "../../user/schema/user";
|
|
38
|
+
import { AuthHandlers } from "../constants";
|
|
39
|
+
import { createAuthEmailPasswordFeature } from "../feature";
|
|
40
|
+
import {
|
|
41
|
+
AUTH_SELF_REGISTRATION_FEATURE,
|
|
42
|
+
createAuthSelfRegistrationToggleFeature,
|
|
43
|
+
} from "../self-registration-toggle";
|
|
44
|
+
|
|
45
|
+
const APP_ACTIVATION_URL = "https://app.example.com/signup/complete";
|
|
46
|
+
const emailTransport = createInMemoryTransport();
|
|
47
|
+
|
|
48
|
+
let stack: TestStack;
|
|
49
|
+
let runtime: GlobalFeatureToggleRuntime;
|
|
50
|
+
|
|
51
|
+
beforeAll(async () => {
|
|
52
|
+
let effective: () => ReadonlySet<string> = () => new Set();
|
|
53
|
+
const runtimeHolder = createLateBoundHolder<GlobalFeatureToggleRuntime>("runtime");
|
|
54
|
+
|
|
55
|
+
stack = await setupTestStack({
|
|
56
|
+
features: [
|
|
57
|
+
createConfigFeature(),
|
|
58
|
+
createUserFeature(),
|
|
59
|
+
createTenantFeature(),
|
|
60
|
+
createTemplateResolverFeature(),
|
|
61
|
+
createRendererFoundationFeature(),
|
|
62
|
+
createDeliveryFeature(),
|
|
63
|
+
createRendererSimpleFeature(),
|
|
64
|
+
createChannelEmailFeature({
|
|
65
|
+
transport: emailTransport,
|
|
66
|
+
renderer: simpleRenderer,
|
|
67
|
+
resolveEmail: async () => "unused@test.local",
|
|
68
|
+
}),
|
|
69
|
+
createAuthEmailPasswordFeature({
|
|
70
|
+
signup: { tokenTtlMinutes: 60, appUrl: APP_ACTIVATION_URL },
|
|
71
|
+
}),
|
|
72
|
+
createAuthSelfRegistrationToggleFeature(),
|
|
73
|
+
createFeatureTogglesFeature({ getRuntime: () => runtimeHolder.get() }),
|
|
74
|
+
],
|
|
75
|
+
effectiveFeatures: () => effective(),
|
|
76
|
+
extraContext: (deps) => ({
|
|
77
|
+
...createDeliveryTestContext(deps),
|
|
78
|
+
configResolver: createConfigResolver(),
|
|
79
|
+
}),
|
|
80
|
+
systemHooks: [],
|
|
81
|
+
anonymousAccess: { defaultTenantId: SYSTEM_TENANT_ID },
|
|
82
|
+
authConfig: {
|
|
83
|
+
membershipQuery: "tenant:query:memberships",
|
|
84
|
+
loginHandler: AuthHandlers.login,
|
|
85
|
+
signup: {
|
|
86
|
+
requestHandler: AuthHandlers.signupRequest,
|
|
87
|
+
confirmHandler: AuthHandlers.signupConfirm,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await unsafeCreateEntityTable(stack.db, userEntity);
|
|
93
|
+
await unsafeCreateEntityTable(stack.db, tenantEntity);
|
|
94
|
+
// globalFeatureStateTable is auto-provisioned by setupTestStack — no
|
|
95
|
+
// manual push needed (that used to mask a missing r.storeTable()
|
|
96
|
+
// registration entirely).
|
|
97
|
+
await unsafePushTables(stack.db, {
|
|
98
|
+
configValuesTable,
|
|
99
|
+
tenantMembershipsTable,
|
|
100
|
+
notificationPreferencesTable,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
runtime = new GlobalFeatureToggleRuntime(stack.db, stack.registry);
|
|
104
|
+
await runtime.initialize();
|
|
105
|
+
effective = runtime.effectiveFeatures;
|
|
106
|
+
runtimeHolder.set(runtime);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
afterAll(async () => {
|
|
110
|
+
await stack.cleanup();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
beforeEach(async () => {
|
|
114
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${userTable.tableName}"`);
|
|
115
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${globalFeatureStateTable.tableName}"`);
|
|
116
|
+
await runtime.refresh();
|
|
117
|
+
emailTransport.sent.length = 0;
|
|
118
|
+
const allKeys = await stack.redis.redis.keys("signup:*");
|
|
119
|
+
if (allKeys.length > 0) await stack.redis.redis.del(...allKeys);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
async function postSignupRequest(email: string): Promise<Response> {
|
|
123
|
+
return stack.http.raw("POST", "/api/auth/signup-request", { email });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
describe("auth-self-registration toggle", () => {
|
|
127
|
+
test("default (no override) → signup-request works", async () => {
|
|
128
|
+
const res = await postSignupRequest("alice@example.com");
|
|
129
|
+
expect(res.status).toBe(200);
|
|
130
|
+
expect(emailTransport.sent).toHaveLength(1);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("runtime off → signup-request still 200 (anti-enumeration silent-success) but no mail sent", async () => {
|
|
134
|
+
runtime.apply(AUTH_SELF_REGISTRATION_FEATURE, false);
|
|
135
|
+
|
|
136
|
+
const res = await postSignupRequest("blocked@example.com");
|
|
137
|
+
expect(res.status).toBe(200);
|
|
138
|
+
expect(emailTransport.sent).toHaveLength(0);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("runtime back on → mail sends again", async () => {
|
|
142
|
+
runtime.apply(AUTH_SELF_REGISTRATION_FEATURE, false);
|
|
143
|
+
await postSignupRequest("still-blocked@example.com");
|
|
144
|
+
expect(emailTransport.sent).toHaveLength(0);
|
|
145
|
+
|
|
146
|
+
runtime.apply(AUTH_SELF_REGISTRATION_FEATURE, true);
|
|
147
|
+
const res = await postSignupRequest("reenabled@example.com");
|
|
148
|
+
expect(res.status).toBe(200);
|
|
149
|
+
expect(emailTransport.sent).toHaveLength(1);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("status query reflects the runtime flip", async () => {
|
|
153
|
+
const onRes = await stack.http.raw("POST", "/api/query", {
|
|
154
|
+
type: "auth-email-password:query:signup-registration-status",
|
|
155
|
+
payload: {},
|
|
156
|
+
});
|
|
157
|
+
expect((await onRes.json()) as { data?: { enabled: boolean } }).toMatchObject({
|
|
158
|
+
data: { enabled: true },
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
runtime.apply(AUTH_SELF_REGISTRATION_FEATURE, false);
|
|
162
|
+
const offRes = await stack.http.raw("POST", "/api/query", {
|
|
163
|
+
type: "auth-email-password:query:signup-registration-status",
|
|
164
|
+
payload: {},
|
|
165
|
+
});
|
|
166
|
+
expect((await offRes.json()) as { data?: { enabled: boolean } }).toMatchObject({
|
|
167
|
+
data: { enabled: false },
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Regression for Finding 1 (#1468): the suite above always composes the
|
|
173
|
+
// companion toggle feature alongside feature-toggles. An app that mounts
|
|
174
|
+
// createFeatureTogglesFeature + a real effectiveFeatures resolver but
|
|
175
|
+
// forgets (or never knew to) compose createAuthSelfRegistrationToggleFeature
|
|
176
|
+
// must not go silently signup-dead — ctx.hasFeature() is checked against a
|
|
177
|
+
// feature name the registry never registered.
|
|
178
|
+
describe("auth-self-registration toggle — companion feature NOT composed", () => {
|
|
179
|
+
const bareEmailTransport = createInMemoryTransport();
|
|
180
|
+
let bareStack: TestStack;
|
|
181
|
+
let bareRuntime: GlobalFeatureToggleRuntime;
|
|
182
|
+
|
|
183
|
+
beforeAll(async () => {
|
|
184
|
+
const runtimeHolder = createLateBoundHolder<GlobalFeatureToggleRuntime>("bare-runtime");
|
|
185
|
+
let effective: () => ReadonlySet<string> = () => new Set();
|
|
186
|
+
|
|
187
|
+
bareStack = await setupTestStack({
|
|
188
|
+
features: [
|
|
189
|
+
createConfigFeature(),
|
|
190
|
+
createUserFeature(),
|
|
191
|
+
createTenantFeature(),
|
|
192
|
+
createTemplateResolverFeature(),
|
|
193
|
+
createRendererFoundationFeature(),
|
|
194
|
+
createDeliveryFeature(),
|
|
195
|
+
createRendererSimpleFeature(),
|
|
196
|
+
createChannelEmailFeature({
|
|
197
|
+
transport: bareEmailTransport,
|
|
198
|
+
renderer: simpleRenderer,
|
|
199
|
+
resolveEmail: async () => "unused@test.local",
|
|
200
|
+
}),
|
|
201
|
+
createAuthEmailPasswordFeature({
|
|
202
|
+
signup: { tokenTtlMinutes: 60, appUrl: APP_ACTIVATION_URL },
|
|
203
|
+
}),
|
|
204
|
+
// Deliberately NO createAuthSelfRegistrationToggleFeature().
|
|
205
|
+
createFeatureTogglesFeature({ getRuntime: () => runtimeHolder.get() }),
|
|
206
|
+
],
|
|
207
|
+
effectiveFeatures: () => effective(),
|
|
208
|
+
extraContext: (deps) => ({
|
|
209
|
+
...createDeliveryTestContext(deps),
|
|
210
|
+
configResolver: createConfigResolver(),
|
|
211
|
+
}),
|
|
212
|
+
systemHooks: [],
|
|
213
|
+
anonymousAccess: { defaultTenantId: SYSTEM_TENANT_ID },
|
|
214
|
+
authConfig: {
|
|
215
|
+
membershipQuery: "tenant:query:memberships",
|
|
216
|
+
loginHandler: AuthHandlers.login,
|
|
217
|
+
signup: {
|
|
218
|
+
requestHandler: AuthHandlers.signupRequest,
|
|
219
|
+
confirmHandler: AuthHandlers.signupConfirm,
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
await unsafeCreateEntityTable(bareStack.db, userEntity);
|
|
225
|
+
await unsafeCreateEntityTable(bareStack.db, tenantEntity);
|
|
226
|
+
await unsafePushTables(bareStack.db, {
|
|
227
|
+
configValuesTable,
|
|
228
|
+
tenantMembershipsTable,
|
|
229
|
+
notificationPreferencesTable,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
bareRuntime = new GlobalFeatureToggleRuntime(bareStack.db, bareStack.registry);
|
|
233
|
+
await bareRuntime.initialize();
|
|
234
|
+
effective = bareRuntime.effectiveFeatures;
|
|
235
|
+
runtimeHolder.set(bareRuntime);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
afterAll(async () => {
|
|
239
|
+
await bareStack.cleanup();
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Fixed (#1468): ctx.hasFeature() is fail-closed for a name nothing ever
|
|
243
|
+
// registered — signup-request and the status query now only gate on the
|
|
244
|
+
// toggle when registry.getFeature(AUTH_SELF_REGISTRATION_FEATURE) proves
|
|
245
|
+
// the companion feature is actually composed, so an app that mounts
|
|
246
|
+
// auth-email-password without it keeps self-signup working.
|
|
247
|
+
test("signup-request still sends mail when the companion toggle feature was never composed", async () => {
|
|
248
|
+
const res = await bareStack.http.raw("POST", "/api/auth/signup-request", {
|
|
249
|
+
email: "not-toggle-aware@example.com",
|
|
250
|
+
});
|
|
251
|
+
expect(res.status).toBe(200);
|
|
252
|
+
expect(bareEmailTransport.sent).toHaveLength(1);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("signup-registration-status reports enabled:true with no toggle feature mounted", async () => {
|
|
256
|
+
const res = await bareStack.http.raw("POST", "/api/query", {
|
|
257
|
+
type: "auth-email-password:query:signup-registration-status",
|
|
258
|
+
payload: {},
|
|
259
|
+
});
|
|
260
|
+
expect((await res.json()) as { data?: { enabled: boolean } }).toMatchObject({
|
|
261
|
+
data: { enabled: true },
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
import { randomBytes } from "node:crypto";
|
|
3
3
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
4
|
-
import { createEncryptionProvider } from "@cosmicdrift/kumiko-framework/db";
|
|
5
4
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
6
5
|
import {
|
|
7
6
|
setupTestStack,
|
|
@@ -10,10 +9,12 @@ import {
|
|
|
10
9
|
unsafeCreateEntityTable,
|
|
11
10
|
unsafePushTables,
|
|
12
11
|
} from "@cosmicdrift/kumiko-framework/stack";
|
|
12
|
+
import { createTestEnvelopeCipher } from "@cosmicdrift/kumiko-framework/testing";
|
|
13
13
|
import * as jose from "jose";
|
|
14
14
|
import { createConfigFeature } from "../../config";
|
|
15
15
|
import { createConfigResolver } from "../../config/resolver";
|
|
16
16
|
import { configValuesTable } from "../../config/table";
|
|
17
|
+
import { hashPassword } from "../../shared";
|
|
17
18
|
import { createTenantFeature } from "../../tenant";
|
|
18
19
|
import { tenantMembershipsTable } from "../../tenant/membership-table";
|
|
19
20
|
import { tenantEntity } from "../../tenant/schema/tenant";
|
|
@@ -23,7 +24,6 @@ import { createUserFeature } from "../../user/feature";
|
|
|
23
24
|
import { userEntity, userTable } from "../../user/schema/user";
|
|
24
25
|
import { AuthErrors, AuthHandlers } from "../constants";
|
|
25
26
|
import { createAuthEmailPasswordFeature } from "../feature";
|
|
26
|
-
import { hashPassword } from "../password-hashing";
|
|
27
27
|
|
|
28
28
|
// In-memory fake of a real sessions-store — just enough to observe that the
|
|
29
29
|
// framework calls the callbacks at the right moments and threads the sid back
|
|
@@ -90,8 +90,8 @@ const TENANT_A: TenantId = "00000000-0000-4000-8000-0000000000a1";
|
|
|
90
90
|
const TENANT_B: TenantId = "00000000-0000-4000-8000-0000000000b1";
|
|
91
91
|
|
|
92
92
|
beforeAll(async () => {
|
|
93
|
-
const encryption =
|
|
94
|
-
const resolver = createConfigResolver({ encryption });
|
|
93
|
+
const encryption = createTestEnvelopeCipher(encryptionKey);
|
|
94
|
+
const resolver = createConfigResolver({ cipher: encryption });
|
|
95
95
|
|
|
96
96
|
store = createFakeStore();
|
|
97
97
|
sidStream = deterministicSids();
|
|
@@ -120,6 +120,22 @@ beforeAll(async () => {
|
|
|
120
120
|
await unsafeCreateEntityTable(stack.db, userEntity);
|
|
121
121
|
await unsafeCreateEntityTable(stack.db, tenantEntity);
|
|
122
122
|
await unsafePushTables(stack.db, { configValuesTable, tenantMembershipsTable });
|
|
123
|
+
|
|
124
|
+
// Prime request-helper's per-user sid cache for systemAdmin (the seed
|
|
125
|
+
// actor every test's seedUser() call authenticates as) here, once,
|
|
126
|
+
// before any test's observation window opens — otherwise whichever
|
|
127
|
+
// test happens to call seedUser() first would observe an extra
|
|
128
|
+
// store.created entry for seed plumbing, not the auth-route behavior
|
|
129
|
+
// under test (#1452: authHeader() now mints one sid per user.id, not
|
|
130
|
+
// one per call, so this only ever fires here).
|
|
131
|
+
await seedUser({
|
|
132
|
+
email: "warmup-seed-actor@example.com",
|
|
133
|
+
password: "warmup-password-not-asserted-on",
|
|
134
|
+
tenants: [],
|
|
135
|
+
});
|
|
136
|
+
store.live.clear();
|
|
137
|
+
store.created.length = 0;
|
|
138
|
+
store.revoked.length = 0;
|
|
123
139
|
});
|
|
124
140
|
|
|
125
141
|
afterAll(async () => {
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// so legacy stateless tokens are expected to have expired. Default false
|
|
5
|
-
// keeps pre-upgrade tokens working; this suite flips it on and asserts.
|
|
1
|
+
// Pins the default sessionChecker behavior: a JWT that arrives WITHOUT a
|
|
2
|
+
// `jti` is rejected at the middleware. There is no relaxed mode — any app
|
|
3
|
+
// that wires sessionChecker gets this for free.
|
|
6
4
|
|
|
7
5
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
8
6
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -21,12 +19,8 @@ let stack: TestStack;
|
|
|
21
19
|
const TENANT: TenantId = testTenantId(1);
|
|
22
20
|
const userId = TestUsers.systemAdmin.id;
|
|
23
21
|
|
|
24
|
-
// Stub checker that always accepts.
|
|
25
|
-
//
|
|
26
|
-
// fires in the strict-mode path. It's present to satisfy the framework's
|
|
27
|
-
// "you wired sessionChecker, so we'll run it if we have an sid" contract.
|
|
28
|
-
// Accepts the full AuthSessionChecker signature (sid + expectedUserId)
|
|
29
|
-
// even though it doesn't use the args.
|
|
22
|
+
// Stub checker that always accepts. Accepts the full AuthSessionChecker
|
|
23
|
+
// signature (sid + expectedUserId) even though it doesn't use the args.
|
|
30
24
|
async function stubChecker(_sid: string, _expectedUserId: string): Promise<"live"> {
|
|
31
25
|
return "live";
|
|
32
26
|
}
|
|
@@ -42,7 +36,6 @@ beforeAll(async () => {
|
|
|
42
36
|
authConfig: {
|
|
43
37
|
membershipQuery: "tenant:query:memberships",
|
|
44
38
|
sessionChecker: stubChecker,
|
|
45
|
-
sessionStrictMode: true,
|
|
46
39
|
},
|
|
47
40
|
});
|
|
48
41
|
});
|
|
@@ -51,7 +44,7 @@ afterAll(async () => {
|
|
|
51
44
|
await stack.cleanup();
|
|
52
45
|
});
|
|
53
46
|
|
|
54
|
-
describe("
|
|
47
|
+
describe("sessionChecker wired: sidless JWTs are rejected", () => {
|
|
55
48
|
test("JWT without jti → 401 with reason=no_sid", async () => {
|
|
56
49
|
// Hand-signed JWT that carries id + tenantId + roles but NO jti. The
|
|
57
50
|
// standard testing request-helper signs JWTs the same way on user
|
|
@@ -93,7 +86,7 @@ describe("sessionStrictMode: sidless JWTs are rejected", () => {
|
|
|
93
86
|
expect(res.status).not.toBe(401);
|
|
94
87
|
// Narrow the "not 401" to exclude other 4xx middleware errors too.
|
|
95
88
|
// A 403 from access-layer or a 400 from shape-validation wouldn't
|
|
96
|
-
// come from
|
|
89
|
+
// come from the sid gate, but either would be a different code
|
|
97
90
|
// path than the one we're testing — flag them if they surface.
|
|
98
91
|
expect(res.status).not.toBe(403);
|
|
99
92
|
expect(res.status).not.toBe(400);
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
|
|
27
27
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
28
28
|
import { asRawClient, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
29
|
+
import { buildEntityTable } from "@cosmicdrift/kumiko-framework/db";
|
|
30
|
+
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
29
31
|
import {
|
|
30
32
|
setupTestStack,
|
|
31
33
|
type TestStack,
|
|
@@ -44,11 +46,53 @@ import { createTemplateResolverFeature } from "../../template-resolver/feature";
|
|
|
44
46
|
import { createTenantFeature } from "../../tenant";
|
|
45
47
|
import { tenantMembershipsTable } from "../../tenant/membership-table";
|
|
46
48
|
import { tenantEntity, tenantTable } from "../../tenant/schema/tenant";
|
|
49
|
+
// kumiko-lint-ignore cross-feature-import regression-proof for #1463: seedTenant
|
|
50
|
+
// must fire tier-engine's entity postSave hook on self-signup, not just on the
|
|
51
|
+
// TenantHandlers.create HTTP path.
|
|
52
|
+
import { tierAssignmentEntity } from "../../tier-engine/entity";
|
|
53
|
+
import { createTierEngineFeature } from "../../tier-engine/feature";
|
|
47
54
|
import { createUserFeature } from "../../user/feature";
|
|
48
55
|
import { userEntity, userTable } from "../../user/schema/user";
|
|
49
56
|
import { AuthErrors, AuthHandlers } from "../constants";
|
|
50
57
|
import { createAuthEmailPasswordFeature } from "../feature";
|
|
51
58
|
|
|
59
|
+
const tierAssignmentTable = buildEntityTable("tier-assignment", tierAssignmentEntity);
|
|
60
|
+
|
|
61
|
+
// Regression-proof for #1478: provisionSignupAccount only threaded `hooks`
|
|
62
|
+
// into seedTenant, leaving seedUserWithPassword's `user`-entity and
|
|
63
|
+
// seedTenantMembership's `tenantMembership`-entity postSave hooks unfired
|
|
64
|
+
// on self-signup — same bug class as #1463, just for the other two
|
|
65
|
+
// entities in the same call. This vendor feature's hook is the user-side
|
|
66
|
+
// analog of tier-engine's tenant hook above.
|
|
67
|
+
const userPostSaveFired = { count: 0 };
|
|
68
|
+
const userHookVendorFeature = defineFeature("user-hook-vendor-test", (r) => {
|
|
69
|
+
r.requires("user");
|
|
70
|
+
r.hook("postSave", { allOf: "user" }, async (ctx) => {
|
|
71
|
+
if (ctx.isNew) userPostSaveFired.count++;
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Regression-proof for #1478/#1526: seedTenantMembership must fire the
|
|
76
|
+
// `tenantMembership` entity's postSave hooks (e.g. a Welcome-notification
|
|
77
|
+
// hook) on self-signup too — pins the `fireEntityPostSave` call added at
|
|
78
|
+
// seeding.ts's seedTenantMembership, not just the `user`/`tenant` ones.
|
|
79
|
+
// #1566: also record ctx._tenantId so a shallow-only rescope regresses.
|
|
80
|
+
const tenantMembershipPostSaveFired = { count: 0, seenTenantIds: [] as string[] };
|
|
81
|
+
const tenantMembershipHookVendorFeature = defineFeature(
|
|
82
|
+
"tenant-membership-hook-vendor-test",
|
|
83
|
+
(r) => {
|
|
84
|
+
r.requires("tenant");
|
|
85
|
+
r.hook("postSave", { allOf: "tenant-membership" }, async (result, context) => {
|
|
86
|
+
if (result.isNew) {
|
|
87
|
+
tenantMembershipPostSaveFired.count++;
|
|
88
|
+
if (context._tenantId !== undefined) {
|
|
89
|
+
tenantMembershipPostSaveFired.seenTenantIds.push(String(context._tenantId));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
|
|
52
96
|
const APP_ACTIVATION_URL = "https://app.example.com/signup/complete";
|
|
53
97
|
|
|
54
98
|
// Activation mails now go through delivery (ctx.notify → channel-email). The
|
|
@@ -78,6 +122,14 @@ beforeAll(async () => {
|
|
|
78
122
|
createAuthEmailPasswordFeature({
|
|
79
123
|
signup: { tokenTtlMinutes: 60, appUrl: APP_ACTIVATION_URL },
|
|
80
124
|
}),
|
|
125
|
+
// Regression-proof for #1463: seedTenant must fire the entity postSave
|
|
126
|
+
// hook on self-signup too, not just on the TenantHandlers.create path.
|
|
127
|
+
createTierEngineFeature({
|
|
128
|
+
defaultTier: "free",
|
|
129
|
+
tierMap: { free: { features: [], caps: {} } },
|
|
130
|
+
}),
|
|
131
|
+
userHookVendorFeature,
|
|
132
|
+
tenantMembershipHookVendorFeature,
|
|
81
133
|
],
|
|
82
134
|
extraContext: (deps) => ({
|
|
83
135
|
...createDeliveryTestContext(deps),
|
|
@@ -102,6 +154,7 @@ beforeAll(async () => {
|
|
|
102
154
|
configValuesTable,
|
|
103
155
|
tenantMembershipsTable,
|
|
104
156
|
notificationPreferencesTable,
|
|
157
|
+
tierAssignmentTable,
|
|
105
158
|
});
|
|
106
159
|
});
|
|
107
160
|
|
|
@@ -112,8 +165,12 @@ afterAll(async () => {
|
|
|
112
165
|
beforeEach(async () => {
|
|
113
166
|
await asRawClient(stack.db).unsafe(`DELETE FROM "${userTable.tableName}"`);
|
|
114
167
|
await asRawClient(stack.db).unsafe(`DELETE FROM "${tenantMembershipsTable.tableName}"`);
|
|
168
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${tierAssignmentTable.tableName}"`);
|
|
115
169
|
await asRawClient(stack.db).unsafe(`DELETE FROM "${tenantTable.tableName}"`);
|
|
116
170
|
emailTransport.sent.length = 0;
|
|
171
|
+
userPostSaveFired.count = 0;
|
|
172
|
+
tenantMembershipPostSaveFired.count = 0;
|
|
173
|
+
tenantMembershipPostSaveFired.seenTenantIds = [];
|
|
117
174
|
// Redis-cleanup damit Resend-Tests keine state-leaks haben.
|
|
118
175
|
const allKeys = await stack.redis.redis.keys("signup:*");
|
|
119
176
|
if (allKeys.length > 0) await stack.redis.redis.del(...allKeys);
|
|
@@ -220,6 +277,28 @@ describe("POST /api/auth/signup-confirm", () => {
|
|
|
220
277
|
expect(JSON.parse(rolesRaw) as string[]).toContain("Admin");
|
|
221
278
|
}
|
|
222
279
|
|
|
280
|
+
// #1463 regression: seedTenant fires the tenant entity's postSave
|
|
281
|
+
// hooks — tier-engine's auto-default-tier hook must run on self-signup
|
|
282
|
+
// exactly like it does on the regular TenantHandlers.create HTTP path.
|
|
283
|
+
const tierRows = await selectMany(stack.db, tierAssignmentTable, {
|
|
284
|
+
tenantId: body.user?.tenantId ?? "",
|
|
285
|
+
});
|
|
286
|
+
expect(tierRows).toHaveLength(1);
|
|
287
|
+
expect(tierRows[0]?.["tier"]).toBe("free");
|
|
288
|
+
|
|
289
|
+
// #1478 regression: seedUserWithPassword (called from
|
|
290
|
+
// provisionSignupAccount) fires the `user` entity's postSave hooks too —
|
|
291
|
+
// before this fix, only seedTenant's hooks param was wired through.
|
|
292
|
+
expect(userPostSaveFired.count).toBe(1);
|
|
293
|
+
|
|
294
|
+
// #1478/#1526 regression: seedTenantMembership (called from
|
|
295
|
+
// provisionSignupAccount) fires the `tenant-membership` entity's postSave
|
|
296
|
+
// hooks too — before this fix, only seedTenant's hooks param was wired.
|
|
297
|
+
expect(tenantMembershipPostSaveFired.count).toBe(1);
|
|
298
|
+
// #1566: rescope must put the new tenant on the hook context, not the
|
|
299
|
+
// anonymous/system caller's tenantId.
|
|
300
|
+
expect(tenantMembershipPostSaveFired.seenTenantIds).toEqual([body.user?.tenantId ?? ""]);
|
|
301
|
+
|
|
223
302
|
// Authority-Beweis: Login mit dem gesetzten Password funktioniert.
|
|
224
303
|
const loginRes = await postLogin(email, password);
|
|
225
304
|
expect(loginRes.status).toBe(200);
|
|
@@ -320,4 +399,16 @@ describe("POST /api/auth/signup-confirm", () => {
|
|
|
320
399
|
const loginAttacker = await postLogin(email, attackerPassword);
|
|
321
400
|
expect(loginAttacker.status).not.toBe(200);
|
|
322
401
|
});
|
|
402
|
+
|
|
403
|
+
test("common password → 400 (schema rejects breach-list password) (#1340)", async () => {
|
|
404
|
+
const email = "common-signup@example.com";
|
|
405
|
+
const token = await requestSignup(email);
|
|
406
|
+
|
|
407
|
+
const confirmRes = await postSignupConfirm(token, "password1");
|
|
408
|
+
expect(confirmRes.status).toBe(400);
|
|
409
|
+
|
|
410
|
+
// No user created — the rejected confirm never landed.
|
|
411
|
+
const userRows = await selectMany(stack.db, userTable, { email });
|
|
412
|
+
expect(userRows).toHaveLength(0);
|
|
413
|
+
});
|
|
323
414
|
});
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
// der server-side Zugriff (handlers, dispatcher) erhalten.
|
|
7
7
|
export const AUTH_EMAIL_PASSWORD_FEATURE = "auth-email-password" as const;
|
|
8
8
|
|
|
9
|
+
// Minimum length for reset/verify hmacSecret — mirrors the ≥32-char
|
|
10
|
+
// JWT_SECRET env check (HMAC-SHA256 key material).
|
|
11
|
+
export const MIN_HMAC_SECRET_LENGTH = 32;
|
|
12
|
+
|
|
9
13
|
// Qualified handler names. Non-CRUD handlers, no entity prefix.
|
|
10
14
|
export const AuthHandlers = {
|
|
11
15
|
login: "auth-email-password:write:login",
|
|
@@ -15,6 +19,12 @@ export const AuthHandlers = {
|
|
|
15
19
|
resetPassword: "auth-email-password:write:reset-password",
|
|
16
20
|
requestEmailVerification: "auth-email-password:write:request-email-verification",
|
|
17
21
|
verifyEmail: "auth-email-password:write:verify-email",
|
|
22
|
+
// Account-Unlock Magic-Link (fixes the lockout-DoS where the monotonic
|
|
23
|
+
// failure-counter re-locks immediately after expiry, #1266). Mirrors
|
|
24
|
+
// reset: request mints an HMAC token + mails it, confirm clears the
|
|
25
|
+
// Redis lockout state.
|
|
26
|
+
requestAccountUnlock: "auth-email-password:write:request-account-unlock",
|
|
27
|
+
confirmAccountUnlock: "auth-email-password:write:confirm-account-unlock",
|
|
18
28
|
// Magic-Link Self-Signup (Pre-Activation-Token-Pattern). request mintet
|
|
19
29
|
// einen opaken Random-Token, speichert ihn bidirektional in Redis und
|
|
20
30
|
// sendet eine Aktivierungs-Mail. confirm löst den Token ein und legt
|
|
@@ -36,6 +46,12 @@ export const AuthHandlers = {
|
|
|
36
46
|
inviteCancel: "auth-email-password:write:invite-cancel",
|
|
37
47
|
} as const;
|
|
38
48
|
|
|
49
|
+
// Qualified query names. Anonymous-readable status so the (unauthenticated)
|
|
50
|
+
// signup page can decide whether to show its own link/form.
|
|
51
|
+
export const AuthQueries = {
|
|
52
|
+
signupRegistrationStatus: "auth-email-password:query:signup-registration-status",
|
|
53
|
+
} as const;
|
|
54
|
+
|
|
39
55
|
// Error codes — kept intentionally generic so clients can't distinguish
|
|
40
56
|
// "email doesn't exist" from "password wrong". Both surface as invalid_credentials.
|
|
41
57
|
// Soft-deleted users also collapse into invalid_credentials to avoid enumeration.
|
|
@@ -55,6 +71,11 @@ export const AuthErrors = {
|
|
|
55
71
|
invalidVerificationToken: "invalid_verification_token",
|
|
56
72
|
verificationNotConfigured: "email_verification_not_configured",
|
|
57
73
|
emailNotVerified: "email_not_verified",
|
|
74
|
+
// Account-Unlock: mirrors the reset-token handling — every verify
|
|
75
|
+
// failure (malformed/bad-signature/expired) collapses to this single
|
|
76
|
+
// code so a probing client can't distinguish tampered from stale.
|
|
77
|
+
invalidUnlockToken: "invalid_unlock_token",
|
|
78
|
+
unlockNotConfigured: "account_unlock_not_configured",
|
|
58
79
|
// Self-Signup: alle confirm-Failures (unbekannter Token, schon
|
|
59
80
|
// konsumiert, abgelaufen) collapsen auf diesen Code — gleicher
|
|
60
81
|
// anti-enumeration-Trade-off wie reset/verify.
|
|
@@ -99,6 +120,10 @@ export const AuthErrors = {
|
|
|
99
120
|
export const AUTH_LOCKOUT_DEFAULT_MAX_FAILED_ATTEMPTS = 5;
|
|
100
121
|
export const AUTH_LOCKOUT_DEFAULT_DURATION_MINUTES = 15;
|
|
101
122
|
|
|
123
|
+
// Account-unlock token TTL — same as reset (15min): short-lived, the user
|
|
124
|
+
// clicks it right after hitting the lockout screen.
|
|
125
|
+
export const AUTH_UNLOCK_DEFAULT_TTL_MINUTES = 15;
|
|
126
|
+
|
|
102
127
|
export const AUTH_RESET_DEFAULT_TTL_MINUTES = 15;
|
|
103
128
|
// Verification tokens live longer by default because the user may not be
|
|
104
129
|
// at their computer the moment they sign up — 24h covers "verify after
|
|
@@ -73,6 +73,14 @@ const STRINGS = {
|
|
|
73
73
|
inviteExpiry: (when: string) => `Der Link läuft am ${when} ab.`,
|
|
74
74
|
inviteIgnore:
|
|
75
75
|
"Falls du diese Einladung nicht erwartet hast, kannst du diese E-Mail ignorieren.",
|
|
76
|
+
unlockSubject: (app: string) => `${app} — Konto entsperren`,
|
|
77
|
+
unlockGreeting: "Hallo,",
|
|
78
|
+
unlockIntro: (app: string) =>
|
|
79
|
+
`dein ${app}-Konto wurde nach mehreren fehlgeschlagenen Anmeldeversuchen vorübergehend gesperrt. Klicke auf den folgenden Link, um es sofort zu entsperren:`,
|
|
80
|
+
unlockButton: "Konto entsperren",
|
|
81
|
+
unlockExpiry: (when: string) => `Der Link läuft am ${when} ab.`,
|
|
82
|
+
unlockIgnore:
|
|
83
|
+
"Falls du diese Sperre nicht ausgelöst hast, kannst du diese E-Mail ignorieren — die Sperre läuft von selbst ab.",
|
|
76
84
|
},
|
|
77
85
|
en: {
|
|
78
86
|
resetSubject: (app: string) => `${app} — Reset your password`,
|
|
@@ -105,6 +113,14 @@ const STRINGS = {
|
|
|
105
113
|
inviteButton: "Accept invitation",
|
|
106
114
|
inviteExpiry: (when: string) => `The link expires on ${when}.`,
|
|
107
115
|
inviteIgnore: "If you weren't expecting this invitation, you can ignore this email.",
|
|
116
|
+
unlockSubject: (app: string) => `${app} — Unlock your account`,
|
|
117
|
+
unlockGreeting: "Hi,",
|
|
118
|
+
unlockIntro: (app: string) =>
|
|
119
|
+
`your ${app} account was temporarily locked after several failed sign-in attempts. Click the link below to unlock it immediately:`,
|
|
120
|
+
unlockButton: "Unlock account",
|
|
121
|
+
unlockExpiry: (when: string) => `The link expires on ${when}.`,
|
|
122
|
+
unlockIgnore:
|
|
123
|
+
"If you didn't trigger this lock, you can ignore this email — the lock expires on its own.",
|
|
108
124
|
},
|
|
109
125
|
} as const;
|
|
110
126
|
|
|
@@ -151,6 +167,22 @@ export function renderResetPasswordEmail(args: RenderTokenContentArgs): AuthMail
|
|
|
151
167
|
});
|
|
152
168
|
}
|
|
153
169
|
|
|
170
|
+
export function renderUnlockAccountEmail(args: RenderTokenContentArgs): AuthMailContent {
|
|
171
|
+
const locale = args.locale ?? "en";
|
|
172
|
+
const appName = args.appName ?? (locale === "de" ? "Konto" : "Account");
|
|
173
|
+
const t = STRINGS[locale];
|
|
174
|
+
return tokenMailContent({
|
|
175
|
+
subject: t.unlockSubject(appName),
|
|
176
|
+
header: t.unlockButton,
|
|
177
|
+
greeting: t.unlockGreeting,
|
|
178
|
+
intro: t.unlockIntro(appName),
|
|
179
|
+
buttonLabel: t.unlockButton,
|
|
180
|
+
buttonUrl: args.url,
|
|
181
|
+
expiry: t.unlockExpiry(formatExpiry(args.expiresAt)),
|
|
182
|
+
ignore: t.unlockIgnore,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
154
186
|
export function renderVerifyEmail(args: RenderTokenContentArgs): AuthMailContent {
|
|
155
187
|
const locale = args.locale ?? "en";
|
|
156
188
|
const appName = args.appName ?? (locale === "de" ? "Konto" : "Account");
|
|
@@ -37,6 +37,15 @@ export function invalidResetToken() {
|
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// @wrapper-known error-helper
|
|
41
|
+
export function invalidUnlockToken() {
|
|
42
|
+
return writeFailure(
|
|
43
|
+
new UnprocessableError(AuthErrors.invalidUnlockToken, {
|
|
44
|
+
i18nKey: "auth.errors.invalidUnlockToken",
|
|
45
|
+
}),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
40
49
|
// @wrapper-known error-helper
|
|
41
50
|
export function invalidVerificationToken() {
|
|
42
51
|
return writeFailure(
|