@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
package/src/sessions/feature.ts
CHANGED
|
@@ -1,29 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EXT_SESSION_STORE,
|
|
3
|
+
type SessionStore,
|
|
4
|
+
type SessionStoreProvider,
|
|
5
|
+
} from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
1
6
|
import { buildEntityTableMeta } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
access,
|
|
9
|
+
defineFeature,
|
|
10
|
+
type FeatureDefinition,
|
|
11
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
12
|
+
import { SESSION_DETAIL_SCREEN_ID, SESSION_LIST_SCREEN_ID, SessionQueries } from "./constants";
|
|
3
13
|
import { cleanupJob } from "./handlers/cleanup.job";
|
|
14
|
+
import { detailQuery } from "./handlers/detail.query";
|
|
4
15
|
import { listQuery } from "./handlers/list.query";
|
|
5
16
|
import { mineQuery } from "./handlers/mine.query";
|
|
6
17
|
import { revokeWrite } from "./handlers/revoke.write";
|
|
7
18
|
import { revokeAllForUserWrite } from "./handlers/revoke-all-for-user.write";
|
|
8
19
|
import { revokeAllOthersWrite } from "./handlers/revoke-all-others.write";
|
|
20
|
+
import { SESSIONS_I18N } from "./i18n";
|
|
9
21
|
import { userSessionEntity } from "./schema/user-session";
|
|
10
|
-
import
|
|
22
|
+
import {
|
|
23
|
+
createSessionCallbacks,
|
|
24
|
+
type SessionAllOthersRevoker,
|
|
25
|
+
type SessionMassRevoker,
|
|
26
|
+
} from "./session-callbacks";
|
|
27
|
+
import { SESSION_REVOKED_EVENT_SHORT, sessionRevokedSchema } from "./session-revoked-event";
|
|
11
28
|
|
|
12
29
|
export type SessionsFeatureOptions = {
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
30
|
+
// A successful update on the `user` entity that changes the `passwordHash`
|
|
31
|
+
// column triggers a mass-revoke of every live session for that user.
|
|
32
|
+
// Industry-standard "password-change signs you out everywhere" flow,
|
|
33
|
+
// including the session that did the change itself — the client has
|
|
17
34
|
// to re-login after a password change.
|
|
18
35
|
//
|
|
19
36
|
// Runs as an afterCommit postSave hook: the password-change commits first,
|
|
20
37
|
// then the sessions are revoked. Best-effort — if the mass-revoker throws,
|
|
21
38
|
// the password change is NOT rolled back (a password change with a stale
|
|
22
39
|
// session still wins over a user-visible error on the change itself).
|
|
40
|
+
//
|
|
41
|
+
// Default: run{Prod,Dev}App bind their own sessionMassRevoker via
|
|
42
|
+
// `bindAutoRevokeOnPasswordChange` (secure-by-default). Set this option
|
|
43
|
+
// only to supply a custom revoker — an explicit value wins over the
|
|
44
|
+
// runtime binding.
|
|
23
45
|
readonly autoRevokeOnPasswordChange?: SessionMassRevoker;
|
|
46
|
+
// Session JWT / store_user_sessions TTL. Was previously auth.sessions.expiresInMs
|
|
47
|
+
// (#1372) — lives on the sessions feature now that sessionStore is the wiring.
|
|
48
|
+
readonly expiresInMs?: number;
|
|
24
49
|
};
|
|
25
50
|
|
|
26
|
-
|
|
51
|
+
export type BindAutoRevokeOnPasswordChange = (revoker: SessionMassRevoker) => void;
|
|
52
|
+
export type BindRevokeAllOtherSessions = (revoker: SessionAllOthersRevoker) => void;
|
|
53
|
+
|
|
54
|
+
// Reads the late-bind setter off a mounted sessions feature's exports.
|
|
55
|
+
// run{Prod,Dev}App call it once the DB connection is concrete — the feature
|
|
56
|
+
// itself is constructed in app run-config long before a db exists, so the
|
|
57
|
+
// revoker can't be a constructor argument.
|
|
58
|
+
export function bindAutoRevokeFromFeature(
|
|
59
|
+
feature: FeatureDefinition,
|
|
60
|
+
): BindAutoRevokeOnPasswordChange | undefined {
|
|
61
|
+
const exports = feature.exports;
|
|
62
|
+
if (exports && typeof exports === "object" && "bindAutoRevokeOnPasswordChange" in exports) {
|
|
63
|
+
const { bindAutoRevokeOnPasswordChange } = exports as {
|
|
64
|
+
bindAutoRevokeOnPasswordChange: unknown;
|
|
65
|
+
};
|
|
66
|
+
if (typeof bindAutoRevokeOnPasswordChange === "function") {
|
|
67
|
+
// @cast-boundary exports-walk — feature.exports is untyped by design
|
|
68
|
+
return bindAutoRevokeOnPasswordChange as BindAutoRevokeOnPasswordChange;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// The sessions feature registers the store_user_sessions table (as an
|
|
27
75
|
// unmanaged direct-write store, NOT an r.entity — see below) and the three
|
|
28
76
|
// user-facing handlers (mine/revoke/revoke-all-others). It intentionally does NOT
|
|
29
77
|
// export a sessionCreator/sessionRevoker here — those are produced by
|
|
@@ -41,7 +89,7 @@ export type SessionsFeatureOptions = {
|
|
|
41
89
|
export function createSessionsFeature(options?: SessionsFeatureOptions): FeatureDefinition {
|
|
42
90
|
return defineFeature("sessions", (r) => {
|
|
43
91
|
r.describe(
|
|
44
|
-
"Tracks signed-in clients in the `
|
|
92
|
+
"Tracks signed-in clients in the `store_user_sessions` table (one row per JWT, keyed by the `sid`/`jti` claim) and exposes handlers for `mine` (list your sessions), `revoke`, and `revokeAllOthers`. Session creation and revocation on the hot auth path are handled by `createSessionCallbacks()`, wired into `buildServer({ auth: { ... } })` outside the dispatcher; the same callbacks are also registered as an auth-foundation sessionStore provider, resolvable generically via `resolveSessionStore()`. The feature also ships a manual-trigger cleanup job for pruning expired rows and an optional `autoRevokeOnPasswordChange` hook that mass-revokes all sessions for a user whenever their `passwordHash` changes.",
|
|
45
93
|
);
|
|
46
94
|
r.uiHints({
|
|
47
95
|
displayLabel: "Sessions · Server-side Logout",
|
|
@@ -51,20 +99,56 @@ export function createSessionsFeature(options?: SessionsFeatureOptions): Feature
|
|
|
51
99
|
// sessionChecker reads read_users on every authenticated request (status
|
|
52
100
|
// gate for locked accounts) — make that a boot-time dependency so a
|
|
53
101
|
// sessions-without-user wiring fails validateBoot instead of 500ing live.
|
|
54
|
-
|
|
55
|
-
//
|
|
102
|
+
// auth-foundation owns EXT_SESSION_STORE, which the useExtension below
|
|
103
|
+
// registers against.
|
|
104
|
+
r.requires("user", "auth-foundation");
|
|
105
|
+
// store_user_sessions is a hot-path direct-write store: sessionCreator
|
|
56
106
|
// inserts and the revoke handlers update rows WITHOUT emitting lifecycle
|
|
57
107
|
// events (the row columns ARE the audit trail). Registering it as
|
|
58
108
|
// r.entity would make it a rebuildable implicit projection whose replay
|
|
59
109
|
// finds zero session events and swaps an empty shadow over the live
|
|
60
110
|
// table — wiping every active session on the next projection rebuild
|
|
61
|
-
// (#498/#494). r.
|
|
111
|
+
// (#498/#494). r.storeTable keeps the migration DDL but opts the
|
|
62
112
|
// table out of implicit rebuild, like jobs/channel-in-app/feature-toggles
|
|
63
113
|
// which are direct-write stores too.
|
|
64
|
-
r.
|
|
114
|
+
r.storeTable(buildEntityTableMeta("user-session", userSessionEntity, { source: "unmanaged" }), {
|
|
65
115
|
reason: "read_side.user_sessions_direct_write",
|
|
116
|
+
// sessionCreator encrypts ip/userAgent via encryptForDirectWrite (#820).
|
|
117
|
+
piiEncryptedOnWrite: true,
|
|
66
118
|
});
|
|
67
119
|
|
|
120
|
+
// Self-registers as auth-foundation's sessionStore provider (#1371) —
|
|
121
|
+
// wraps the same createSessionCallbacks() used by the manual
|
|
122
|
+
// buildServer({ auth: { ... } }) wiring above; a future issue (#1372)
|
|
123
|
+
// removes that manual wiring once the middleware resolves generically
|
|
124
|
+
// via resolveSessionStore().
|
|
125
|
+
r.useExtension(EXT_SESSION_STORE, "default", {
|
|
126
|
+
build: (deps): SessionStore => {
|
|
127
|
+
const callbacks = createSessionCallbacks({
|
|
128
|
+
db: deps.db,
|
|
129
|
+
...(options?.expiresInMs !== undefined && { expiresInMs: options.expiresInMs }),
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
creator: callbacks.sessionCreator,
|
|
133
|
+
revoker: callbacks.sessionRevoker,
|
|
134
|
+
checker: callbacks.sessionChecker,
|
|
135
|
+
massRevoker: callbacks.sessionMassRevoker,
|
|
136
|
+
revokeAllOthers: callbacks.sessionRevokeAllOthers,
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
} satisfies SessionStoreProvider);
|
|
140
|
+
|
|
141
|
+
// Custom domain-event for cross-instance access-invalidation (#1559).
|
|
142
|
+
// r.defineEvent registers the schema so ctx.unsafeAppendEvent (revoke,
|
|
143
|
+
// revoke-all-others) enforces it at append time. revoke-all-for-user
|
|
144
|
+
// appends via the low-level append() instead (needs to anchor on
|
|
145
|
+
// SYSTEM_TENANT_ID — see that handler for why) — append() does NOT
|
|
146
|
+
// consult the registry, so that callsite parses against
|
|
147
|
+
// sessionRevokedSchema explicitly, same guarantee via a different path.
|
|
148
|
+
// No projection: the payload IS the read, consumed directly off the
|
|
149
|
+
// event-store NOTIFY (#1560).
|
|
150
|
+
r.defineEvent(SESSION_REVOKED_EVENT_SHORT, sessionRevokedSchema);
|
|
151
|
+
|
|
68
152
|
const handlers = {
|
|
69
153
|
revoke: r.writeHandler(revokeWrite),
|
|
70
154
|
revokeAllOthers: r.writeHandler(revokeAllOthersWrite),
|
|
@@ -75,6 +159,7 @@ export function createSessionsFeature(options?: SessionsFeatureOptions): Feature
|
|
|
75
159
|
const queries = {
|
|
76
160
|
mine: r.queryHandler(mineQuery),
|
|
77
161
|
list: r.queryHandler(listQuery),
|
|
162
|
+
detail: r.queryHandler(detailQuery),
|
|
78
163
|
};
|
|
79
164
|
|
|
80
165
|
// Retention: chunked DELETE of expired/revoked rows. Manual trigger
|
|
@@ -93,20 +178,84 @@ export function createSessionsFeature(options?: SessionsFeatureOptions): Feature
|
|
|
93
178
|
// "the handler didn't touch this column", which is exactly the signal
|
|
94
179
|
// we want to skip on. Works for both direct user:update calls and any
|
|
95
180
|
// other handler that happens to write the column.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
181
|
+
let autoRevoke = options?.autoRevokeOnPasswordChange;
|
|
182
|
+
r.hook("postSave", { allOf: "user" }, async (ctx) => {
|
|
183
|
+
// skip: nothing bound — stateless-JWT deployments without a runtime
|
|
184
|
+
// that calls bindAutoRevokeOnPasswordChange keep the old behavior.
|
|
185
|
+
if (!autoRevoke) return;
|
|
186
|
+
// skip: brand-new user, no sessions can possibly exist yet. The
|
|
187
|
+
// initial passwordHash on a user:create would trip the second guard
|
|
188
|
+
// otherwise — every registration would do a mass-revoke roundtrip
|
|
189
|
+
// for a user who literally has no rows in user_sessions.
|
|
190
|
+
if (ctx.isNew) return;
|
|
191
|
+
// skip: handler didn't touch passwordHash, nothing to revoke
|
|
192
|
+
if (ctx.changes["passwordHash"] === undefined) return;
|
|
193
|
+
await autoRevoke(String(ctx.id));
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const bindAutoRevokeOnPasswordChange: BindAutoRevokeOnPasswordChange = (revoker) => {
|
|
197
|
+
// explicit constructor option wins over the runtime binding
|
|
198
|
+
autoRevoke ??= revoker;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
r.translations({ keys: SESSIONS_I18N });
|
|
202
|
+
|
|
203
|
+
const listAccess = { roles: access.admin };
|
|
204
|
+
|
|
205
|
+
r.screen({
|
|
206
|
+
id: SESSION_LIST_SCREEN_ID,
|
|
207
|
+
type: "projectionList",
|
|
208
|
+
query: SessionQueries.list,
|
|
209
|
+
columns: [
|
|
210
|
+
{ field: "id", label: "sessions.list.col.id" },
|
|
211
|
+
{ field: "userId", label: "sessions.list.col.userId" },
|
|
212
|
+
{ field: "createdAt", label: "sessions.list.col.createdAt" },
|
|
213
|
+
{ field: "expiresAt", label: "sessions.list.col.expiresAt" },
|
|
214
|
+
{ field: "revokedAt", label: "sessions.list.col.revokedAt" },
|
|
215
|
+
],
|
|
216
|
+
rowActions: [
|
|
217
|
+
{
|
|
218
|
+
kind: "navigate",
|
|
219
|
+
id: "open",
|
|
220
|
+
label: "sessions.list.action.open",
|
|
221
|
+
screen: SESSION_DETAIL_SCREEN_ID,
|
|
222
|
+
entityId: "id",
|
|
223
|
+
rowClick: true,
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
access: listAccess,
|
|
227
|
+
});
|
|
228
|
+
r.screen({
|
|
229
|
+
id: SESSION_DETAIL_SCREEN_ID,
|
|
230
|
+
type: "projectionDetail",
|
|
231
|
+
query: SessionQueries.detail,
|
|
232
|
+
listScreenId: SESSION_LIST_SCREEN_ID,
|
|
233
|
+
layout: {
|
|
234
|
+
sections: [
|
|
235
|
+
{
|
|
236
|
+
fields: ["id", "userId", "createdAt", "expiresAt", "revokedAt", "ip", "userAgent"],
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
fieldLabels: {
|
|
241
|
+
id: "sessions.detail.field.id",
|
|
242
|
+
userId: "sessions.detail.field.userId",
|
|
243
|
+
createdAt: "sessions.detail.field.createdAt",
|
|
244
|
+
expiresAt: "sessions.detail.field.expiresAt",
|
|
245
|
+
revokedAt: "sessions.detail.field.revokedAt",
|
|
246
|
+
ip: "sessions.detail.field.ip",
|
|
247
|
+
userAgent: "sessions.detail.field.userAgent",
|
|
248
|
+
},
|
|
249
|
+
access: listAccess,
|
|
250
|
+
});
|
|
251
|
+
r.nav({
|
|
252
|
+
id: "session-list",
|
|
253
|
+
label: "sessions:nav.sessionList",
|
|
254
|
+
icon: "list",
|
|
255
|
+
screen: "sessions:screen:session-list",
|
|
256
|
+
order: 10,
|
|
257
|
+
});
|
|
109
258
|
|
|
110
|
-
return { handlers, queries };
|
|
259
|
+
return { handlers, queries, bindAutoRevokeOnPasswordChange };
|
|
111
260
|
});
|
|
112
261
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
+
import { access, defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { decryptStoredPii } from "../../shared";
|
|
5
|
+
import { userSessionTable } from "../schema/user-session";
|
|
6
|
+
|
|
7
|
+
// Admin single-session inspector — mirrors list.query's decrypt handling for
|
|
8
|
+
// the one-row case. ctx.db (TenantDb) applies tenant-scoping automatically.
|
|
9
|
+
export const detailQuery = defineQueryHandler({
|
|
10
|
+
name: "user-session:detail",
|
|
11
|
+
schema: z.object({ id: z.uuid() }),
|
|
12
|
+
access: { roles: access.admin },
|
|
13
|
+
handler: async (query, ctx) => {
|
|
14
|
+
const row = await fetchOne<{
|
|
15
|
+
id: string;
|
|
16
|
+
userId: string;
|
|
17
|
+
createdAt: unknown;
|
|
18
|
+
expiresAt: unknown;
|
|
19
|
+
revokedAt: unknown;
|
|
20
|
+
ip: string | null;
|
|
21
|
+
userAgent: string | null;
|
|
22
|
+
}>(ctx.db, userSessionTable, { id: query.payload.id });
|
|
23
|
+
if (!row) return null;
|
|
24
|
+
return {
|
|
25
|
+
id: row.id,
|
|
26
|
+
userId: row.userId,
|
|
27
|
+
createdAt: row.createdAt,
|
|
28
|
+
expiresAt: row.expiresAt,
|
|
29
|
+
revokedAt: row.revokedAt,
|
|
30
|
+
ip: row.ip ? await decryptStoredPii(row.ip, "ip", "sessions:detail") : row.ip,
|
|
31
|
+
userAgent: row.userAgent
|
|
32
|
+
? await decryptStoredPii(row.userAgent, "userAgent", "sessions:detail")
|
|
33
|
+
: row.userAgent,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { access, defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { decryptStoredPii } from "../../shared";
|
|
4
5
|
import { userSessionTable } from "../schema/user-session";
|
|
5
6
|
|
|
6
7
|
// Admin view of every session in the active tenant. ctx.db (TenantDb)
|
|
@@ -22,14 +23,18 @@ export const listQuery = defineQueryHandler({
|
|
|
22
23
|
}>(ctx.db, userSessionTable, undefined, {
|
|
23
24
|
orderBy: { col: "createdAt", direction: "desc" },
|
|
24
25
|
});
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
return Promise.all(
|
|
27
|
+
rows.map(async (r) => ({
|
|
28
|
+
id: r.id,
|
|
29
|
+
userId: r.userId,
|
|
30
|
+
createdAt: r.createdAt,
|
|
31
|
+
expiresAt: r.expiresAt,
|
|
32
|
+
revokedAt: r.revokedAt,
|
|
33
|
+
ip: r.ip ? await decryptStoredPii(r.ip, "ip", "sessions:list") : r.ip,
|
|
34
|
+
userAgent: r.userAgent
|
|
35
|
+
? await decryptStoredPii(r.userAgent, "userAgent", "sessions:list")
|
|
36
|
+
: r.userAgent,
|
|
37
|
+
})),
|
|
38
|
+
);
|
|
34
39
|
},
|
|
35
40
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { decryptStoredPii } from "../../shared";
|
|
4
5
|
import { userSessionTable } from "../schema/user-session";
|
|
5
6
|
|
|
6
7
|
// "My live sessions" — the backing data for a devices/sessions UI. Returns
|
|
@@ -26,13 +27,17 @@ export const mineQuery = defineQueryHandler({
|
|
|
26
27
|
},
|
|
27
28
|
);
|
|
28
29
|
const currentSid = query.user.sid;
|
|
29
|
-
return
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
return Promise.all(
|
|
31
|
+
rows.map(async (r) => ({
|
|
32
|
+
id: r.id,
|
|
33
|
+
createdAt: r.createdAt,
|
|
34
|
+
expiresAt: r.expiresAt,
|
|
35
|
+
ip: r.ip ? await decryptStoredPii(r.ip, "ip", "sessions:mine") : r.ip,
|
|
36
|
+
userAgent: r.userAgent
|
|
37
|
+
? await decryptStoredPii(r.userAgent, "userAgent", "sessions:mine")
|
|
38
|
+
: r.userAgent,
|
|
39
|
+
current: currentSid === r.id,
|
|
40
|
+
})),
|
|
41
|
+
);
|
|
37
42
|
},
|
|
38
43
|
});
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import { updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
-
import { access, defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { access, defineWriteHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { append } from "@cosmicdrift/kumiko-framework/event-store";
|
|
4
|
+
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
3
5
|
import { Temporal } from "temporal-polyfill";
|
|
4
6
|
import { z } from "zod";
|
|
5
7
|
import { userSessionTable } from "../schema/user-session";
|
|
8
|
+
import {
|
|
9
|
+
SESSION_REVOKED_AGGREGATE_TYPE,
|
|
10
|
+
SESSION_REVOKED_EVENT_QN,
|
|
11
|
+
sessionRevokedSchema,
|
|
12
|
+
} from "../session-revoked-event";
|
|
6
13
|
|
|
7
14
|
// Mass-revoke ALL live sessions for a target user — privileged-only.
|
|
8
|
-
// Used by user-data-rights:restrict-account
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
15
|
+
// Used by user-data-rights:restrict-account for account-freeze (DSGVO
|
|
16
|
+
// Art. 18) and potentially other ops flows ("ban user", "compromised
|
|
17
|
+
// account"). Unlike revoke-all-others, the caller's own session (if any)
|
|
18
|
+
// is revoked too — the caller is System (cron/operator/cross-feature),
|
|
19
|
+
// not the end-user themselves.
|
|
13
20
|
//
|
|
14
|
-
// Tenant-scope:
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
21
|
+
// Tenant-scope: the userSession schema persists tenantId per row (a user
|
|
22
|
+
// can have sessions in multiple tenants). We revoke cross-tenant because
|
|
23
|
+
// "account restriction" is a global user-level statement (the forget path
|
|
24
|
+
// is global too, see the User-Entity special doc). The UPDATE filters
|
|
25
|
+
// only on userId.
|
|
19
26
|
export const revokeAllForUserWrite = defineWriteHandler({
|
|
20
27
|
name: "user-session:revoke-all-for-user",
|
|
21
28
|
schema: z.object({
|
|
@@ -23,13 +30,45 @@ export const revokeAllForUserWrite = defineWriteHandler({
|
|
|
23
30
|
}),
|
|
24
31
|
access: { roles: access.privileged },
|
|
25
32
|
handler: async (event, ctx) => {
|
|
26
|
-
const updated = await updateMany(
|
|
33
|
+
const updated = await updateMany<{ id: string }>(
|
|
27
34
|
ctx.db.raw,
|
|
28
35
|
userSessionTable,
|
|
29
36
|
{ revokedAt: Temporal.Now.instant() },
|
|
30
37
|
{ userId: event.payload.userId, revokedAt: null },
|
|
31
38
|
);
|
|
32
39
|
|
|
40
|
+
// Lightweight append alongside the direct-write above (#1559) — see
|
|
41
|
+
// revoke.write.ts for why this isn't a lifecycle event on
|
|
42
|
+
// store_user_sessions. Uses the low-level append() (not
|
|
43
|
+
// ctx.unsafeAppendEvent) because this handler is deliberately
|
|
44
|
+
// cross-tenant: the privileged caller's own tenantId (what
|
|
45
|
+
// ctx.unsafeAppendEvent would bind to) has no relationship to the
|
|
46
|
+
// target user's session tenant(s). Anchored on SYSTEM_TENANT_ID
|
|
47
|
+
// instead, same as the jobs feature's cross-tenant run-lifecycle
|
|
48
|
+
// events (job-run-logger.ts) — a fresh aggregate per call, so no
|
|
49
|
+
// predecessor/version_conflict concerns.
|
|
50
|
+
//
|
|
51
|
+
// append() itself does NOT validate against the r.defineEvent-
|
|
52
|
+
// registered schema (that guarantee only comes from going through
|
|
53
|
+
// ctx.unsafeAppendEvent's dispatcher path). Mirrors job-run-logger.ts:
|
|
54
|
+
// parse explicitly before the raw append so a shape drift fails loudly
|
|
55
|
+
// here instead of landing unvalidated on the events table.
|
|
56
|
+
if (updated.length > 0) {
|
|
57
|
+
const payload = sessionRevokedSchema.parse({
|
|
58
|
+
userId: event.payload.userId,
|
|
59
|
+
sessionIds: updated.map((row) => row.id),
|
|
60
|
+
});
|
|
61
|
+
await append(ctx.db.raw, {
|
|
62
|
+
aggregateId: generateId(),
|
|
63
|
+
aggregateType: SESSION_REVOKED_AGGREGATE_TYPE,
|
|
64
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
65
|
+
expectedVersion: 0,
|
|
66
|
+
type: SESSION_REVOKED_EVENT_QN,
|
|
67
|
+
payload,
|
|
68
|
+
metadata: { userId: event.user.id },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
33
72
|
return {
|
|
34
73
|
isSuccess: true as const,
|
|
35
74
|
data: { count: updated.length, userId: event.payload.userId },
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
|
+
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
4
5
|
import { Temporal } from "temporal-polyfill";
|
|
5
6
|
import { z } from "zod";
|
|
6
7
|
import { SessionErrors } from "../constants";
|
|
7
8
|
import { userSessionTable } from "../schema/user-session";
|
|
9
|
+
import { SESSION_REVOKED_AGGREGATE_TYPE, SESSION_REVOKED_EVENT_QN } from "../session-revoked-event";
|
|
8
10
|
|
|
9
11
|
// "Sign out everywhere else" — keep the caller's current session, kill all
|
|
10
12
|
// other live sessions for this user. Requires `user.sid` so "keep current"
|
|
@@ -25,13 +27,29 @@ export const revokeAllOthersWrite = defineWriteHandler({
|
|
|
25
27
|
);
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
const updated = await updateMany(
|
|
30
|
+
const updated = await updateMany<{ id: string }>(
|
|
29
31
|
ctx.db,
|
|
30
32
|
userSessionTable,
|
|
31
33
|
{ revokedAt: Temporal.Now.instant() },
|
|
32
34
|
{ userId: event.user.id, revokedAt: null, id: { ne: keepSid } },
|
|
33
35
|
);
|
|
34
36
|
|
|
37
|
+
// Lightweight append alongside the direct-write above (#1559) — see
|
|
38
|
+
// revoke.write.ts for why this isn't a lifecycle event on
|
|
39
|
+
// store_user_sessions. Skipped when nothing was revoked (no other
|
|
40
|
+
// live sessions) — a spurious invalidation would be the failure mode.
|
|
41
|
+
if (updated.length > 0) {
|
|
42
|
+
await ctx.unsafeAppendEvent({
|
|
43
|
+
aggregateId: generateId(),
|
|
44
|
+
aggregateType: SESSION_REVOKED_AGGREGATE_TYPE,
|
|
45
|
+
type: SESSION_REVOKED_EVENT_QN,
|
|
46
|
+
payload: {
|
|
47
|
+
userId: event.user.id,
|
|
48
|
+
sessionIds: updated.map((row) => row.id),
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
35
53
|
return { isSuccess: true, data: { count: updated.length } };
|
|
36
54
|
},
|
|
37
55
|
});
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { fetchOne, updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
|
+
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
4
5
|
import { Temporal } from "temporal-polyfill";
|
|
5
6
|
import { z } from "zod";
|
|
6
7
|
import { SessionErrors } from "../constants";
|
|
7
8
|
import { userSessionTable } from "../schema/user-session";
|
|
9
|
+
import { SESSION_REVOKED_AGGREGATE_TYPE, SESSION_REVOKED_EVENT_QN } from "../session-revoked-event";
|
|
8
10
|
|
|
9
11
|
// Revoke a single session by id (= JWT jti). Three distinguishable outcomes:
|
|
10
12
|
//
|
|
@@ -36,6 +38,17 @@ export const revokeWrite = defineWriteHandler({
|
|
|
36
38
|
);
|
|
37
39
|
|
|
38
40
|
if (updated.length > 0) {
|
|
41
|
+
// Lightweight append alongside the direct-write above (#1559) — NOT a
|
|
42
|
+
// lifecycle event for store_user_sessions (that table stays an
|
|
43
|
+
// unmanaged direct-write store, see feature.ts). Own aggregate id per
|
|
44
|
+
// revoke, no predecessor to satisfy, no version_conflict against a
|
|
45
|
+
// concurrent revoke on a different sid.
|
|
46
|
+
await ctx.unsafeAppendEvent({
|
|
47
|
+
aggregateId: generateId(),
|
|
48
|
+
aggregateType: SESSION_REVOKED_AGGREGATE_TYPE,
|
|
49
|
+
type: SESSION_REVOKED_EVENT_QN,
|
|
50
|
+
payload: { userId: event.user.id, sessionIds: [event.payload.id] },
|
|
51
|
+
});
|
|
39
52
|
return { isSuccess: true, data: { id: event.payload.id } };
|
|
40
53
|
}
|
|
41
54
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// @runtime client
|
|
2
|
+
// Server + client i18n for the sessions operator screens.
|
|
3
|
+
|
|
4
|
+
type LocalizedString = { readonly de: string; readonly en: string };
|
|
5
|
+
|
|
6
|
+
export const SESSIONS_I18N: Readonly<Record<string, LocalizedString>> = {
|
|
7
|
+
"screen:session-list.title": { de: "Sitzungen", en: "Sessions" },
|
|
8
|
+
"screen:session-detail.title": { de: "Sitzung", en: "Session" },
|
|
9
|
+
"sessions:nav.sessionList": { de: "Sitzungen", en: "Sessions" },
|
|
10
|
+
"sessions.list.col.id": { de: "ID", en: "ID" },
|
|
11
|
+
"sessions.list.col.userId": { de: "User-ID", en: "User ID" },
|
|
12
|
+
"sessions.list.col.createdAt": { de: "Erstellt", en: "Created" },
|
|
13
|
+
"sessions.list.col.expiresAt": { de: "Läuft ab", en: "Expires" },
|
|
14
|
+
"sessions.list.col.revokedAt": { de: "Widerrufen", en: "Revoked" },
|
|
15
|
+
"sessions.list.action.open": { de: "Details", en: "Details" },
|
|
16
|
+
"sessions.detail.field.id": { de: "ID", en: "ID" },
|
|
17
|
+
"sessions.detail.field.userId": { de: "User-ID", en: "User ID" },
|
|
18
|
+
"sessions.detail.field.createdAt": { de: "Erstellt", en: "Created" },
|
|
19
|
+
"sessions.detail.field.expiresAt": { de: "Läuft ab", en: "Expires" },
|
|
20
|
+
"sessions.detail.field.revokedAt": { de: "Widerrufen", en: "Revoked" },
|
|
21
|
+
"sessions.detail.field.ip": { de: "IP-Adresse", en: "IP address" },
|
|
22
|
+
"sessions.detail.field.userAgent": { de: "User-Agent", en: "User agent" },
|
|
23
|
+
};
|
package/src/sessions/index.ts
CHANGED
|
@@ -6,8 +6,8 @@ export {
|
|
|
6
6
|
SessionHandlers,
|
|
7
7
|
SessionQueries,
|
|
8
8
|
} from "./constants";
|
|
9
|
-
export type { SessionsFeatureOptions } from "./feature";
|
|
10
|
-
export { createSessionsFeature } from "./feature";
|
|
9
|
+
export type { BindAutoRevokeOnPasswordChange, SessionsFeatureOptions } from "./feature";
|
|
10
|
+
export { bindAutoRevokeFromFeature, createSessionsFeature } from "./feature";
|
|
11
11
|
export { userSessionEntity, userSessionTable } from "./schema/user-session";
|
|
12
12
|
export type {
|
|
13
13
|
SessionCallbacks,
|
|
@@ -15,3 +15,10 @@ export type {
|
|
|
15
15
|
SessionMassRevoker,
|
|
16
16
|
} from "./session-callbacks";
|
|
17
17
|
export { createSessionCallbacks } from "./session-callbacks";
|
|
18
|
+
export type { SessionRevokedPayload } from "./session-revoked-event";
|
|
19
|
+
export {
|
|
20
|
+
SESSION_REVOKED_AGGREGATE_TYPE,
|
|
21
|
+
SESSION_REVOKED_EVENT_QN,
|
|
22
|
+
SESSION_REVOKED_EVENT_SHORT,
|
|
23
|
+
sessionRevokedSchema,
|
|
24
|
+
} from "./session-revoked-event";
|
|
@@ -20,7 +20,7 @@ export const userSessionEntity = createEntity({
|
|
|
20
20
|
// Entity-Key ist "user-session" (mit Dash), toTableName's snake-case-
|
|
21
21
|
// Transform käme auf "read_user-sessions" → kein valides SQL ohne Quoting.
|
|
22
22
|
// Deshalb expliziter Override auf den read_-konformen Namen.
|
|
23
|
-
table: "
|
|
23
|
+
table: "store_user_sessions",
|
|
24
24
|
// sid-as-PK: the sessionCreator callback generates the UUID, returns it to
|
|
25
25
|
// the framework; the framework stamps it as `jti`; here the same value is
|
|
26
26
|
// the row primary key. Single source of truth for the identifier.
|
|
@@ -53,13 +53,17 @@ export const userSessionEntity = createEntity({
|
|
|
53
53
|
revokedAt: createTimestampField({
|
|
54
54
|
access: { write: access.privileged },
|
|
55
55
|
}),
|
|
56
|
+
// Subject is the session's user, not the session row — a user-forget
|
|
57
|
+
// must shred these, so the key hangs off userId (sid-self would orphan).
|
|
56
58
|
ip: createTextField({
|
|
57
59
|
maxLength: 64,
|
|
58
60
|
access: { write: access.privileged },
|
|
61
|
+
userOwned: { ownerField: "userId" },
|
|
59
62
|
}),
|
|
60
63
|
userAgent: createTextField({
|
|
61
64
|
maxLength: 512,
|
|
62
65
|
access: { write: access.privileged },
|
|
66
|
+
userOwned: { ownerField: "userId" },
|
|
63
67
|
}),
|
|
64
68
|
},
|
|
65
69
|
});
|
|
@@ -70,4 +74,6 @@ export const userSessionEntity = createEntity({
|
|
|
70
74
|
// handlers write it directly via ctx.db; the meta carries no executor-only
|
|
71
75
|
// brand so those writes stay legal. See feature.ts for the rebuild-exclusion
|
|
72
76
|
// rationale (#494/#498).
|
|
73
|
-
export const userSessionTable = buildEntityTableMeta("user-session", userSessionEntity
|
|
77
|
+
export const userSessionTable = buildEntityTableMeta("user-session", userSessionEntity, {
|
|
78
|
+
source: "unmanaged",
|
|
79
|
+
});
|