@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
|
@@ -1,24 +1,33 @@
|
|
|
1
1
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
-
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
-
import {
|
|
2
|
+
import { access, defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import {
|
|
4
|
+
AccessDeniedError,
|
|
5
|
+
UnprocessableError,
|
|
6
|
+
writeFailure,
|
|
7
|
+
} from "@cosmicdrift/kumiko-framework/errors";
|
|
4
8
|
import { z } from "zod";
|
|
9
|
+
import { tenantMembershipsTable } from "../../tenant";
|
|
5
10
|
import { USER_STATUS, userTable } from "../../user";
|
|
11
|
+
import { isSystemAdminActor } from "../lib/is-admin-actor";
|
|
6
12
|
import { updateUserLifecycle } from "../lib/update-user-lifecycle";
|
|
7
13
|
|
|
8
14
|
// POST /api/user/lift-restriction (S2.U6) — DSGVO Art. 18 Reverse.
|
|
9
15
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// login
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// access-Konfig oder Custom-Wrapper.
|
|
16
|
+
// Operator-only. A Restricted user's own session is unconditionally
|
|
17
|
+
// rejected by sessionChecker (BLOCKED_STATUSES) the moment their status
|
|
18
|
+
// flips — their JWT/login can't reach this or any other authenticated
|
|
19
|
+
// endpoint. There is no self-service path today, so lifting a restriction
|
|
20
|
+
// always targets someone else's account by id.
|
|
16
21
|
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
+
// Tenant scope: `access.admin` includes TenantAdmin, which is tenant-
|
|
23
|
+
// scoped even though the User-entity lookup below uses `ctx.db.raw`
|
|
24
|
+
// (bypasses the auto-tenant-filter, same as restrict-account.write.ts —
|
|
25
|
+
// User status is intentionally global, see user-data-rights.md
|
|
26
|
+
// "Cross-Tenant-Semantik"). Without a membership check, a TenantAdmin
|
|
27
|
+
// from tenant A could unrestrict/reactivate a user who has never been a
|
|
28
|
+
// member of tenant A. Only SystemAdmin (platform-wide) skips the check;
|
|
29
|
+
// TenantAdmin/Admin must have an active membership in the target's own
|
|
30
|
+
// tenantId.
|
|
22
31
|
//
|
|
23
32
|
// State-Transitions:
|
|
24
33
|
// Restricted → Active ✓
|
|
@@ -27,17 +36,33 @@ import { updateUserLifecycle } from "../lib/update-user-lifecycle";
|
|
|
27
36
|
// Deleted → ... ✗ 422 not_restricted
|
|
28
37
|
export const liftRestrictionWrite = defineWriteHandler({
|
|
29
38
|
name: "lift-restriction",
|
|
30
|
-
schema: z.object({}),
|
|
31
|
-
access: {
|
|
39
|
+
schema: z.object({ userId: z.string().uuid() }),
|
|
40
|
+
access: { roles: access.admin },
|
|
32
41
|
handler: async (event, ctx) => {
|
|
42
|
+
const targetUserId = event.payload.userId;
|
|
43
|
+
|
|
44
|
+
if (!isSystemAdminActor(event.user)) {
|
|
45
|
+
const membership = await fetchOne(ctx.db.raw, tenantMembershipsTable, {
|
|
46
|
+
userId: targetUserId,
|
|
47
|
+
tenantId: event.user.tenantId,
|
|
48
|
+
});
|
|
49
|
+
if (!membership) {
|
|
50
|
+
return writeFailure(
|
|
51
|
+
new AccessDeniedError({
|
|
52
|
+
details: { reason: "target_user_not_in_admin_tenant" },
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
33
58
|
const userRow = await fetchOne<{ status: string }>(ctx.db.raw, userTable, {
|
|
34
|
-
id:
|
|
59
|
+
id: targetUserId,
|
|
35
60
|
});
|
|
36
61
|
|
|
37
62
|
if (!userRow) {
|
|
38
63
|
return writeFailure(
|
|
39
64
|
new UnprocessableError("user_not_found", {
|
|
40
|
-
details: { reason: "user_not_found", userId:
|
|
65
|
+
details: { reason: "user_not_found", userId: targetUserId },
|
|
41
66
|
}),
|
|
42
67
|
);
|
|
43
68
|
}
|
|
@@ -51,12 +76,12 @@ export const liftRestrictionWrite = defineWriteHandler({
|
|
|
51
76
|
);
|
|
52
77
|
}
|
|
53
78
|
|
|
54
|
-
await updateUserLifecycle(ctx.db.raw,
|
|
79
|
+
await updateUserLifecycle(ctx.db.raw, targetUserId, { status: USER_STATUS.Active });
|
|
55
80
|
|
|
56
81
|
return {
|
|
57
82
|
isSuccess: true as const,
|
|
58
83
|
data: {
|
|
59
|
-
userId:
|
|
84
|
+
userId: targetUserId,
|
|
60
85
|
status: USER_STATUS.Active,
|
|
61
86
|
},
|
|
62
87
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { selectMany, type WhereObject } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { Temporal } from "temporal-polyfill";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { downloadAttemptsTable } from "../schema/download-attempt";
|
|
5
6
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { selectMany, type WhereObject } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
4
|
+
import { Temporal } from "temporal-polyfill";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
|
|
6
7
|
// DSGVO Art. 15 Selbstauskunft — User reads HIS OWN audit-log.
|
|
@@ -95,7 +95,9 @@ export function createRequestDeletionByEmailHandler(opts: RequestDeletionByEmail
|
|
|
95
95
|
if (opts.sendDeletionVerificationEmail) {
|
|
96
96
|
try {
|
|
97
97
|
await opts.sendDeletionVerificationEmail({
|
|
98
|
-
|
|
98
|
+
// Row value is ciphertext with an active KMS; the lookup above
|
|
99
|
+
// only matches byte-exact, so the payload IS the stored plaintext.
|
|
100
|
+
email: event.payload.email,
|
|
99
101
|
verifyUrl,
|
|
100
102
|
expiresAt: expiresAt.toString(),
|
|
101
103
|
});
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import { createSystemUser, defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
AccessDeniedError,
|
|
5
|
+
UnprocessableError,
|
|
6
|
+
writeFailure,
|
|
7
|
+
} from "@cosmicdrift/kumiko-framework/errors";
|
|
4
8
|
import { z } from "zod";
|
|
9
|
+
import { tenantMembershipsTable } from "../../tenant";
|
|
5
10
|
import { USER_STATUS, userTable } from "../../user";
|
|
11
|
+
import { isAdminActor, isSystemAdminActor } from "../lib/is-admin-actor";
|
|
6
12
|
import { updateUserLifecycle } from "../lib/update-user-lifecycle";
|
|
7
13
|
|
|
8
14
|
// POST /api/user/restrict (S2.U6) — DSGVO Art. 18 Account-Freeze.
|
|
@@ -15,6 +21,22 @@ import { updateUserLifecycle } from "../lib/update-user-lifecycle";
|
|
|
15
21
|
// muss gemountet sein). App-Author ohne sessions-feature kriegt einen
|
|
16
22
|
// Boot-Resolver-Error via r.usesApi("sessions.revokeAllForUser").
|
|
17
23
|
//
|
|
24
|
+
// Self-service by default (userId omitted → event.user.id). An admin can
|
|
25
|
+
// also target someone else's account — needed once a user is already
|
|
26
|
+
// Restricted, since their own session is then unconditionally rejected by
|
|
27
|
+
// sessionChecker and they can no longer call this (or any) endpoint
|
|
28
|
+
// themselves; only an operator path reaches it at that point.
|
|
29
|
+
//
|
|
30
|
+
// Tenant scope: `isAdminActor` accepts access.admin, which includes
|
|
31
|
+
// TenantAdmin — tenant-scoped, even though the User-entity lookup below
|
|
32
|
+
// uses `ctx.db.raw` (bypasses the auto-tenant-filter, same as
|
|
33
|
+
// lift-restriction.write.ts — User status is intentionally global, see
|
|
34
|
+
// user-data-rights.md "Cross-Tenant-Semantik"). Without a membership
|
|
35
|
+
// check, a TenantAdmin from tenant A could restrict/unrestrict a user who
|
|
36
|
+
// has never been a member of tenant A. Only SystemAdmin (platform-wide)
|
|
37
|
+
// skips the check; TenantAdmin/Admin must have an active membership in
|
|
38
|
+
// the target's own tenantId.
|
|
39
|
+
//
|
|
18
40
|
// State-Transitions:
|
|
19
41
|
// Active → Restricted ✓ (dieser Handler)
|
|
20
42
|
// Restricted → Restricted ✗ 422 already_restricted (Idempotenz-Guard)
|
|
@@ -22,19 +44,48 @@ import { updateUserLifecycle } from "../lib/update-user-lifecycle";
|
|
|
22
44
|
// Deleted → ... ✗ 422 user_not_in_active_state
|
|
23
45
|
export const restrictAccountWrite = defineWriteHandler({
|
|
24
46
|
name: "restrict-account",
|
|
25
|
-
schema: z.object({}),
|
|
47
|
+
schema: z.object({ userId: z.string().uuid().optional() }),
|
|
26
48
|
access: { openToAll: true },
|
|
27
49
|
handler: async (event, ctx) => {
|
|
50
|
+
const targetUserId = event.payload.userId ?? event.user.id;
|
|
51
|
+
if (targetUserId !== event.user.id) {
|
|
52
|
+
if (!isAdminActor(event.user)) {
|
|
53
|
+
return writeFailure(
|
|
54
|
+
new AccessDeniedError({
|
|
55
|
+
details: { reason: "admin_required_for_other_user" },
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
// Same cross-tenant guard as lift-restriction.write.ts: only
|
|
60
|
+
// SystemAdmin (platform-wide) may target a user without an active
|
|
61
|
+
// membership in the caller's own tenant. Without this, a TenantAdmin
|
|
62
|
+
// from tenant A could restrict — and force-revoke the sessions of —
|
|
63
|
+
// a user who has never been a member of tenant A.
|
|
64
|
+
if (!isSystemAdminActor(event.user)) {
|
|
65
|
+
const membership = await fetchOne(ctx.db.raw, tenantMembershipsTable, {
|
|
66
|
+
userId: targetUserId,
|
|
67
|
+
tenantId: event.user.tenantId,
|
|
68
|
+
});
|
|
69
|
+
if (!membership) {
|
|
70
|
+
return writeFailure(
|
|
71
|
+
new AccessDeniedError({
|
|
72
|
+
details: { reason: "target_user_not_in_admin_tenant" },
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
28
79
|
// ctx.db.raw weil User-Entity tenant-agnostisch ist (analog
|
|
29
80
|
// request-deletion.write.ts Cross-Tenant-Section).
|
|
30
81
|
const userRow = await fetchOne<{ status: string }>(ctx.db.raw, userTable, {
|
|
31
|
-
id:
|
|
82
|
+
id: targetUserId,
|
|
32
83
|
});
|
|
33
84
|
|
|
34
85
|
if (!userRow) {
|
|
35
86
|
return writeFailure(
|
|
36
87
|
new UnprocessableError("user_not_found", {
|
|
37
|
-
details: { reason: "user_not_found", userId:
|
|
88
|
+
details: { reason: "user_not_found", userId: targetUserId },
|
|
38
89
|
}),
|
|
39
90
|
);
|
|
40
91
|
}
|
|
@@ -55,7 +106,7 @@ export const restrictAccountWrite = defineWriteHandler({
|
|
|
55
106
|
);
|
|
56
107
|
}
|
|
57
108
|
|
|
58
|
-
await updateUserLifecycle(ctx.db.raw,
|
|
109
|
+
await updateUserLifecycle(ctx.db.raw, targetUserId, { status: USER_STATUS.Restricted });
|
|
59
110
|
|
|
60
111
|
// Cross-Feature: alle live sessions revoken — sonst koennte der User
|
|
61
112
|
// mit existierendem JWT bis zur Token-Expiry weiter schreiben.
|
|
@@ -63,13 +114,13 @@ export const restrictAccountWrite = defineWriteHandler({
|
|
|
63
114
|
// System-User-Roles im access-gate hat.
|
|
64
115
|
const systemUser = createSystemUser(event.user.tenantId);
|
|
65
116
|
await ctx.writeAs(systemUser, "sessions:write:user-session:revoke-all-for-user", {
|
|
66
|
-
userId:
|
|
117
|
+
userId: targetUserId,
|
|
67
118
|
});
|
|
68
119
|
|
|
69
120
|
return {
|
|
70
121
|
isSuccess: true as const,
|
|
71
122
|
data: {
|
|
72
|
-
userId:
|
|
123
|
+
userId: targetUserId,
|
|
73
124
|
status: USER_STATUS.Restricted,
|
|
74
125
|
},
|
|
75
126
|
};
|
|
@@ -77,6 +77,8 @@ export function createRunForgetCleanupHandler(opts: RunForgetCleanupOptions = {}
|
|
|
77
77
|
hookCallsAttempted: result.hookCallsAttempted,
|
|
78
78
|
errorCount: result.errors.length,
|
|
79
79
|
errors: result.errors,
|
|
80
|
+
incompleteCount: result.incomplete.length,
|
|
81
|
+
incomplete: result.incomplete,
|
|
80
82
|
},
|
|
81
83
|
};
|
|
82
84
|
},
|
|
@@ -1,37 +1,40 @@
|
|
|
1
1
|
// @runtime client
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
// Wire-Errors aus den download-handlers tragen i18nKeys statt fixe
|
|
6
|
-
// Strings — UI rendert die Keys ueber den Renderer-LocaleProvider. Apps
|
|
7
|
-
// koennen einzelne Keys via `userDataRightsClient({ translations: { de:
|
|
8
|
-
// {...} } })` ueberschreiben (Pattern matched auth-email-password).
|
|
9
|
-
//
|
|
10
|
-
// **Scope 4b.fix3:** nur die download-error-keys. Andere Keys (UI-
|
|
11
|
-
// Texte fuer Forget-Pfad, Status-Labels, Banner) kommen mit Atom 6+
|
|
12
|
-
// (UI-Integration).
|
|
2
|
+
// Server + client i18n for user-data-rights (operator screens + nav). Pure
|
|
3
|
+
// data, importable from the web bundle (web/i18n.ts derives the client keys).
|
|
4
|
+
type LocalizedString = { readonly de: string; readonly en: string };
|
|
13
5
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export
|
|
17
|
-
de:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
6
|
+
export const USER_DATA_RIGHTS_I18N: Readonly<Record<string, LocalizedString>> = {
|
|
7
|
+
"user-data-rights:nav.exportJobs": { de: "DSGVO-Exporte", en: "GDPR exports" },
|
|
8
|
+
"screen:export-job-list.title": { de: "DSGVO-Exporte", en: "GDPR exports" },
|
|
9
|
+
"screen:export-job-detail.title": { de: "Export-Job", en: "Export job" },
|
|
10
|
+
"screen:download-attempt-list.title": { de: "Download-Versuche", en: "Download attempts" },
|
|
11
|
+
"screen:privacy-center.title": { de: "Datenschutz", en: "Privacy" },
|
|
12
|
+
"user-data-rights:entity:export-job:field:userId": { de: "Benutzer", en: "User" },
|
|
13
|
+
"user-data-rights:entity:export-job:field:status": { de: "Status", en: "Status" },
|
|
14
|
+
"user-data-rights:entity:export-job:field:requestedAt": { de: "Angefordert", en: "Requested" },
|
|
15
|
+
"user-data-rights:entity:export-job:field:completedAt": { de: "Abgeschlossen", en: "Completed" },
|
|
16
|
+
"user-data-rights:entity:export-job:field:expiresAt": { de: "Läuft ab", en: "Expires" },
|
|
17
|
+
"user-data-rights:entity:export-job:field:requestedFromTenantId": {
|
|
18
|
+
de: "Mandant",
|
|
19
|
+
en: "Tenant",
|
|
20
|
+
},
|
|
21
|
+
"user-data-rights:entity:export-job:field:startedAt": { de: "Gestartet", en: "Started" },
|
|
22
|
+
"user-data-rights:entity:export-job:field:downloadStorageKey": {
|
|
23
|
+
de: "Speicher-Schlüssel",
|
|
24
|
+
en: "Storage key",
|
|
25
|
+
},
|
|
26
|
+
"user-data-rights:entity:export-job:field:bytesWritten": { de: "Bytes", en: "Bytes" },
|
|
27
|
+
"user-data-rights:entity:export-job:field:errorMessage": { de: "Fehler", en: "Error" },
|
|
28
|
+
"user-data-rights:entity:download-attempt:field:attemptedAt": {
|
|
29
|
+
de: "Zeitpunkt",
|
|
30
|
+
en: "Attempted at",
|
|
26
31
|
},
|
|
27
|
-
en:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
"The export is not yet ready or has failed. Please check the status endpoint.",
|
|
34
|
-
"userDataRights.errors.download.signedUrlNotSupported":
|
|
35
|
-
"The download is currently unavailable due to a server configuration issue. The operator has been notified.",
|
|
32
|
+
"user-data-rights:entity:download-attempt:field:result": { de: "Ergebnis", en: "Result" },
|
|
33
|
+
"user-data-rights:entity:download-attempt:field:via": { de: "Via", en: "Via" },
|
|
34
|
+
"user-data-rights:entity:download-attempt:field:ip": { de: "IP", en: "IP" },
|
|
35
|
+
"user-data-rights:entity:download-attempt:field:attemptedByUserId": {
|
|
36
|
+
de: "Benutzer",
|
|
37
|
+
en: "User",
|
|
36
38
|
},
|
|
39
|
+
"user-data-rights:entity:download-attempt:field:jobId": { de: "Job", en: "Job" },
|
|
37
40
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SessionUser } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { access } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
|
|
4
|
+
const ADMIN_ROLES: ReadonlySet<string> = new Set(access.admin);
|
|
5
|
+
const SYSTEM_ADMIN_ROLES: ReadonlySet<string> = new Set(access.systemAdmin);
|
|
6
|
+
|
|
7
|
+
// Reused by handlers that are `openToAll: true` but still need a manual
|
|
8
|
+
// self-or-admin gate (e.g. restrict-account.write.ts) — Set-lookup instead
|
|
9
|
+
// of the O(n·m) `roles.some(role => access.admin.includes(role))` re-scan.
|
|
10
|
+
export function isAdminActor(user: Pick<SessionUser, "roles">): boolean {
|
|
11
|
+
return user.roles.some((role) => ADMIN_ROLES.has(role));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Platform-wide operator role — exempt from the cross-tenant membership
|
|
15
|
+
// check that otherwise gates the tenant-scoped admin roles (TenantAdmin/
|
|
16
|
+
// Admin) before they act on another tenant's user.
|
|
17
|
+
export function isSystemAdminActor(user: Pick<SessionUser, "roles">): boolean {
|
|
18
|
+
return user.roles.some((role) => SYSTEM_ADMIN_ROLES.has(role));
|
|
19
|
+
}
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
// construction). Extracted from the export cron so both crons + the manual
|
|
5
5
|
// forget handler share one construction site instead of inlining it three times.
|
|
6
6
|
|
|
7
|
-
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
7
|
+
import type { DbConnection, TenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
8
8
|
import type { ConfigResolver, Registry, TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
9
|
+
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
9
10
|
import type { FileStorageProvider } from "@cosmicdrift/kumiko-framework/files";
|
|
10
11
|
import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
|
|
11
12
|
import { createConfigAccessor } from "../../config";
|
|
@@ -18,7 +19,7 @@ export interface TenantStorageResolverCtx {
|
|
|
18
19
|
// Undefined → the returned resolver throws (callers decide fail-loud vs skip).
|
|
19
20
|
readonly configResolver: ConfigResolver | undefined;
|
|
20
21
|
readonly secrets: SecretsContext | undefined;
|
|
21
|
-
readonly db: DbConnection;
|
|
22
|
+
readonly db: DbConnection | TenantDb;
|
|
22
23
|
readonly userId: string;
|
|
23
24
|
readonly handlerName: string;
|
|
24
25
|
}
|
|
@@ -28,9 +29,9 @@ export function makeTenantStorageProviderResolver(
|
|
|
28
29
|
): (tenantId: TenantId) => Promise<FileStorageProvider> {
|
|
29
30
|
return async (tenantId) => {
|
|
30
31
|
if (!ctx.configResolver) {
|
|
31
|
-
throw new
|
|
32
|
-
|
|
33
|
-
);
|
|
32
|
+
throw new InternalError({
|
|
33
|
+
message: `[${ctx.handlerName}] ctx.configResolver missing — cannot resolve the file provider for tenant ${tenantId}`,
|
|
34
|
+
});
|
|
34
35
|
}
|
|
35
36
|
const config = createConfigAccessor(
|
|
36
37
|
ctx.registry,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
2
|
import type { DbRunner } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
import { createEventStoreExecutor, createTenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
4
|
-
import { createSystemUser,
|
|
4
|
+
import { createSystemUser, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
5
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
6
6
|
import { USER_STATUS, userEntity, userTable } from "../../user";
|
|
7
7
|
|
|
@@ -21,16 +21,6 @@ import { USER_STATUS, userEntity, userTable } from "../../user";
|
|
|
21
21
|
// kann abweichen).
|
|
22
22
|
const userExecutor = createEventStoreExecutor(userTable, userEntity, { entityName: "user" });
|
|
23
23
|
|
|
24
|
-
// Stream-Tenant = die framework-injizierte tenant_id der read_users-Row. Der
|
|
25
|
-
// Reducer setzt sie aus `user.created`.tenantId, ein Rebuild rekonstruiert sie
|
|
26
|
-
// daraus — sie IST also der Stream-Key des Aggregats. Die Row direkt zu lesen
|
|
27
|
-
// (statt das created-Event zu joinen) deckt auch direkt-geseedete Rows ab.
|
|
28
|
-
async function streamTenantOf(conn: DbRunner, userId: string): Promise<TenantId | null> {
|
|
29
|
-
const row = await fetchOne<{ tenantId?: string }>(conn, userTable, { id: userId });
|
|
30
|
-
// @cast-boundary db-row — tenant_id ist eine TenantId-shaped uuid-Spalte.
|
|
31
|
-
return row?.tenantId ? (row.tenantId as TenantId) : null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
24
|
// `conn` ist ctx.db.raw (regulaere Handler) ODER die offene tx (forget-cleanup
|
|
35
25
|
// Sub-Tx) — so bleibt der Event-Append atomar mit dem umgebenden Write.
|
|
36
26
|
export async function updateUserLifecycle(
|
|
@@ -38,20 +28,14 @@ export async function updateUserLifecycle(
|
|
|
38
28
|
userId: string,
|
|
39
29
|
changes: Record<string, unknown>,
|
|
40
30
|
): Promise<void> {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Rescope BEIDE Achsen auf den Stream-Tenant: der db-Arg treibt loadById +
|
|
49
|
-
// den Stream-Read, der user-Arg die Event-tenantId + Ownership. Nur beide
|
|
50
|
-
// zusammen halten created + updated auf einem Stream.
|
|
51
|
-
const tenantDb = createTenantDb(conn, streamTenantId, "tenant");
|
|
31
|
+
// user ist systemStream (#497): der Executor-Choke-Point addressiert den
|
|
32
|
+
// Stream immer auf SYSTEM_TENANT_ID — ein Rescope auf die row-tenant_id
|
|
33
|
+
// waere wirkungslos. "system"-Mode, damit loadById auch Legacy-Rows findet,
|
|
34
|
+
// deren tenant_id noch vor dem #762-Backfill-Rebuild steht.
|
|
35
|
+
const tenantDb = createTenantDb(conn, SYSTEM_TENANT_ID, "system");
|
|
52
36
|
const result = await userExecutor.update(
|
|
53
37
|
{ id: userId, changes },
|
|
54
|
-
createSystemUser(
|
|
38
|
+
createSystemUser(SYSTEM_TENANT_ID),
|
|
55
39
|
tenantDb,
|
|
56
40
|
{ skipOptimisticLock: true },
|
|
57
41
|
);
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
|
|
47
47
|
import { fetchOne, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
48
48
|
import { addDurationSpec } from "@cosmicdrift/kumiko-framework/compliance";
|
|
49
|
+
import { PII_ERASED_SENTINEL } from "@cosmicdrift/kumiko-framework/crypto";
|
|
49
50
|
import type { DbConnection, DbRunner } from "@cosmicdrift/kumiko-framework/db";
|
|
50
51
|
import { createEventStoreExecutor, createTenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
51
52
|
import type { Registry, TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -57,6 +58,7 @@ import {
|
|
|
57
58
|
} from "@cosmicdrift/kumiko-framework/files";
|
|
58
59
|
import type { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
59
60
|
import { resolveProfileForTenant } from "../compliance-profiles";
|
|
61
|
+
import { decryptStoredPii } from "../shared";
|
|
60
62
|
import { userTable } from "../user";
|
|
61
63
|
import { selectExportJobsForStorageCleanup } from "./db/queries/export-jobs";
|
|
62
64
|
import { runUserExport, type UserExportBundle } from "./run-user-export";
|
|
@@ -103,9 +105,10 @@ export const tokenCrud = createEventStoreExecutor(
|
|
|
103
105
|
*
|
|
104
106
|
* **Plain-Token-Recovery bei Email-Fail:** plain ist ephemeral. Wenn
|
|
105
107
|
* der Callback throwt, ist der plain verloren. Operator-Eingriff
|
|
106
|
-
* noetig
|
|
107
|
-
*
|
|
108
|
-
*
|
|
108
|
+
* noetig: Job auf pending flippen — NUR das. Die Token-Row NICHT
|
|
109
|
+
* loeschen (eventloser Read-Side-Delete → Rebuild-Kollision, fw#832);
|
|
110
|
+
* der naechste Worker-Run rotiert das Token in place auf demselben
|
|
111
|
+
* Aggregat. Atom 5b (Re-issue-handler) wird das automatisieren.
|
|
109
112
|
*/
|
|
110
113
|
export type SendExportReadyEmailFn = (args: {
|
|
111
114
|
readonly userId: string;
|
|
@@ -149,7 +152,7 @@ export interface RunExportJobsArgs {
|
|
|
149
152
|
readonly sendExportFailedEmail?: SendExportFailedEmailFn;
|
|
150
153
|
/** Base-URL fuer den Magic-Link. App-Author setzt das (z.B.
|
|
151
154
|
* "https://app.example.com/user-export/by-token"). Worker baut
|
|
152
|
-
*
|
|
155
|
+
* a URL fragment (not a query param — see issue #1271) and passes that
|
|
153
156
|
* Callback durch. Required wenn sendExportReadyEmail gesetzt. */
|
|
154
157
|
readonly appExportDownloadUrl?: string;
|
|
155
158
|
}
|
|
@@ -425,22 +428,53 @@ async function processJob(args: {
|
|
|
425
428
|
// Plain-Token bleibt im Worker-Memory + wird via RunExportJobsResult
|
|
426
429
|
// an Atom 5 (Notification) weitergegeben. NUR der hash landet in DB.
|
|
427
430
|
// ES via tokenCrud.create — kein direct-INSERT.
|
|
431
|
+
//
|
|
432
|
+
// **Re-Run (fw#832):** Flippt ein Operator einen done/failed-Job
|
|
433
|
+
// zurueck auf pending (Plain-Token verloren, Email-Fail), existiert
|
|
434
|
+
// fuers jobId schon eine Token-Row. Ein zweites create wuerde ein
|
|
435
|
+
// zweites `created`-Event auf frischem Aggregat emittieren — der
|
|
436
|
+
// Projection-Rebuild kollidiert dann dauerhaft am one_per_job-Index.
|
|
437
|
+
// Stattdessen: Token-Rotation in place auf demselben Aggregat
|
|
438
|
+
// (update mit frischem Hash + Reset der Audit-Felder).
|
|
428
439
|
const expiresAt = addDurationSpec(now, ttl);
|
|
429
440
|
const { plain: tokenPlain, hash: tokenHash } = await generateDownloadToken();
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
issuedAt: now,
|
|
435
|
-
expiresAt,
|
|
436
|
-
// Atom 4a.fix: useCount explizit 0 statt default null. 4b's
|
|
437
|
-
// Verify-Pfad incrementiert via `useCount + 1` ohne COALESCE-
|
|
438
|
-
// Defensiv-Code.
|
|
439
|
-
useCount: 0,
|
|
440
|
-
},
|
|
441
|
-
executor,
|
|
442
|
-
tdb,
|
|
441
|
+
const existingToken = await fetchOne<{ id: string; version: number }>(
|
|
442
|
+
db,
|
|
443
|
+
exportDownloadTokensTable,
|
|
444
|
+
{ jobId: job.id },
|
|
443
445
|
);
|
|
446
|
+
const tokenCreateResult = existingToken
|
|
447
|
+
? await tokenCrud.update(
|
|
448
|
+
{
|
|
449
|
+
id: existingToken.id,
|
|
450
|
+
version: existingToken.version,
|
|
451
|
+
changes: {
|
|
452
|
+
tokenHash,
|
|
453
|
+
issuedAt: now,
|
|
454
|
+
expiresAt,
|
|
455
|
+
useCount: 0,
|
|
456
|
+
lastUsedAt: null,
|
|
457
|
+
lastUsedFromIp: null,
|
|
458
|
+
lastUsedUserAgent: null,
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
executor,
|
|
462
|
+
tdb,
|
|
463
|
+
)
|
|
464
|
+
: await tokenCrud.create(
|
|
465
|
+
{
|
|
466
|
+
jobId: job.id,
|
|
467
|
+
tokenHash,
|
|
468
|
+
issuedAt: now,
|
|
469
|
+
expiresAt,
|
|
470
|
+
// Atom 4a.fix: useCount explizit 0 statt default null. 4b's
|
|
471
|
+
// Verify-Pfad incrementiert via `useCount + 1` ohne COALESCE-
|
|
472
|
+
// Defensiv-Code.
|
|
473
|
+
useCount: 0,
|
|
474
|
+
},
|
|
475
|
+
executor,
|
|
476
|
+
tdb,
|
|
477
|
+
);
|
|
444
478
|
if (!tokenCreateResult.isSuccess) {
|
|
445
479
|
// Token-Creation failed VOR done-flip → Job bleibt running.
|
|
446
480
|
// Catch-Pfad flippt auf failed mit klarer Diagnose. Storage-
|
|
@@ -767,7 +801,9 @@ async function lookupUserContact(
|
|
|
767
801
|
locale: string | null;
|
|
768
802
|
} | null;
|
|
769
803
|
if (!row?.email) return null;
|
|
770
|
-
|
|
804
|
+
const email = await decryptStoredPii(row.email, "email", "user-data-rights:export-ready");
|
|
805
|
+
if (email === PII_ERASED_SENTINEL) return null;
|
|
806
|
+
return { email, locale: row.locale ?? null };
|
|
771
807
|
}
|
|
772
808
|
|
|
773
809
|
// Atom 5 — Email-Notification beim done-flip. Best-effort:
|
|
@@ -808,7 +844,7 @@ async function fireExportReadyCallback(args: {
|
|
|
808
844
|
"user-data-rights: sendExportReadyEmail gesetzt aber appExportDownloadUrl fehlt — beide muessen zusammen konfiguriert sein",
|
|
809
845
|
);
|
|
810
846
|
}
|
|
811
|
-
const downloadUrl = `${baseUrl}
|
|
847
|
+
const downloadUrl = `${baseUrl}#token=${encodeURIComponent(args.plainToken)}`;
|
|
812
848
|
await args.send({
|
|
813
849
|
userId: args.job.userId,
|
|
814
850
|
userEmail: contact.email,
|