@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
|
@@ -3,8 +3,28 @@ import {
|
|
|
3
3
|
EXT_USER_DATA,
|
|
4
4
|
type FeatureDefinition,
|
|
5
5
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
6
|
+
import { apiTokenDeleteHook, apiTokenExportHook } from "./hooks/api-token.userdata-hook";
|
|
7
|
+
import { configValueDeleteHook, configValueExportHook } from "./hooks/config-value.userdata-hook";
|
|
8
|
+
import {
|
|
9
|
+
deliveryAttemptDeleteHook,
|
|
10
|
+
deliveryAttemptExportHook,
|
|
11
|
+
} from "./hooks/delivery-attempt.userdata-hook";
|
|
6
12
|
import { fileRefDeleteHook, fileRefExportHook } from "./hooks/file-ref.userdata-hook";
|
|
13
|
+
import {
|
|
14
|
+
inAppMessageDeleteHook,
|
|
15
|
+
inAppMessageExportHook,
|
|
16
|
+
} from "./hooks/in-app-message.userdata-hook";
|
|
17
|
+
import { jobRunDeleteHook, jobRunExportHook } from "./hooks/job-run.userdata-hook";
|
|
18
|
+
import {
|
|
19
|
+
notificationPreferenceDeleteHook,
|
|
20
|
+
notificationPreferenceExportHook,
|
|
21
|
+
} from "./hooks/notification-preference.userdata-hook";
|
|
22
|
+
import {
|
|
23
|
+
tenantInvitationDeleteHook,
|
|
24
|
+
tenantInvitationExportHook,
|
|
25
|
+
} from "./hooks/tenant-invitation.userdata-hook";
|
|
7
26
|
import { userDeleteHook, userExportHook } from "./hooks/user.userdata-hook";
|
|
27
|
+
import { userSessionDeleteHook, userSessionExportHook } from "./hooks/user-session.userdata-hook";
|
|
8
28
|
|
|
9
29
|
// user-data-rights-defaults — Default-Hooks für die Core-Entities
|
|
10
30
|
// `user` (S2.H1) und `fileRef` (S2.H2).
|
|
@@ -31,7 +51,7 @@ import { userDeleteHook, userExportHook } from "./hooks/user.userdata-hook";
|
|
|
31
51
|
export function createUserDataRightsDefaultsFeature(): FeatureDefinition {
|
|
32
52
|
return defineFeature("user-data-rights-defaults", (r) => {
|
|
33
53
|
r.describe(
|
|
34
|
-
"Registers ready-made `EXT_USER_DATA` export and delete hooks for the
|
|
54
|
+
"Registers ready-made `EXT_USER_DATA` export and delete hooks for the bundled entities that hold per-user data: `user` (delete strategy sets email to `deleted-<id>@anonymized.invalid`, nulls `passwordHash`, sets status to `Deleted`; anonymize strategy sets email to `anonymized-<id>@anonymized.invalid` without touching `passwordHash`), `fileRef` (delete removes both the DB row and the storage binary), plus — gated on the source feature being mounted — `user-session` (ip/userAgent, hard-delete), `api-token` (hard-delete = revoke), `in-app-message` (hard-delete), `tenant-invitation` (invitee email forgotten/pseudonymized, inviter link severed), `notification-preference` and user-scoped `config-value` (purged via the forget verb), plus export-only hooks for the events-only aggregates `delivery-attempt` (recipientAddress) and `job-run` (payload) whose erasure runs via crypto-shredding. Mount this alongside `user-data-rights` for standard GDPR compliance; omit it only if your app needs custom anonymization logic for these entities.",
|
|
35
55
|
);
|
|
36
56
|
r.uiHints({
|
|
37
57
|
displayLabel: "User Data Rights · Default Hooks",
|
|
@@ -39,6 +59,19 @@ export function createUserDataRightsDefaultsFeature(): FeatureDefinition {
|
|
|
39
59
|
recommended: false,
|
|
40
60
|
});
|
|
41
61
|
r.requires("user", "files", "user-data-rights");
|
|
62
|
+
// Optional sources: hooks are registered unconditionally but each one
|
|
63
|
+
// no-ops at runtime when its source feature isn't mounted (see
|
|
64
|
+
// hooks/feature-mounted.ts) — the export runner has no per-hook
|
|
65
|
+
// try/catch, so a query against a missing table would kill the job.
|
|
66
|
+
r.optionalRequires(
|
|
67
|
+
"sessions",
|
|
68
|
+
"personal-access-tokens",
|
|
69
|
+
"channel-in-app",
|
|
70
|
+
"tenant",
|
|
71
|
+
"delivery",
|
|
72
|
+
"config",
|
|
73
|
+
"jobs",
|
|
74
|
+
);
|
|
42
75
|
|
|
43
76
|
r.useExtension(EXT_USER_DATA, "user", {
|
|
44
77
|
export: userExportHook,
|
|
@@ -49,5 +82,49 @@ export function createUserDataRightsDefaultsFeature(): FeatureDefinition {
|
|
|
49
82
|
export: fileRefExportHook,
|
|
50
83
|
delete: fileRefDeleteHook,
|
|
51
84
|
});
|
|
85
|
+
|
|
86
|
+
r.useExtension(EXT_USER_DATA, "user-session", {
|
|
87
|
+
export: userSessionExportHook,
|
|
88
|
+
delete: userSessionDeleteHook,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
r.useExtension(EXT_USER_DATA, "api-token", {
|
|
92
|
+
export: apiTokenExportHook,
|
|
93
|
+
delete: apiTokenDeleteHook,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
r.useExtension(EXT_USER_DATA, "in-app-message", {
|
|
97
|
+
export: inAppMessageExportHook,
|
|
98
|
+
delete: inAppMessageDeleteHook,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
r.useExtension(EXT_USER_DATA, "tenant-invitation", {
|
|
102
|
+
export: tenantInvitationExportHook,
|
|
103
|
+
delete: tenantInvitationDeleteHook,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
r.useExtension(EXT_USER_DATA, "notification-preference", {
|
|
107
|
+
export: notificationPreferenceExportHook,
|
|
108
|
+
delete: notificationPreferenceDeleteHook,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
r.useExtension(EXT_USER_DATA, "config-value", {
|
|
112
|
+
export: configValueExportHook,
|
|
113
|
+
delete: configValueDeleteHook,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Events-only aggregates (#799): export reads the projected rows, the
|
|
117
|
+
// delete hooks are deliberate no-ops — erasure happens via crypto-
|
|
118
|
+
// shredding (forget erases the user's DEK; recipientAddress / job
|
|
119
|
+
// payload become unreadable in events AND rows).
|
|
120
|
+
r.useExtension(EXT_USER_DATA, "delivery-attempt", {
|
|
121
|
+
export: deliveryAttemptExportHook,
|
|
122
|
+
delete: deliveryAttemptDeleteHook,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
r.useExtension(EXT_USER_DATA, "job-run", {
|
|
126
|
+
export: jobRunExportHook,
|
|
127
|
+
delete: jobRunDeleteHook,
|
|
128
|
+
});
|
|
52
129
|
});
|
|
53
130
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { deleteMany, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
+
import type { UserDataDeleteHook, UserDataExportHook } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { apiTokenTable } from "../../personal-access-tokens";
|
|
4
|
+
import { featureMounted } from "./feature-mounted";
|
|
5
|
+
|
|
6
|
+
// userData-Hooks for personal-access-tokens rows. Same shape as user-session:
|
|
7
|
+
// unmanaged direct-write store (no event stream, rebuild-safe DELETE), and a
|
|
8
|
+
// token without its user is worthless — both strategies hard-delete. Deleting
|
|
9
|
+
// the row also revokes the token (the resolver point-reads this table), which
|
|
10
|
+
// is the correct outcome for a forgotten user.
|
|
11
|
+
|
|
12
|
+
export const apiTokenExportHook: UserDataExportHook = async (ctx) => {
|
|
13
|
+
if (!featureMounted(ctx, "personal-access-tokens")) return null;
|
|
14
|
+
const rows = await selectMany<Record<string, unknown>>(ctx.db, apiTokenTable, {
|
|
15
|
+
userId: ctx.userId,
|
|
16
|
+
tenantId: ctx.tenantId,
|
|
17
|
+
});
|
|
18
|
+
if (rows.length === 0) return null;
|
|
19
|
+
return {
|
|
20
|
+
entity: "api-token",
|
|
21
|
+
// tokenHash stays out of the bundle — secret-derived, not personal data.
|
|
22
|
+
rows: rows.map((r) => ({
|
|
23
|
+
name: r["name"] ?? null,
|
|
24
|
+
prefix: r["prefix"] ?? null,
|
|
25
|
+
scopes: r["scopes"] ?? null,
|
|
26
|
+
createdAt: String(r["createdAt"] ?? ""),
|
|
27
|
+
expiresAt: r["expiresAt"] ? String(r["expiresAt"]) : null,
|
|
28
|
+
revokedAt: r["revokedAt"] ? String(r["revokedAt"]) : null,
|
|
29
|
+
})),
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const apiTokenDeleteHook: UserDataDeleteHook = async (ctx) => {
|
|
34
|
+
// skip: personal-access-tokens not mounted — its table doesn't exist, nothing to erase.
|
|
35
|
+
if (!featureMounted(ctx, "personal-access-tokens")) return;
|
|
36
|
+
await deleteMany(ctx.db, apiTokenTable, { userId: ctx.userId, tenantId: ctx.tenantId });
|
|
37
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
+
import { createEventStoreExecutor, createTenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
+
import {
|
|
4
|
+
createSystemUser,
|
|
5
|
+
type UserDataDeleteHook,
|
|
6
|
+
type UserDataExportHook,
|
|
7
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
8
|
+
import { configValueEntity, configValuesTable } from "../../config";
|
|
9
|
+
import { featureMounted } from "./feature-mounted";
|
|
10
|
+
|
|
11
|
+
// userData-Hooks for config's USER-scoped rows (userId set). Tenant-/system-
|
|
12
|
+
// scope rows carry no per-user subject and stay untouched. Event-sourced
|
|
13
|
+
// entity → forget goes through the executor (rebuild replays the erasure).
|
|
14
|
+
// A user-scoped setting without its user is meaningless — both strategies
|
|
15
|
+
// purge via the forget verb, which also erases encrypted value blobs.
|
|
16
|
+
|
|
17
|
+
const crud = createEventStoreExecutor(configValuesTable, configValueEntity, {
|
|
18
|
+
entityName: "config-value",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const configValueExportHook: UserDataExportHook = async (ctx) => {
|
|
22
|
+
if (!featureMounted(ctx, "config")) return null;
|
|
23
|
+
const rows = await selectMany<Record<string, unknown>>(ctx.db, configValuesTable, {
|
|
24
|
+
tenantId: ctx.tenantId,
|
|
25
|
+
userId: ctx.userId,
|
|
26
|
+
});
|
|
27
|
+
if (rows.length === 0) return null;
|
|
28
|
+
return {
|
|
29
|
+
entity: "config-value",
|
|
30
|
+
// value may be an encrypted blob — exported as stored; decryption is the
|
|
31
|
+
// config resolver's concern and secret-backed values don't belong in the
|
|
32
|
+
// bundle in plaintext.
|
|
33
|
+
rows: rows.map((r) => ({ key: r["key"], value: r["value"] })),
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const configValueDeleteHook: UserDataDeleteHook = async (ctx) => {
|
|
38
|
+
// skip: config not mounted — its table doesn't exist, nothing to erase.
|
|
39
|
+
if (!featureMounted(ctx, "config")) return;
|
|
40
|
+
const systemUser = createSystemUser(ctx.tenantId);
|
|
41
|
+
const tdb = createTenantDb(ctx.db, ctx.tenantId, "system");
|
|
42
|
+
const rows = await selectMany<Record<string, unknown>>(ctx.db, configValuesTable, {
|
|
43
|
+
tenantId: ctx.tenantId,
|
|
44
|
+
userId: ctx.userId,
|
|
45
|
+
});
|
|
46
|
+
for (const row of rows) {
|
|
47
|
+
const id = row["id"]; // @cast-boundary db-row
|
|
48
|
+
if (typeof id !== "string") continue;
|
|
49
|
+
await crud.forget({ id }, systemUser, tdb);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
+
import type { UserDataDeleteHook, UserDataExportHook } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { deliveryAttemptsTable } from "../../delivery";
|
|
4
|
+
import { featureMounted } from "./feature-mounted";
|
|
5
|
+
|
|
6
|
+
// userData-Hooks for delivery's attempt log (deferred from #797, closed by
|
|
7
|
+
// #799). deliveryAttempt is an events-only aggregate — the export reads the
|
|
8
|
+
// projected rows; recipientAddress may be ciphertext (kumiko-pii:), which
|
|
9
|
+
// the export runner's central decrypt sweep resolves to plaintext (or
|
|
10
|
+
// [[erased]] after a forget).
|
|
11
|
+
|
|
12
|
+
export const deliveryAttemptExportHook: UserDataExportHook = async (ctx) => {
|
|
13
|
+
if (!featureMounted(ctx, "delivery")) return null;
|
|
14
|
+
const rows = await selectMany<Record<string, unknown>>(ctx.db, deliveryAttemptsTable, {
|
|
15
|
+
tenantId: ctx.tenantId,
|
|
16
|
+
recipientId: ctx.userId,
|
|
17
|
+
});
|
|
18
|
+
if (rows.length === 0) return null;
|
|
19
|
+
return {
|
|
20
|
+
entity: "delivery-attempt",
|
|
21
|
+
rows: rows.map((r) => ({
|
|
22
|
+
notificationType: r["notificationType"],
|
|
23
|
+
channel: r["channel"],
|
|
24
|
+
status: r["status"],
|
|
25
|
+
recipientAddress: r["recipientAddress"],
|
|
26
|
+
priority: r["priority"],
|
|
27
|
+
createdAt: r["createdAt"],
|
|
28
|
+
})),
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const deliveryAttemptDeleteHook: UserDataDeleteHook = async () => {
|
|
33
|
+
// Deliberate no-op: erasure runs via crypto-shredding — the forget pipeline
|
|
34
|
+
// erases the recipient's DEK, which makes recipientAddress unreadable in
|
|
35
|
+
// BOTH the append-only events and the projected rows (#799). A read-side
|
|
36
|
+
// UPDATE here would be wiped on the next projection rebuild anyway.
|
|
37
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { UserDataHookCtx } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
|
|
3
|
+
// The defaults feature registers hooks for OPTIONAL source features
|
|
4
|
+
// (optionalRequires). When the source isn't mounted its tables don't exist,
|
|
5
|
+
// and the export runner has no try/catch around hooks — a query against a
|
|
6
|
+
// missing table would kill the whole export job. Every gated hook must
|
|
7
|
+
// early-return on this check before touching the source's tables.
|
|
8
|
+
export function featureMounted(ctx: UserDataHookCtx, featureName: string): boolean {
|
|
9
|
+
return ctx.registry.getFeature(featureName) !== undefined;
|
|
10
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { deleteMany, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
+
import type { UserDataDeleteHook, UserDataExportHook } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { inAppMessagesTable } from "../../channel-in-app";
|
|
4
|
+
import { featureMounted } from "./feature-mounted";
|
|
5
|
+
|
|
6
|
+
// userData-Hooks for channel-in-app's in_app_messages. Plain SQL table (no
|
|
7
|
+
// r.entity, no event stream — see channel-in-app/tables.ts), written by
|
|
8
|
+
// direct INSERT in the channel adapter, so a direct DELETE is rebuild-safe.
|
|
9
|
+
// Messages are addressed TO the user (title/body can quote personal content);
|
|
10
|
+
// both strategies hard-delete.
|
|
11
|
+
//
|
|
12
|
+
// This table has no entity fields, so the V3 pii-annotation boot guard cannot
|
|
13
|
+
// see it — coverage lives solely in these hooks.
|
|
14
|
+
|
|
15
|
+
export const inAppMessageExportHook: UserDataExportHook = async (ctx) => {
|
|
16
|
+
if (!featureMounted(ctx, "channel-in-app")) return null;
|
|
17
|
+
const rows = await selectMany<Record<string, unknown>>(ctx.db, inAppMessagesTable, {
|
|
18
|
+
userId: ctx.userId,
|
|
19
|
+
tenantId: ctx.tenantId,
|
|
20
|
+
});
|
|
21
|
+
if (rows.length === 0) return null;
|
|
22
|
+
return {
|
|
23
|
+
entity: "in-app-message",
|
|
24
|
+
rows: rows.map((r) => ({
|
|
25
|
+
notificationType: r["notificationType"] ?? null,
|
|
26
|
+
title: r["title"] ?? null,
|
|
27
|
+
body: r["body"] ?? null,
|
|
28
|
+
isRead: r["isRead"] ?? false,
|
|
29
|
+
createdAt: String(r["createdAt"] ?? ""),
|
|
30
|
+
})),
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const inAppMessageDeleteHook: UserDataDeleteHook = async (ctx) => {
|
|
35
|
+
// skip: channel-in-app not mounted — its table doesn't exist, nothing to erase.
|
|
36
|
+
if (!featureMounted(ctx, "channel-in-app")) return;
|
|
37
|
+
await deleteMany(ctx.db, inAppMessagesTable, { userId: ctx.userId, tenantId: ctx.tenantId });
|
|
38
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
+
import type { UserDataDeleteHook, UserDataExportHook } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { jobRunsTable } from "../../jobs";
|
|
4
|
+
import { featureMounted } from "./feature-mounted";
|
|
5
|
+
|
|
6
|
+
// userData-Hooks for the jobs run log (deferred from #797, closed by #799).
|
|
7
|
+
// Job runs live on the SYSTEM tenant regardless of who triggered them, so
|
|
8
|
+
// the export filters by triggeredById only — Art. 15 covers everything the
|
|
9
|
+
// user caused, across tenants. payload may be ciphertext (kumiko-pii:); the
|
|
10
|
+
// export runner's central decrypt sweep resolves it.
|
|
11
|
+
|
|
12
|
+
export const jobRunExportHook: UserDataExportHook = async (ctx) => {
|
|
13
|
+
if (!featureMounted(ctx, "jobs")) return null;
|
|
14
|
+
const rows = await selectMany<Record<string, unknown>>(ctx.db, jobRunsTable, {
|
|
15
|
+
triggeredById: ctx.userId,
|
|
16
|
+
});
|
|
17
|
+
if (rows.length === 0) return null;
|
|
18
|
+
return {
|
|
19
|
+
entity: "job-run",
|
|
20
|
+
rows: rows.map((r) => ({
|
|
21
|
+
jobName: r["jobName"],
|
|
22
|
+
status: r["status"],
|
|
23
|
+
payload: r["payload"],
|
|
24
|
+
startedAt: r["startedAt"],
|
|
25
|
+
finishedAt: r["finishedAt"],
|
|
26
|
+
})),
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const jobRunDeleteHook: UserDataDeleteHook = async () => {
|
|
31
|
+
// Deliberate no-op: erasure runs via crypto-shredding — the forget pipeline
|
|
32
|
+
// erases the triggering user's DEK, which makes the payload unreadable in
|
|
33
|
+
// BOTH the append-only events and the projected rows (#799). triggeredById
|
|
34
|
+
// itself is a pseudonymous fk (plaintext by design, like config.userId).
|
|
35
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
+
import { createEventStoreExecutor, createTenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
+
import {
|
|
4
|
+
createSystemUser,
|
|
5
|
+
type UserDataDeleteHook,
|
|
6
|
+
type UserDataExportHook,
|
|
7
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
8
|
+
import { notificationPreferenceEntity, notificationPreferencesTable } from "../../delivery";
|
|
9
|
+
import { featureMounted } from "./feature-mounted";
|
|
10
|
+
|
|
11
|
+
// userData-Hooks for delivery's notification-preference rows. Event-sourced
|
|
12
|
+
// entity → forget goes through the executor so a rebuild replays the erasure.
|
|
13
|
+
// A preference without its user is meaningless, so both strategies purge via
|
|
14
|
+
// the forget verb.
|
|
15
|
+
//
|
|
16
|
+
// The delivery ATTEMPTS log is covered separately: export via
|
|
17
|
+
// delivery-attempt.userdata-hook.ts, erasure via crypto-shredding (#799).
|
|
18
|
+
|
|
19
|
+
const crud = createEventStoreExecutor(notificationPreferencesTable, notificationPreferenceEntity, {
|
|
20
|
+
entityName: "notification-preference",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const notificationPreferenceExportHook: UserDataExportHook = async (ctx) => {
|
|
24
|
+
if (!featureMounted(ctx, "delivery")) return null;
|
|
25
|
+
const rows = await selectMany<Record<string, unknown>>(ctx.db, notificationPreferencesTable, {
|
|
26
|
+
tenantId: ctx.tenantId,
|
|
27
|
+
userId: ctx.userId,
|
|
28
|
+
});
|
|
29
|
+
if (rows.length === 0) return null;
|
|
30
|
+
return {
|
|
31
|
+
entity: "notification-preference",
|
|
32
|
+
rows: rows.map((r) => ({
|
|
33
|
+
notificationType: r["notificationType"],
|
|
34
|
+
channel: r["channel"],
|
|
35
|
+
enabled: r["enabled"],
|
|
36
|
+
})),
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const notificationPreferenceDeleteHook: UserDataDeleteHook = async (ctx) => {
|
|
41
|
+
// skip: delivery not mounted — its table doesn't exist, nothing to erase.
|
|
42
|
+
if (!featureMounted(ctx, "delivery")) return;
|
|
43
|
+
const systemUser = createSystemUser(ctx.tenantId);
|
|
44
|
+
const tdb = createTenantDb(ctx.db, ctx.tenantId, "system");
|
|
45
|
+
const rows = await selectMany<Record<string, unknown>>(ctx.db, notificationPreferencesTable, {
|
|
46
|
+
tenantId: ctx.tenantId,
|
|
47
|
+
userId: ctx.userId,
|
|
48
|
+
});
|
|
49
|
+
for (const row of rows) {
|
|
50
|
+
const id = row["id"]; // @cast-boundary db-row
|
|
51
|
+
if (typeof id !== "string") continue;
|
|
52
|
+
await crud.forget({ id }, systemUser, tdb);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { fetchOne, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
+
import { createEventStoreExecutor, createTenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
+
import {
|
|
4
|
+
createSystemUser,
|
|
5
|
+
type UserDataDeleteHook,
|
|
6
|
+
type UserDataExportHook,
|
|
7
|
+
type UserDataHookCtx,
|
|
8
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
9
|
+
import { decryptStoredPii } from "../../shared";
|
|
10
|
+
import { tenantInvitationEntity, tenantInvitationsTable } from "../../tenant";
|
|
11
|
+
import { userTable } from "../../user";
|
|
12
|
+
import { featureMounted } from "./feature-mounted";
|
|
13
|
+
|
|
14
|
+
// userData-Hooks for tenant-invitation rows. Event-sourced entity → all
|
|
15
|
+
// forget writes go through the executor so a projection rebuild replays the
|
|
16
|
+
// erasure (a direct write would be wiped — the Art.17 hole the user/fileRef
|
|
17
|
+
// hooks already close).
|
|
18
|
+
//
|
|
19
|
+
// Two subject paths in one hook:
|
|
20
|
+
// invitee — rows whose `email` is the user's email. delete → executor
|
|
21
|
+
// forget (row purged); anonymize → email pseudonymized.
|
|
22
|
+
// inviter — rows whose `invitedBy` is the user. The row belongs to the
|
|
23
|
+
// invitee, so both strategies only sever the person-link
|
|
24
|
+
// (invitedBy → pseudonym; the field is required, null not
|
|
25
|
+
// allowed).
|
|
26
|
+
//
|
|
27
|
+
// Email source: on forget the user hook may already have anonymized the
|
|
28
|
+
// user row, so ctx.userEmailBeforeDelete (captured by runForgetCleanup
|
|
29
|
+
// before the transaction) takes precedence over a live lookup.
|
|
30
|
+
|
|
31
|
+
const crud = createEventStoreExecutor(tenantInvitationsTable, tenantInvitationEntity, {
|
|
32
|
+
entityName: "tenant-invitation",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const INVITED_BY_ANONYMIZED = "anonymized";
|
|
36
|
+
|
|
37
|
+
async function resolveUserEmail(ctx: UserDataHookCtx): Promise<string | null> {
|
|
38
|
+
if (ctx.userEmailBeforeDelete) return ctx.userEmailBeforeDelete.toLowerCase();
|
|
39
|
+
const row = (await fetchOne(ctx.db, userTable, { id: ctx.userId })) as {
|
|
40
|
+
email: string;
|
|
41
|
+
} | null; // @cast-boundary db-runner
|
|
42
|
+
if (!row) return null;
|
|
43
|
+
// Stored value is ciphertext with an active KMS; lowercasing a base64
|
|
44
|
+
// blob would break both lookup arms — decrypt first.
|
|
45
|
+
const email = await decryptStoredPii(row.email, "email", "user-data-rights:invitation-hook");
|
|
46
|
+
return email.toLowerCase();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const tenantInvitationExportHook: UserDataExportHook = async (ctx) => {
|
|
50
|
+
if (!featureMounted(ctx, "tenant")) return null;
|
|
51
|
+
const email = await resolveUserEmail(ctx);
|
|
52
|
+
if (!email) return null;
|
|
53
|
+
// Invitation emails are lowercase-normalized on insert, so an exact match
|
|
54
|
+
// is a case-insensitive match.
|
|
55
|
+
const rows = await selectMany<Record<string, unknown>>(ctx.db, tenantInvitationsTable, {
|
|
56
|
+
tenantId: ctx.tenantId,
|
|
57
|
+
email,
|
|
58
|
+
});
|
|
59
|
+
if (rows.length === 0) return null;
|
|
60
|
+
return {
|
|
61
|
+
entity: "tenant-invitation",
|
|
62
|
+
rows: rows.map((r) => ({
|
|
63
|
+
email: r["email"],
|
|
64
|
+
role: r["role"],
|
|
65
|
+
status: r["status"],
|
|
66
|
+
expiresAt: String(r["expiresAt"] ?? ""),
|
|
67
|
+
})),
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const tenantInvitationDeleteHook: UserDataDeleteHook = async (ctx, strategy) => {
|
|
72
|
+
// skip: tenant not mounted — its table doesn't exist, nothing to erase.
|
|
73
|
+
if (!featureMounted(ctx, "tenant")) return;
|
|
74
|
+
const systemUser = createSystemUser(ctx.tenantId);
|
|
75
|
+
const tdb = createTenantDb(ctx.db, ctx.tenantId, "system");
|
|
76
|
+
|
|
77
|
+
const email = await resolveUserEmail(ctx);
|
|
78
|
+
if (email) {
|
|
79
|
+
const inviteeRows = await selectMany<Record<string, unknown>>(ctx.db, tenantInvitationsTable, {
|
|
80
|
+
tenantId: ctx.tenantId,
|
|
81
|
+
email,
|
|
82
|
+
});
|
|
83
|
+
for (const row of inviteeRows) {
|
|
84
|
+
const id = row["id"]; // @cast-boundary db-row
|
|
85
|
+
if (typeof id !== "string") continue;
|
|
86
|
+
if (strategy === "delete") {
|
|
87
|
+
await crud.forget({ id }, systemUser, tdb);
|
|
88
|
+
} else {
|
|
89
|
+
// Row-id in the pseudonym keeps the (tenantId, email) unique index
|
|
90
|
+
// collision-free when a user has invitations in several states.
|
|
91
|
+
await crud.update(
|
|
92
|
+
{ id, changes: { email: `forgotten-${id}@anonymized.invalid` } },
|
|
93
|
+
systemUser,
|
|
94
|
+
tdb,
|
|
95
|
+
{ skipOptimisticLock: true },
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const inviterRows = await selectMany<Record<string, unknown>>(ctx.db, tenantInvitationsTable, {
|
|
102
|
+
tenantId: ctx.tenantId,
|
|
103
|
+
invitedBy: ctx.userId,
|
|
104
|
+
});
|
|
105
|
+
for (const row of inviterRows) {
|
|
106
|
+
const id = row["id"]; // @cast-boundary db-row
|
|
107
|
+
if (typeof id !== "string") continue;
|
|
108
|
+
await crud.update({ id, changes: { invitedBy: INVITED_BY_ANONYMIZED } }, systemUser, tdb, {
|
|
109
|
+
skipOptimisticLock: true,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { deleteMany, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
+
import type { UserDataDeleteHook, UserDataExportHook } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { userSessionTable } from "../../sessions";
|
|
4
|
+
import { featureMounted } from "./feature-mounted";
|
|
5
|
+
|
|
6
|
+
// userData-Hooks for the sessions feature's user-session rows (ip, userAgent).
|
|
7
|
+
//
|
|
8
|
+
// user-session is an unmanaged direct-write store (no event stream, excluded
|
|
9
|
+
// from projection rebuilds — see sessions/feature.ts #494/#498), so a direct
|
|
10
|
+
// DELETE is legal here and survives rebuilds; the executor path the ES
|
|
11
|
+
// entities use does not apply.
|
|
12
|
+
//
|
|
13
|
+
// Both forget strategies hard-delete: a session row without its user is
|
|
14
|
+
// worthless (revocation audit trail loses its subject), and ip/userAgent are
|
|
15
|
+
// pure PII with no shared-data value to preserve via anonymization.
|
|
16
|
+
|
|
17
|
+
export const userSessionExportHook: UserDataExportHook = async (ctx) => {
|
|
18
|
+
if (!featureMounted(ctx, "sessions")) return null;
|
|
19
|
+
const rows = await selectMany<Record<string, unknown>>(ctx.db, userSessionTable, {
|
|
20
|
+
userId: ctx.userId,
|
|
21
|
+
tenantId: ctx.tenantId,
|
|
22
|
+
});
|
|
23
|
+
if (rows.length === 0) return null;
|
|
24
|
+
return {
|
|
25
|
+
entity: "user-session",
|
|
26
|
+
// sid (row id) stays out of the bundle — it is the JWT jti and
|
|
27
|
+
// token-shaped, not portable personal data.
|
|
28
|
+
rows: rows.map((r) => ({
|
|
29
|
+
createdAt: String(r["createdAt"] ?? ""),
|
|
30
|
+
expiresAt: String(r["expiresAt"] ?? ""),
|
|
31
|
+
revokedAt: r["revokedAt"] ? String(r["revokedAt"]) : null,
|
|
32
|
+
ip: r["ip"] ?? null,
|
|
33
|
+
userAgent: r["userAgent"] ?? null,
|
|
34
|
+
})),
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const userSessionDeleteHook: UserDataDeleteHook = async (ctx) => {
|
|
39
|
+
// skip: sessions not mounted — its table doesn't exist, nothing to erase.
|
|
40
|
+
if (!featureMounted(ctx, "sessions")) return;
|
|
41
|
+
await deleteMany(ctx.db, userSessionTable, { userId: ctx.userId, tenantId: ctx.tenantId });
|
|
42
|
+
};
|
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
export { createUserDataRightsDefaultsFeature } from "./feature";
|
|
2
|
+
export { apiTokenDeleteHook, apiTokenExportHook } from "./hooks/api-token.userdata-hook";
|
|
3
|
+
export {
|
|
4
|
+
configValueDeleteHook,
|
|
5
|
+
configValueExportHook,
|
|
6
|
+
} from "./hooks/config-value.userdata-hook";
|
|
7
|
+
export {
|
|
8
|
+
deliveryAttemptDeleteHook,
|
|
9
|
+
deliveryAttemptExportHook,
|
|
10
|
+
} from "./hooks/delivery-attempt.userdata-hook";
|
|
2
11
|
export {
|
|
3
12
|
fileRefDeleteHook,
|
|
4
13
|
fileRefExportHook,
|
|
5
14
|
} from "./hooks/file-ref.userdata-hook";
|
|
15
|
+
export {
|
|
16
|
+
inAppMessageDeleteHook,
|
|
17
|
+
inAppMessageExportHook,
|
|
18
|
+
} from "./hooks/in-app-message.userdata-hook";
|
|
19
|
+
export { jobRunDeleteHook, jobRunExportHook } from "./hooks/job-run.userdata-hook";
|
|
20
|
+
export {
|
|
21
|
+
notificationPreferenceDeleteHook,
|
|
22
|
+
notificationPreferenceExportHook,
|
|
23
|
+
} from "./hooks/notification-preference.userdata-hook";
|
|
24
|
+
export {
|
|
25
|
+
tenantInvitationDeleteHook,
|
|
26
|
+
tenantInvitationExportHook,
|
|
27
|
+
} from "./hooks/tenant-invitation.userdata-hook";
|
|
6
28
|
export { userDeleteHook, userExportHook } from "./hooks/user.userdata-hook";
|
|
29
|
+
export {
|
|
30
|
+
userSessionDeleteHook,
|
|
31
|
+
userSessionExportHook,
|
|
32
|
+
} from "./hooks/user-session.userdata-hook";
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// dispatcht genau diese Strings).
|
|
5
5
|
|
|
6
6
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
7
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
7
8
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
8
9
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
9
10
|
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
@@ -18,7 +19,6 @@ import {
|
|
|
18
19
|
import { expectErrorIncludes, resetTestTables } from "@cosmicdrift/kumiko-framework/testing";
|
|
19
20
|
import { AuthErrors, AuthHandlers } from "../../auth-email-password/constants";
|
|
20
21
|
import { createAuthEmailPasswordFeature } from "../../auth-email-password/feature";
|
|
21
|
-
import { hashPassword } from "../../auth-email-password/password-hashing";
|
|
22
22
|
import {
|
|
23
23
|
createComplianceProfilesFeature,
|
|
24
24
|
tenantComplianceProfileEntity,
|
|
@@ -27,7 +27,9 @@ import {
|
|
|
27
27
|
import { createConfigFeature } from "../../config";
|
|
28
28
|
import { configValuesTable } from "../../config/table";
|
|
29
29
|
import { createDataRetentionFeature } from "../../data-retention";
|
|
30
|
+
import { createFilesFeature } from "../../files";
|
|
30
31
|
import { createSessionsFeature } from "../../sessions";
|
|
32
|
+
import { hashPassword } from "../../shared";
|
|
31
33
|
import { createTenantFeature } from "../../tenant";
|
|
32
34
|
import { tenantMembershipsTable } from "../../tenant/membership-table";
|
|
33
35
|
import { tenantEntity } from "../../tenant/schema/tenant";
|
|
@@ -36,6 +38,7 @@ import { UserErrors, UserHandlers, UserQueries } from "../../user";
|
|
|
36
38
|
import { createUserFeature } from "../../user/feature";
|
|
37
39
|
import { userEntity, userTable } from "../../user/schema/user";
|
|
38
40
|
import { createUserDataRightsFeature } from "../../user-data-rights";
|
|
41
|
+
import { createUserDataRightsDefaultsFeature } from "../../user-data-rights-defaults";
|
|
39
42
|
import {
|
|
40
43
|
UserDataRightsHandlers,
|
|
41
44
|
UserProfileErrors,
|
|
@@ -58,8 +61,11 @@ beforeAll(async () => {
|
|
|
58
61
|
createAuthEmailPasswordFeature(),
|
|
59
62
|
createDataRetentionFeature(),
|
|
60
63
|
createComplianceProfilesFeature(),
|
|
64
|
+
authFoundationFeature,
|
|
61
65
|
createSessionsFeature(),
|
|
66
|
+
createFilesFeature(),
|
|
62
67
|
createUserDataRightsFeature(),
|
|
68
|
+
createUserDataRightsDefaultsFeature(),
|
|
63
69
|
createUserProfileFeature(),
|
|
64
70
|
],
|
|
65
71
|
authConfig: {
|
|
@@ -37,6 +37,7 @@ function makeDispatcher(me: Record<string, unknown>): Dispatcher {
|
|
|
37
37
|
query: (async () => ({ isSuccess: true, data: me })) as unknown as Dispatcher["query"],
|
|
38
38
|
batch: (async () => ({ isSuccess: true, results: [] })) as unknown as Dispatcher["batch"],
|
|
39
39
|
statusStore,
|
|
40
|
+
async *stream() {},
|
|
40
41
|
pendingWrites: () => [],
|
|
41
42
|
pendingFiles: () => [],
|
|
42
43
|
} as unknown as Dispatcher; // @cast-boundary test-stub
|
|
@@ -76,6 +77,10 @@ describe("ProfileScreen", () => {
|
|
|
76
77
|
expect(view.getByTestId("profile-email")).toBeTruthy();
|
|
77
78
|
expect(view.getByTestId("profile-password")).toBeTruthy();
|
|
78
79
|
expect(view.getByTestId("profile-danger")).toBeTruthy();
|
|
80
|
+
// 607/4: page heading is <h1> (Heading variant="page") — the danger-zone
|
|
81
|
+
// title must be <h2>, not Card's default <h3>, so the levels don't skip.
|
|
82
|
+
expect(view.getByTestId("profile-danger").querySelector("h2")).not.toBeNull();
|
|
83
|
+
expect(view.getByTestId("profile-danger").querySelector("h3")).toBeNull();
|
|
79
84
|
expect(view.getByTestId("profile-email-current").textContent).toContain("marc@example.com");
|
|
80
85
|
expect(view.getByTestId("profile-danger-delete")).toBeTruthy();
|
|
81
86
|
// Echte i18n: kein einziger roher Key im sichtbaren Text.
|
|
@@ -11,7 +11,9 @@ export function createUserProfileFeature(): FeatureDefinition {
|
|
|
11
11
|
"change-email and account deletion (user-data-rights request/cancel with " +
|
|
12
12
|
"grace period) into one screen. Apps register the screen as " +
|
|
13
13
|
'`type: "custom"` with `__component: "UserProfileScreen"`. Requires `user`, ' +
|
|
14
|
-
"`auth-email-password`, and `user-data-rights
|
|
14
|
+
"`auth-email-password`, `user-data-rights`, and `user-data-rights-defaults` " +
|
|
15
|
+
"(so the GDPR boot-validator finds export/delete hooks for `user`'s PII " +
|
|
16
|
+
"fields once user-data-rights is mounted).",
|
|
15
17
|
);
|
|
16
18
|
r.uiHints({
|
|
17
19
|
displayLabel: "User Profile · Self-Service",
|
|
@@ -21,6 +23,7 @@ export function createUserProfileFeature(): FeatureDefinition {
|
|
|
21
23
|
r.requires("user");
|
|
22
24
|
r.requires("auth-email-password");
|
|
23
25
|
r.requires("user-data-rights");
|
|
26
|
+
r.requires("user-data-rights-defaults");
|
|
24
27
|
|
|
25
28
|
const handlers = {
|
|
26
29
|
changeEmail: r.writeHandler(changeEmailWrite),
|