@cosmicdrift/kumiko-bundled-features 0.386.0 → 0.388.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/LICENSE +21 -57
- package/dist/agent-tools/tool-catalog.js +1 -1
- package/dist/agent-tools/tool-dispatch.d.ts +7 -7
- package/dist/agent-tools/tool-dispatch.js +9 -9
- package/dist/audit/handlers/audit-events.js +1 -1
- package/dist/audit/screens.js +1 -1
- package/dist/auth-email-password/feature.js +1 -1
- package/dist/auth-email-password/handlers/change-password.write.js +3 -3
- package/dist/auth-email-password/handlers/confirm-token-flow.js +5 -5
- package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +1 -1
- package/dist/auth-email-password/handlers/invite-accept.write.js +18 -18
- package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
- package/dist/auth-email-password/handlers/invite-create.write.js +10 -10
- package/dist/auth-email-password/handlers/invite-info.query.js +1 -1
- package/dist/auth-email-password/handlers/invite-signup-complete.write.js +24 -26
- package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/login.write.js +1 -1
- package/dist/auth-email-password/handlers/self-registration-status.query.js +1 -1
- package/dist/auth-email-password/handlers/signup-confirm.write.js +34 -37
- package/dist/auth-email-password/handlers/signup-request.write.js +6 -7
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.js +1 -4
- package/dist/auth-email-password/index.js +5 -5
- package/dist/auth-email-password/lockout-store.js +1 -1
- package/dist/auth-email-password/seeding.d.ts +42 -45
- package/dist/auth-email-password/seeding.js +51 -53
- package/dist/auth-email-password/signup-token-store.js +8 -8
- package/dist/auth-email-password/testing.js +3 -3
- package/dist/auth-email-password/web/auth-client.d.ts +2 -2
- package/dist/auth-email-password/web/auth-client.js +36 -37
- package/dist/auth-email-password/web/auth-form-primitives.js +10 -10
- package/dist/auth-email-password/web/auth-gate.js +15 -16
- package/dist/auth-email-password/web/client-plugin.d.ts +7 -7
- package/dist/auth-email-password/web/client-plugin.js +4 -5
- package/dist/auth-email-password/web/confirm-account-unlock-screen.js +1 -1
- package/dist/auth-email-password/web/constants.js +3 -3
- package/dist/auth-email-password/web/default-topbar-actions.d.ts +8 -8
- package/dist/auth-email-password/web/default-topbar-actions.js +6 -6
- package/dist/auth-email-password/web/forgot-password-screen.d.ts +2 -2
- package/dist/auth-email-password/web/forgot-password-screen.js +9 -9
- package/dist/auth-email-password/web/index.js +5 -5
- package/dist/auth-email-password/web/invite-accept-screen.d.ts +1 -1
- package/dist/auth-email-password/web/invite-accept-screen.js +16 -16
- package/dist/auth-email-password/web/login-screen.d.ts +1 -1
- package/dist/auth-email-password/web/login-screen.js +2 -3
- package/dist/auth-email-password/web/reset-password-screen.d.ts +4 -4
- package/dist/auth-email-password/web/reset-password-screen.js +12 -13
- package/dist/auth-email-password/web/session.d.ts +10 -9
- package/dist/auth-email-password/web/session.js +28 -29
- package/dist/auth-email-password/web/signup-complete-screen.js +3 -3
- package/dist/auth-email-password/web/signup-screen.d.ts +1 -1
- package/dist/auth-email-password/web/signup-screen.js +13 -13
- package/dist/auth-email-password/web/tenant-switcher.d.ts +4 -4
- package/dist/auth-email-password/web/tenant-switcher.js +21 -22
- package/dist/auth-email-password/web/use-shell-user.js +12 -13
- package/dist/auth-email-password/web/user-menu.d.ts +4 -4
- package/dist/auth-email-password/web/user-menu.js +12 -13
- package/dist/auth-email-password/web/verify-email-screen.d.ts +2 -2
- package/dist/auth-email-password/web/verify-email-screen.js +9 -10
- package/dist/auth-foundation/boot-checks.js +4 -4
- package/dist/auth-foundation/resolve-anonymous-access.d.ts +1 -1
- package/dist/auth-foundation/resolve-anonymous-access.js +3 -3
- package/dist/auth-foundation/types.js +2 -2
- package/dist/auth-mfa/feature-exports.d.ts +2 -2
- package/dist/auth-mfa/feature.js +8 -0
- package/dist/auth-mfa/handlers/disable.write.js +3 -3
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +4 -4
- package/dist/auth-mfa/handlers/enable-confirm.write.js +2 -2
- package/dist/auth-mfa/handlers/enable-start-preauth.write.js +1 -1
- package/dist/auth-mfa/handlers/reencrypt.job.js +1 -1
- package/dist/auth-mfa/handlers/regenerate-recovery.write.js +5 -5
- package/dist/auth-mfa/handlers/verify.write.js +1 -1
- package/dist/auth-mfa/token-secrets.js +1 -1
- package/dist/auth-mfa/web/client-plugin.d.ts +1 -1
- package/dist/auth-mfa/web/client-plugin.js +1 -1
- package/dist/auth-mfa/web/i18n.js +8 -4
- package/dist/auth-mfa/web/index.js +4 -4
- package/dist/auth-mfa/web/mfa-client.js +17 -16
- package/dist/auth-mfa/web/mfa-code-or-recovery-field.d.ts +11 -0
- package/dist/auth-mfa/web/mfa-code-or-recovery-field.js +12 -0
- package/dist/auth-mfa/web/mfa-disable-dialog.js +14 -3
- package/dist/auth-mfa/web/mfa-regenerate-recovery-dialog.js +14 -3
- package/dist/auth-mfa/web/mfa-setup-preauth-screen.js +10 -5
- package/dist/auth-mfa/web/mfa-verify-screen.js +32 -11
- package/dist/billing-foundation/aggregate-id.d.ts +5 -5
- package/dist/billing-foundation/aggregate-id.js +10 -11
- package/dist/billing-foundation/checkout-core.d.ts +9 -14
- package/dist/billing-foundation/checkout-core.js +24 -39
- package/dist/billing-foundation/consumer-protection/confirmation-mail.js +8 -2
- package/dist/billing-foundation/consumer-protection/consent-gate.d.ts +1 -0
- package/dist/billing-foundation/consumer-protection/consent-gate.js +1 -0
- package/dist/billing-foundation/consumer-protection/issue-confirmation.js +9 -3
- package/dist/billing-foundation/consumer-protection/mail-notifications.js +3 -1
- package/dist/billing-foundation/consumer-protection/termination-receipt-limit.d.ts +4 -2
- package/dist/billing-foundation/consumer-protection/termination-receipt-limit.js +6 -5
- package/dist/billing-foundation/consumer-protection/termination-record.d.ts +5 -1
- package/dist/billing-foundation/consumer-protection/termination-record.js +27 -17
- package/dist/billing-foundation/consumer-protection/termination-request.js +12 -7
- package/dist/billing-foundation/consumer-protection/termination-revoke.js +12 -7
- package/dist/billing-foundation/consumer-protection/termination-texts.d.ts +1 -1
- package/dist/billing-foundation/consumer-protection/termination-texts.js +3 -0
- package/dist/billing-foundation/consumer-protection/withdrawal-period.d.ts +5 -2
- package/dist/billing-foundation/consumer-protection/withdrawal-period.js +8 -9
- package/dist/billing-foundation/entities.js +27 -27
- package/dist/billing-foundation/events.d.ts +1 -0
- package/dist/billing-foundation/events.js +19 -17
- package/dist/billing-foundation/feature.js +45 -44
- package/dist/billing-foundation/get-subscription-for-tenant.d.ts +3 -3
- package/dist/billing-foundation/get-subscription-for-tenant.js +3 -5
- package/dist/billing-foundation/handlers/billing-plans.query.js +35 -5
- package/dist/billing-foundation/handlers/create-checkout-session.write.js +19 -17
- package/dist/billing-foundation/handlers/create-portal-session.write.js +11 -12
- package/dist/billing-foundation/handlers/list-subscriptions.query.js +4 -4
- package/dist/billing-foundation/handlers/plan-switch-preview.query.d.ts +13 -0
- package/dist/billing-foundation/handlers/plan-switch-preview.query.js +48 -0
- package/dist/billing-foundation/handlers/process-event.write.d.ts +6 -0
- package/dist/billing-foundation/handlers/process-event.write.js +28 -22
- package/dist/billing-foundation/handlers/start-plan-checkout.write.js +32 -5
- package/dist/billing-foundation/handlers/switch-plan.write.d.ts +2 -2
- package/dist/billing-foundation/handlers/switch-plan.write.js +56 -64
- package/dist/billing-foundation/handlers/sync-subscription.write.js +12 -1
- package/dist/billing-foundation/i18n.js +22 -0
- package/dist/billing-foundation/index.d.ts +1 -1
- package/dist/billing-foundation/plan-catalog.d.ts +21 -5
- package/dist/billing-foundation/plan-catalog.js +25 -16
- package/dist/billing-foundation/plan-switch.d.ts +12 -0
- package/dist/billing-foundation/plan-switch.js +60 -0
- package/dist/billing-foundation/projection.d.ts +15 -11
- package/dist/billing-foundation/projection.js +38 -37
- package/dist/billing-foundation/subscription-tier-sync.js +3 -3
- package/dist/billing-foundation/tenant-destroy-hook.d.ts +2 -2
- package/dist/billing-foundation/tenant-destroy-hook.js +7 -7
- package/dist/billing-foundation/types.d.ts +97 -42
- package/dist/billing-foundation/types.js +29 -20
- package/dist/billing-foundation/validate-options.js +17 -0
- package/dist/billing-foundation/web/billing-plans-panel.js +82 -36
- package/dist/billing-foundation/web/checkout-consent-dialog.d.ts +17 -3
- package/dist/billing-foundation/web/checkout-consent-dialog.js +14 -10
- package/dist/billing-foundation/web/constants.d.ts +4 -0
- package/dist/billing-foundation/web/constants.js +4 -0
- package/dist/billing-foundation/web/plan-price.d.ts +15 -0
- package/dist/billing-foundation/web/plan-price.js +32 -0
- package/dist/billing-foundation/web/plan-switch-confirm-dialog.d.ts +15 -0
- package/dist/billing-foundation/web/plan-switch-confirm-dialog.js +66 -0
- package/dist/cap-counter/aggregate-id.js +8 -9
- package/dist/cap-counter/cap-reservation.js +5 -1
- package/dist/cap-counter/constants.js +11 -12
- package/dist/cap-counter/enforce-cap.d.ts +59 -62
- package/dist/cap-counter/enforce-cap.js +49 -49
- package/dist/cap-counter/entity.js +29 -29
- package/dist/cap-counter/feature.js +34 -35
- package/dist/cap-counter/handlers/increment-rolling.write.d.ts +2 -2
- package/dist/cap-counter/handlers/increment-rolling.write.js +31 -31
- package/dist/cap-counter/handlers/mark-soft-warned.write.js +4 -5
- package/dist/cap-counter/stock-cap-guard.js +8 -2
- package/dist/cap-counter/with-cap-enforcement.d.ts +3 -5
- package/dist/channel-email/feature.js +1 -1
- package/dist/channel-email/smtp-transport.d.ts +18 -17
- package/dist/channel-email/smtp-transport.js +24 -21
- package/dist/channel-in-app/handlers/unread-count.query.js +3 -4
- package/dist/compliance-profiles/feature.js +4 -4
- package/dist/compliance-profiles/handlers/for-tenant.query.js +3 -6
- package/dist/compliance-profiles/handlers/list-profiles.query.js +5 -5
- package/dist/compliance-profiles/handlers/needs-profile.query.js +4 -4
- package/dist/compliance-profiles/handlers/set-profile.write.js +13 -14
- package/dist/compliance-profiles/handlers/sub-processors.query.js +9 -11
- package/dist/compliance-profiles/resolve-for-tenant.js +7 -7
- package/dist/compliance-profiles/schema/profile-selection.js +18 -23
- package/dist/compliance-profiles/seeding.js +14 -15
- package/dist/compliance-profiles-ops/handlers/tenants-missing-profile.query.js +3 -3
- package/dist/compliance-profiles-ops/index.js +2 -2
- package/dist/config/handlers/cascade.query.d.ts +5 -0
- package/dist/config/handlers/cascade.query.js +4 -3
- package/dist/config/handlers/readiness.query.d.ts +4 -4
- package/dist/config/handlers/readiness.query.js +1 -1
- package/dist/config/handlers/reencrypt.job.js +4 -5
- package/dist/config/handlers/set.write.js +4 -4
- package/dist/config/handlers/values.query.d.ts +5 -0
- package/dist/config/handlers/values.query.js +4 -3
- package/dist/config/resolver.js +4 -4
- package/dist/config/table.js +10 -10
- package/dist/config/write-helpers.d.ts +1 -0
- package/dist/config/write-helpers.js +19 -10
- package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +2 -2
- package/dist/crypto-shredding/handlers/forget-subject.write.js +25 -21
- package/dist/custom-fields/aggregate-id.d.ts +12 -13
- package/dist/custom-fields/aggregate-id.js +16 -17
- package/dist/custom-fields/entity.js +49 -47
- package/dist/custom-fields/events.js +7 -8
- package/dist/custom-fields/feature.d.ts +14 -14
- package/dist/custom-fields/feature.js +53 -54
- package/dist/custom-fields/handlers/clear-custom-field.write.d.ts +3 -2
- package/dist/custom-fields/handlers/clear-custom-field.write.js +9 -8
- package/dist/custom-fields/handlers/define-tenant-field.write.d.ts +1 -1
- package/dist/custom-fields/handlers/define-tenant-field.write.js +2 -2
- package/dist/custom-fields/handlers/delete-system-field.write.js +6 -7
- package/dist/custom-fields/handlers/delete-tenant-field.write.d.ts +1 -1
- package/dist/custom-fields/handlers/delete-tenant-field.write.js +3 -3
- package/dist/custom-fields/handlers/set-custom-field.write.d.ts +3 -3
- package/dist/custom-fields/handlers/set-custom-field.write.js +25 -28
- package/dist/custom-fields/handlers/update-tenant-field.write.d.ts +1 -1
- package/dist/custom-fields/handlers/update-tenant-field.write.js +1 -1
- package/dist/custom-fields/lib/field-access.js +2 -2
- package/dist/custom-fields/lib/parse-serialized-field.js +1 -1
- package/dist/custom-fields/lib/quota.js +1 -1
- package/dist/custom-fields/lib/value-schema.js +10 -12
- package/dist/custom-fields/run-retention.js +3 -3
- package/dist/custom-fields/schemas.js +14 -14
- package/dist/custom-fields/web/client-plugin.js +2 -5
- package/dist/custom-fields/web/constants.js +2 -2
- package/dist/custom-fields/web/custom-fields-form-section.d.ts +4 -4
- package/dist/custom-fields/web/custom-fields-form-section.js +22 -22
- package/dist/custom-fields/wire-for-entity.js +65 -67
- package/dist/custom-fields/wire-user-data-rights.js +3 -3
- package/dist/data-retention/_internal/parse-override.js +2 -3
- package/dist/data-retention/feature.js +11 -18
- package/dist/data-retention/handlers/policy-for.query.js +6 -6
- package/dist/data-retention/keep-for.d.ts +7 -7
- package/dist/data-retention/keep-for.js +15 -15
- package/dist/data-retention/override-schema.d.ts +3 -4
- package/dist/data-retention/override-schema.js +13 -15
- package/dist/data-retention/presets.d.ts +4 -5
- package/dist/data-retention/presets.js +18 -23
- package/dist/data-retention/resolve-tenant-preset.js +17 -16
- package/dist/data-retention/resolver.d.ts +12 -12
- package/dist/data-retention/resolver.js +24 -23
- package/dist/data-retention/run-retention-cleanup.d.ts +8 -3
- package/dist/data-retention/run-retention-cleanup.js +61 -51
- package/dist/data-retention/schema/tenant-retention-override.js +13 -11
- package/dist/delivery/channel-context.js +6 -3
- package/dist/delivery/db/queries/preferences.js +2 -2
- package/dist/delivery/delivery-service.js +22 -7
- package/dist/delivery/feature.js +2 -2
- package/dist/delivery/handlers/log.query.js +1 -1
- package/dist/delivery/handlers/preferences.query.d.ts +1 -1
- package/dist/delivery/handlers/preferences.query.js +20 -1
- package/dist/delivery/handlers/unsubscribe-page-chrome.query.js +1 -1
- package/dist/delivery/tables.js +9 -8
- package/dist/delivery/unsubscribe-link.d.ts +3 -1
- package/dist/delivery/unsubscribe-page-strings.d.ts +1 -0
- package/dist/delivery/unsubscribe-page-strings.js +1 -0
- package/dist/delivery/unsubscribe-page.d.ts +2 -0
- package/dist/delivery/unsubscribe-page.js +5 -0
- package/dist/delivery/unsubscribe.js +29 -11
- package/dist/document-ingest-foundation/entity.js +4 -4
- package/dist/document-ingest-foundation/events.js +2 -2
- package/dist/document-ingest-foundation/feature.js +2 -2
- package/dist/document-ingest-foundation/pages.js +1 -1
- package/dist/document-ingest-foundation/tenant-destroy-hook.js +4 -4
- package/dist/feature-toggles/compose-tier-resolver.js +1 -1
- package/dist/feature-toggles/feature.js +1 -1
- package/dist/feature-toggles/global-feature-state-table.js +1 -1
- package/dist/feature-toggles/handlers/registered.query.js +5 -5
- package/dist/feature-toggles/toggle-sync-signal.js +1 -1
- package/dist/feature-toggles/web/constants.js +1 -1
- package/dist/file-derivatives/feature.d.ts +1 -1
- package/dist/file-derivatives/feature.js +1 -1
- package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
- package/dist/file-derivatives/presets.js +1 -1
- package/dist/file-foundation/feature.js +6 -6
- package/dist/file-foundation/index.d.ts +1 -1
- package/dist/file-foundation/index.js +1 -1
- package/dist/file-foundation/read-file-ref-content.d.ts +3 -0
- package/dist/file-foundation/read-file-ref-content.js +6 -1
- package/dist/file-provider-inmemory/feature.d.ts +1 -1
- package/dist/file-provider-inmemory/feature.js +16 -16
- package/dist/file-provider-s3/env-named-feature.d.ts +0 -2
- package/dist/file-provider-s3/feature.js +16 -16
- package/dist/file-provider-s3/lib/s3-provider.js +6 -6
- package/dist/files/feature.js +6 -6
- package/dist/files-tenant-data/handlers/sweep-orphaned-derivatives.job.js +4 -4
- package/dist/files-tenant-data/hooks.js +24 -26
- package/dist/files-tenant-data/index.js +1 -1
- package/dist/folders/feature.d.ts +1 -1
- package/dist/folders/web/folder-manager.js +2 -2
- package/dist/folders/web/tree.js +5 -5
- package/dist/form-draft/db/queries/cleanup.js +3 -3
- package/dist/form-draft/db/queries/owned-file-refs.js +1 -1
- package/dist/form-draft/decrypt-draft.js +5 -1
- package/dist/form-draft/entity.js +3 -3
- package/dist/form-draft/feature.js +4 -5
- package/dist/form-draft/handlers/discard.write.js +1 -1
- package/dist/form-draft/release-file-refs.js +6 -6
- package/dist/form-draft/schemas.js +3 -3
- package/dist/form-draft/web/constants.js +0 -2
- package/dist/host-routing/dispatch.js +11 -20
- package/dist/inbound-mail-foundation/aggregate-id.d.ts +13 -13
- package/dist/inbound-mail-foundation/aggregate-id.js +19 -19
- package/dist/inbound-mail-foundation/connect-routes.js +4 -3
- package/dist/inbound-mail-foundation/constants.d.ts +22 -24
- package/dist/inbound-mail-foundation/constants.js +30 -33
- package/dist/inbound-mail-foundation/entities.d.ts +12 -12
- package/dist/inbound-mail-foundation/entities.js +47 -48
- package/dist/inbound-mail-foundation/events.d.ts +4 -3
- package/dist/inbound-mail-foundation/events.js +50 -47
- package/dist/inbound-mail-foundation/feature.js +39 -39
- package/dist/inbound-mail-foundation/handlers/account-state.js +8 -8
- package/dist/inbound-mail-foundation/handlers/connect-account.write.js +25 -26
- package/dist/inbound-mail-foundation/handlers/disconnect-account.write.js +7 -7
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +50 -51
- package/dist/inbound-mail-foundation/handlers/list-accounts.query.js +8 -8
- package/dist/inbound-mail-foundation/handlers/list-messages.query.js +10 -10
- package/dist/inbound-mail-foundation/handlers/scope-visibility.js +3 -4
- package/dist/inbound-mail-foundation/handlers/update-account.write.d.ts +2 -2
- package/dist/inbound-mail-foundation/handlers/update-account.write.js +22 -14
- package/dist/inbound-mail-foundation/i18n.js +0 -1
- package/dist/inbound-mail-foundation/oauth-state.d.ts +5 -5
- package/dist/inbound-mail-foundation/oauth-state.js +11 -10
- package/dist/inbound-mail-foundation/projection.d.ts +4 -4
- package/dist/inbound-mail-foundation/projection.js +31 -29
- package/dist/inbound-mail-foundation/provider-factory.d.ts +4 -4
- package/dist/inbound-mail-foundation/provider-factory.js +9 -9
- package/dist/inbound-mail-foundation/retention-sweep.d.ts +5 -3
- package/dist/inbound-mail-foundation/retention-sweep.js +38 -36
- package/dist/inbound-mail-foundation/screens.js +0 -3
- package/dist/inbound-mail-foundation/tenant-destroy-hook.js +14 -16
- package/dist/inbound-mail-foundation/types.d.ts +49 -49
- package/dist/inbound-mail-foundation/types.js +21 -21
- package/dist/inbound-mail-foundation/watch-supervisor.d.ts +10 -10
- package/dist/inbound-mail-foundation/watch-supervisor.js +85 -67
- package/dist/inbound-provider-imap/credential-document.js +13 -13
- package/dist/inbound-provider-imap/feature.d.ts +2 -2
- package/dist/inbound-provider-imap/feature.js +42 -40
- package/dist/inbound-provider-imap/imap-client.d.ts +12 -11
- package/dist/inbound-provider-imap/imap-client.js +20 -19
- package/dist/inbound-provider-inmemory/feature.d.ts +9 -9
- package/dist/inbound-provider-inmemory/feature.js +19 -19
- package/dist/jobs/db/queries/retention.js +2 -2
- package/dist/jobs/db/queries/stale-run-sweep.js +3 -3
- package/dist/jobs/events.js +1 -1
- package/dist/jobs/feature.d.ts +1 -0
- package/dist/jobs/feature.js +13 -10
- package/dist/jobs/handlers/detail.query.js +4 -4
- package/dist/jobs/handlers/list.query.js +1 -1
- package/dist/jobs/handlers/projection-rebuild.job.js +1 -1
- package/dist/jobs/handlers/reindex-entity.job.js +1 -1
- package/dist/jobs/handlers/retry.write.js +1 -1
- package/dist/jobs/handlers/tenant-failures.query.d.ts +1 -1
- package/dist/jobs/handlers/tenant-failures.query.js +45 -41
- package/dist/jobs/handlers/tenant-runs.query.d.ts +1 -1
- package/dist/jobs/handlers/tenant-runs.query.js +54 -50
- package/dist/jobs/job-instant.js +1 -1
- package/dist/jobs/job-run-logger.js +15 -15
- package/dist/jobs/job-run-table.js +3 -3
- package/dist/jobs/screens.js +1 -1
- package/dist/jobs/tenant-job-failure-table.js +1 -1
- package/dist/jobs/tenant-job-run-table.js +1 -1
- package/dist/ledger/entity.js +5 -5
- package/dist/ledger/feature.js +3 -3
- package/dist/ledger/handlers/confirm-schedule-period.write.js +3 -3
- package/dist/ledger/recurring.js +12 -12
- package/dist/ledger/schemas.js +1 -1
- package/dist/ledger/web/constants.js +2 -2
- package/dist/ledger/web/index.js +7 -7
- package/dist/legal-pages/feature.d.ts +4 -4
- package/dist/legal-pages/feature.js +25 -26
- package/dist/legal-pages/markdown.js +3 -3
- package/dist/legal-pages/web/client-plugin.js +23 -24
- package/dist/legal-pages/web/index.js +0 -1
- package/dist/mail-foundation/feature.d.ts +5 -5
- package/dist/mail-foundation/feature.js +27 -25
- package/dist/mail-transport-inmemory/feature.d.ts +4 -4
- package/dist/mail-transport-inmemory/feature.js +27 -28
- package/dist/mail-transport-smtp/feature.js +20 -20
- package/dist/managed-pages/branding.js +6 -6
- package/dist/managed-pages/feature.d.ts +26 -26
- package/dist/managed-pages/feature.js +34 -34
- package/dist/managed-pages/handlers/by-slug.query.js +5 -6
- package/dist/managed-pages/handlers/by-tenant-published.query.js +2 -2
- package/dist/managed-pages/handlers/set.write.js +5 -7
- package/dist/managed-pages/screens/page-screens.js +11 -11
- package/dist/managed-pages/seeding.js +8 -8
- package/dist/managed-pages/table.js +13 -12
- package/dist/managed-pages/web/client-plugin.d.ts +1 -1
- package/dist/managed-pages/web/client-plugin.js +5 -5
- package/dist/managed-pages/web/i18n.js +1 -1
- package/dist/notes-history/entity.js +4 -4
- package/dist/notes-history/feature.d.ts +2 -2
- package/dist/notes-history-user-data/hooks.js +1 -1
- package/dist/page-render/layout.js +9 -9
- package/dist/page-render/markdown.js +11 -11
- package/dist/page-render/security-headers.js +7 -6
- package/dist/personal-access-tokens/feature.d.ts +1 -1
- package/dist/personal-access-tokens/feature.js +9 -5
- package/dist/personal-access-tokens/roles.js +2 -2
- package/dist/readiness/handlers/status.query.js +3 -3
- package/dist/renderer-foundation/api.js +12 -13
- package/dist/renderer-foundation/constants.js +2 -2
- package/dist/renderer-foundation/feature.js +7 -7
- package/dist/renderer-pdf/render-table-pdf.d.ts +1 -1
- package/dist/renderer-pdf/render-table-pdf.js +22 -10
- package/dist/secrets/feature.js +9 -9
- package/dist/secrets/web/constants.js +2 -2
- package/dist/seo/feature.js +3 -3
- package/dist/sessions/constants.d.ts +1 -1
- package/dist/sessions/constants.js +1 -1
- package/dist/sessions/feature.d.ts +2 -2
- package/dist/sessions/feature.js +12 -8
- package/dist/sessions/handlers/revoke-all-for-user.write.js +2 -2
- package/dist/sessions/handlers/revoke-all-others.write.js +2 -2
- package/dist/sessions/handlers/revoke.write.js +1 -1
- package/dist/sessions/schema/user-session.js +4 -4
- package/dist/sessions/screens.js +2 -2
- package/dist/sessions/session-callbacks.js +2 -2
- package/dist/sessions/session-revoked-event.js +2 -2
- package/dist/shared/assert-no-where-rule-in-ownership-write.js +1 -1
- package/dist/shared/decrypt-stored-pii.d.ts +1 -0
- package/dist/shared/decrypt-stored-pii.js +7 -2
- package/dist/shared/encrypt-for-direct-write.js +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/mail-template-body.d.ts +6 -2
- package/dist/shared/mail-template-body.js +7 -4
- package/dist/shared/map-with-concurrency.js +5 -4
- package/dist/shared/parent-visibility.js +2 -2
- package/dist/shared/password-hashing.js +1 -1
- package/dist/shared/signup-handover.js +1 -2
- package/dist/shared/single-use-token-store.js +1 -1
- package/dist/step-dispatcher/feature.js +1 -0
- package/dist/subscription-dodo/client.d.ts +17 -0
- package/dist/subscription-dodo/client.js +31 -0
- package/dist/subscription-dodo/constants.d.ts +12 -0
- package/dist/subscription-dodo/constants.js +19 -0
- package/dist/subscription-dodo/feature.d.ts +37 -0
- package/dist/subscription-dodo/feature.js +144 -0
- package/dist/subscription-dodo/index.d.ts +2 -0
- package/dist/subscription-dodo/index.js +4 -0
- package/dist/subscription-dodo/plugin-methods.d.ts +23 -0
- package/dist/subscription-dodo/plugin-methods.js +260 -0
- package/dist/subscription-dodo/runtime.d.ts +27 -0
- package/dist/subscription-dodo/runtime.js +89 -0
- package/dist/subscription-dodo/verify-webhook.d.ts +37 -0
- package/dist/subscription-dodo/verify-webhook.js +189 -0
- package/dist/subscription-mollie/constants.js +15 -17
- package/dist/subscription-mollie/feature.d.ts +17 -18
- package/dist/subscription-mollie/feature.js +24 -24
- package/dist/subscription-mollie/index.js +3 -3
- package/dist/subscription-mollie/plugin-methods.d.ts +2 -2
- package/dist/subscription-mollie/plugin-methods.js +15 -14
- package/dist/subscription-mollie/verify-webhook.d.ts +10 -10
- package/dist/subscription-mollie/verify-webhook.js +27 -27
- package/dist/subscription-stripe/constants.js +4 -6
- package/dist/subscription-stripe/feature.d.ts +14 -14
- package/dist/subscription-stripe/feature.js +36 -34
- package/dist/subscription-stripe/index.js +6 -6
- package/dist/subscription-stripe/plugin-methods.js +18 -18
- package/dist/subscription-stripe/runtime.d.ts +1 -1
- package/dist/subscription-stripe/runtime.js +8 -7
- package/dist/subscription-stripe/verify-webhook.d.ts +11 -11
- package/dist/subscription-stripe/verify-webhook.js +59 -61
- package/dist/tags/feature.d.ts +3 -3
- package/dist/tags/screens.js +1 -1
- package/dist/template-resolver/api.d.ts +3 -2
- package/dist/template-resolver/api.js +34 -12
- package/dist/template-resolver/compile-document-body.js +1 -0
- package/dist/template-resolver/feature.js +2 -2
- package/dist/template-resolver/handlers/bundle-import.js +3 -3
- package/dist/template-resolver/handlers/copy-locale.write.d.ts +5 -1
- package/dist/template-resolver/handlers/copy-locale.write.js +22 -7
- package/dist/template-resolver/handlers/diff-to-system.query.js +3 -2
- package/dist/template-resolver/handlers/document-base.js +3 -2
- package/dist/template-resolver/handlers/find-by-id.query.js +8 -7
- package/dist/template-resolver/handlers/list.query.js +6 -7
- package/dist/template-resolver/handlers/overview.query.d.ts +3 -0
- package/dist/template-resolver/handlers/overview.query.js +6 -3
- package/dist/template-resolver/handlers/shared.js +2 -2
- package/dist/template-resolver/handlers/template-write.js +3 -3
- package/dist/template-resolver/handlers/tenant-override-counts.query.js +1 -3
- package/dist/template-resolver/handlers/tenant-override-selection.d.ts +2 -1
- package/dist/template-resolver/handlers/tenant-overrides.query.js +28 -5
- package/dist/template-resolver/handlers/upsert-system.write.js +3 -4
- package/dist/template-resolver/handlers/upsert-tenant.write.js +4 -5
- package/dist/template-resolver/handlers/usage.query.d.ts +2 -1
- package/dist/template-resolver/handlers/usage.query.js +26 -16
- package/dist/template-resolver/handlers/validate.query.js +7 -2
- package/dist/template-resolver/seeding.js +30 -14
- package/dist/template-resolver/store-template-resource.d.ts +1 -0
- package/dist/template-resolver/store-template-resource.js +19 -4
- package/dist/template-resolver/table.js +10 -11
- package/dist/template-resolver/template-delivery-policy.d.ts +0 -5
- package/dist/template-resolver/template-delivery-policy.js +0 -6
- package/dist/template-resolver/template-meta.js +7 -4
- package/dist/template-resolver/template-preview.js +4 -3
- package/dist/template-resolver/web/client-plugin.js +2 -2
- package/dist/template-resolver/web/constants.d.ts +2 -2
- package/dist/template-resolver/web/constants.js +3 -3
- package/dist/template-resolver/web/index.d.ts +1 -0
- package/dist/template-resolver/web/index.js +1 -0
- package/dist/template-resolver/web/mail-layout-constants.d.ts +5 -0
- package/dist/template-resolver/web/mail-layout-constants.js +7 -0
- package/dist/tenant/feature.js +7 -7
- package/dist/tenant/handlers/member-directory.query.d.ts +1 -0
- package/dist/tenant/handlers/member-directory.query.js +11 -4
- package/dist/tenant/handlers/members.query.js +1 -1
- package/dist/tenant/handlers/team-list.query.js +1 -1
- package/dist/tenant/handlers/tenant-directory.query.d.ts +1 -0
- package/dist/tenant/handlers/tenant-directory.query.js +9 -5
- package/dist/tenant/invitation-table.js +37 -40
- package/dist/tenant/membership-table.js +3 -3
- package/dist/tenant/schema/tenant.js +7 -9
- package/dist/tenant/screens.js +1 -1
- package/dist/tenant/seeding.d.ts +6 -5
- package/dist/tenant/seeding.js +12 -12
- package/dist/tenant/testing.js +5 -6
- package/dist/tenant/web/constants.js +2 -2
- package/dist/tenant-handover/events.js +5 -7
- package/dist/tenant-handover/feature.js +1 -1
- package/dist/tenant-handover/grant.js +1 -1
- package/dist/tenant-handover/handlers/claim.write.js +1 -1
- package/dist/tenant-handover/move-entity-graph.js +8 -9
- package/dist/tenant-handover/signup-handover-provider.js +1 -2
- package/dist/tenant-handover/transfer-graph.js +4 -5
- package/dist/tenant-lifecycle/boot-checks.js +1 -1
- package/dist/tenant-lifecycle/constants.d.ts +1 -0
- package/dist/tenant-lifecycle/constants.js +4 -0
- package/dist/tenant-lifecycle/feature.js +1 -1
- package/dist/tenant-lifecycle/run-tenant-destroy.js +2 -2
- package/dist/tenant-settings/constants.js +1 -1
- package/dist/tenant-settings/tenant-defaults.d.ts +1 -1
- package/dist/tenant-settings/tenant-defaults.js +9 -2
- package/dist/tier-engine/aggregate-id.d.ts +11 -11
- package/dist/tier-engine/aggregate-id.js +15 -16
- package/dist/tier-engine/compose-app.d.ts +14 -14
- package/dist/tier-engine/compose-app.js +1 -1
- package/dist/tier-engine/entity.js +18 -19
- package/dist/tier-engine/feature.d.ts +19 -17
- package/dist/tier-engine/feature.js +68 -69
- package/dist/tier-engine/handlers/active-tier.query.js +6 -7
- package/dist/tier-engine/handlers/get-tenant-tier.query.js +4 -4
- package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +7 -8
- package/dist/tier-engine/handlers/set-tenant-tier.write.js +7 -4
- package/dist/tier-engine/tier-resolver.js +2 -2
- package/dist/tier-engine/trial.js +8 -8
- package/dist/tier-engine/web/constants.js +2 -2
- package/dist/tier-engine/web/index.js +4 -4
- package/dist/user/db/queries/stream-tenant-backfill.js +8 -8
- package/dist/user/handlers/create.write.js +4 -4
- package/dist/user/schema/user.d.ts +11 -11
- package/dist/user/schema/user.js +45 -44
- package/dist/user/seeding.d.ts +15 -15
- package/dist/user/seeding.js +28 -28
- package/dist/user/testing.js +3 -3
- package/dist/user-data-rights/audit-download.d.ts +8 -8
- package/dist/user-data-rights/audit-download.js +19 -20
- package/dist/user-data-rights/boot-checks.js +1 -1
- package/dist/user-data-rights/deletion-token.js +1 -1
- package/dist/user-data-rights/feature.d.ts +49 -50
- package/dist/user-data-rights/feature.js +54 -53
- package/dist/user-data-rights/handlers/cancel-deletion.write.js +9 -9
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +8 -8
- package/dist/user-data-rights/handlers/download-by-job.query.js +29 -30
- package/dist/user-data-rights/handlers/download-by-token.query.js +40 -42
- package/dist/user-data-rights/handlers/export-status.query.js +8 -9
- package/dist/user-data-rights/handlers/lift-restriction.write.js +7 -8
- package/dist/user-data-rights/handlers/list-download-attempts.query.js +2 -2
- package/dist/user-data-rights/handlers/my-audit-log.query.js +4 -4
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +5 -5
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.js +16 -15
- package/dist/user-data-rights/handlers/request-deletion.write.d.ts +2 -2
- package/dist/user-data-rights/handlers/request-deletion.write.js +7 -9
- package/dist/user-data-rights/handlers/request-export.write.js +42 -41
- package/dist/user-data-rights/handlers/restrict-account.write.js +22 -19
- package/dist/user-data-rights/handlers/run-forget-cleanup.write.js +8 -8
- package/dist/user-data-rights/index.js +8 -10
- package/dist/user-data-rights/lib/default-mailers.js +3 -3
- package/dist/user-data-rights/lib/update-user-lifecycle.js +14 -17
- package/dist/user-data-rights/run-export-jobs.d.ts +38 -42
- package/dist/user-data-rights/run-export-jobs.js +130 -237
- package/dist/user-data-rights/run-forget-cleanup.d.ts +16 -20
- package/dist/user-data-rights/run-forget-cleanup.js +65 -83
- package/dist/user-data-rights/run-user-export.d.ts +34 -33
- package/dist/user-data-rights/run-user-export.js +55 -57
- package/dist/user-data-rights/schema/download-attempt.js +11 -11
- package/dist/user-data-rights/schema/download-token.js +37 -41
- package/dist/user-data-rights/schema/export-job.d.ts +4 -4
- package/dist/user-data-rights/schema/export-job.js +65 -67
- package/dist/user-data-rights/token-helpers.d.ts +8 -9
- package/dist/user-data-rights/token-helpers.js +21 -25
- package/dist/user-data-rights/web/client-plugin.d.ts +5 -5
- package/dist/user-data-rights/web/client-plugin.js +1 -1
- package/dist/user-data-rights/web/confirm-deletion-screen.js +6 -5
- package/dist/user-data-rights/web/constants.js +1 -1
- package/dist/user-data-rights/web/index.js +5 -5
- package/dist/user-data-rights/web/privacy-center-screen.js +14 -13
- package/dist/user-data-rights/web/public-deletion-gate.d.ts +4 -4
- package/dist/user-data-rights/web/request-deletion-screen.js +6 -6
- package/dist/user-data-rights/zip-path.d.ts +15 -15
- package/dist/user-data-rights/zip-path.js +41 -40
- package/dist/user-data-rights-defaults/feature.js +15 -15
- package/dist/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.js +3 -5
- package/dist/user-data-rights-defaults/hooks/file-ref.userdata-hook.js +45 -51
- package/dist/user-data-rights-defaults/hooks/job-run.userdata-hook.js +2 -2
- package/dist/user-data-rights-defaults/hooks/notification-preference.userdata-hook.js +1 -1
- package/dist/user-data-rights-defaults/hooks/user-session.userdata-hook.js +3 -3
- package/dist/user-data-rights-defaults/hooks/user.userdata-hook.js +32 -31
- package/dist/user-profile/handlers/change-email.write.js +13 -13
- package/dist/user-profile/index.js +3 -3
- package/dist/user-profile/web/client-plugin.js +1 -1
- package/dist/user-profile/web/constants.js +1 -1
- package/dist/user-profile/web/profile-screen.js +1 -1
- package/dist/waitlist/handlers/export-purge.write.js +2 -1
- package/dist/waitlist/handlers/submit.write.js +5 -5
- package/dist/waitlist/import.d.ts +1 -0
- package/dist/waitlist/import.js +4 -0
- package/dist/waitlist/legacy-migrate.js +17 -6
- package/dist/waitlist/link-on-signup.js +18 -6
- package/dist/waitlist/options.d.ts +3 -0
- package/dist/workflow-runner/aggregate-id.d.ts +1 -1
- package/dist/workflow-runner/aggregate-id.js +1 -1
- package/dist/workflow-runner/event-subscriber.js +4 -5
- package/dist/workflow-runner/event-trigger.js +4 -17
- package/dist/workflow-runner/feature.js +2 -2
- package/dist/workflow-runner/handlers/resume-run.write.js +4 -4
- package/dist/workflow-runner/pending-projection.js +6 -7
- package/dist/workflow-runner/runner.js +6 -6
- package/dist/workflow-runner/tables.js +6 -6
- package/dist/workflow-runner/workflow-registry.js +1 -1
- package/package.json +20 -10
- package/src/agent-tools/changes.json +6 -0
- package/src/audit/changes.json +12 -0
- package/src/auth-mfa/changes.json +6 -0
- package/src/billing-foundation/changes.json +55 -0
- package/src/cap-counter/changes.json +18 -0
- package/src/channel-email/changes.json +6 -0
- package/src/config/changes.json +12 -0
- package/src/crypto-shredding/changes.json +6 -0
- package/src/delivery/changes.json +30 -0
- package/src/form-draft/changes.json +6 -0
- package/src/host-routing/changes.json +6 -0
- package/src/inbound-mail-foundation/changes.json +18 -0
- package/src/jobs/changes.json +13 -0
- package/src/mail-foundation/changes.json +6 -0
- package/src/renderer-pdf/changes.json +6 -0
- package/src/step-dispatcher/changes.json +6 -0
- package/src/subscription-dodo/changes.json +14 -0
- package/src/subscription-stripe/changes.json +12 -0
- package/src/template-resolver/changes.json +48 -0
- package/src/tenant/changes.json +6 -0
- package/src/tenant-lifecycle/changes.json +6 -0
- package/src/tier-engine/changes.json +6 -0
- package/src/waitlist/changes.json +18 -0
- package/dist/billing-foundation/consumer-protection/stored-user-email.d.ts +0 -1
- package/dist/billing-foundation/consumer-protection/stored-user-email.js +0 -8
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// kumiko-lint-ignore app-feature-structure
|
|
1
|
+
// kumiko-lint-ignore app-feature-structure feature.ts is over 300 lines and not yet split
|
|
2
2
|
import { UNKNOWN_CLIENT_IP } from "@cosmicdrift/kumiko-framework/api";
|
|
3
3
|
import { createSystemConfig, createSystemUser, defineFeature, EXT_USER_DATA, i18nKey, SYSTEM_USER_ID, } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
@@ -61,16 +61,16 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
61
61
|
r.envSchema(deletionTokenSecretEnvSchema);
|
|
62
62
|
r.usesApi("compliance.forTenant");
|
|
63
63
|
r.usesApi("retention.policyFor");
|
|
64
|
-
//
|
|
65
|
-
// r.usesApi
|
|
66
|
-
//
|
|
67
|
-
//
|
|
64
|
+
// restrict-account calls sessions.revokeAllForUser across features.
|
|
65
|
+
// r.usesApi enables boot validation: without the sessions feature, the app
|
|
66
|
+
// fails at boot instead of raising an opaque "handler not found" error
|
|
67
|
+
// on the first restrict call.
|
|
68
68
|
r.usesApi("sessions.revokeAllForUser");
|
|
69
|
-
// file-foundation
|
|
70
|
-
//
|
|
71
|
-
//
|
|
69
|
+
// file-foundation is a soft dependency: only the export worker needs it
|
|
70
|
+
// for storage writes. Apps that use only forget (not export) do not need
|
|
71
|
+
// to mount file-foundation.
|
|
72
72
|
r.extendsRegistrar(EXT_USER_DATA, {});
|
|
73
|
-
// GDPR-storage guards V2+V3
|
|
73
|
+
// GDPR-storage guards V2+V3 — moved off the framework-internal
|
|
74
74
|
// boot-validator onto this feature's own r.bootCheck(), since it's the
|
|
75
75
|
// EXT_USER_DATA channel owner: any r.useExtension(EXT_USER_DATA, ...)
|
|
76
76
|
// usage already requires user-data-rights to be mounted.
|
|
@@ -93,20 +93,20 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
93
93
|
options: ["single-user", "multi-user"],
|
|
94
94
|
env: "TENANT_MODEL",
|
|
95
95
|
}));
|
|
96
|
-
//
|
|
96
|
+
// ExportJob-Lifecycle-Entity.
|
|
97
97
|
r.entity({ name: "export-job", ...exportJobEntity });
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
98
|
+
// The worker generates the token when flipping the job to done (see
|
|
99
|
+
// run-export-jobs.ts). The hash is stored in the database, while the plain
|
|
100
|
+
// token is kept in RunExportJobsResult for email notification.
|
|
101
101
|
r.entity({ name: "export-download-token", ...exportDownloadTokenEntity });
|
|
102
|
-
//
|
|
102
|
+
// Audit-Trail invalid Download-Attempts (DPO Brute-Force-Detection).
|
|
103
103
|
r.entity({ name: "download-attempt", ...downloadAttemptEntity });
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
104
|
+
// GDPR Article 18 account freeze (pause processing).
|
|
105
|
+
// The endpoints restrict and lift the restriction. The login block for
|
|
106
|
+
// restricted users lives in auth-email-password/login.write.ts.
|
|
107
107
|
r.writeHandler(restrictAccountWrite);
|
|
108
108
|
r.writeHandler(liftRestrictionWrite);
|
|
109
|
-
//
|
|
109
|
+
// Endpoints for the GDPR Article 17 forget path with a grace period.
|
|
110
110
|
r.writeHandler(createRequestDeletionHandler({
|
|
111
111
|
...(opts.sendDeletionRequestedEmail && {
|
|
112
112
|
sendDeletionRequestedEmail: opts.sendDeletionRequestedEmail,
|
|
@@ -129,26 +129,26 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
129
129
|
r.writeHandler(createConfirmDeletionByTokenHandler(opts.deletionTokenSecret !== undefined
|
|
130
130
|
? { deletionTokenSecret: opts.deletionTokenSecret }
|
|
131
131
|
: {}));
|
|
132
|
-
//
|
|
133
|
-
// sendDeletionExecutedEmail
|
|
134
|
-
//
|
|
135
|
-
//
|
|
132
|
+
// Cleanup runner as a privileged handler. When
|
|
133
|
+
// sendDeletionExecutedEmail is set, the handler passes the callback to
|
|
134
|
+
// runForgetCleanup; the worker caches userEmail+tenantIds before the
|
|
135
|
+
// transaction (see run-forget-cleanup.ts).
|
|
136
136
|
r.writeHandler(createRunForgetCleanupHandler(opts.sendDeletionExecutedEmail
|
|
137
137
|
? { sendDeletionExecutedEmail: opts.sendDeletionExecutedEmail }
|
|
138
138
|
: {}));
|
|
139
139
|
r.exposesApi("userDataRights.runForget");
|
|
140
|
-
//
|
|
140
|
+
// User touchpoints for the asynchronous export pipeline.
|
|
141
141
|
r.writeHandler(requestExportWrite);
|
|
142
142
|
r.queryHandler(exportStatusQuery);
|
|
143
143
|
r.exposesApi("userDataRights.runExport");
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
// r.httpRoute
|
|
144
|
+
// Download endpoints for token and session paths.
|
|
145
|
+
// Both query handlers verify the token or session, obtain a signed URL
|
|
146
|
+
// from the storage provider, and persist audit fields on the token row.
|
|
147
|
+
// The r.httpRoute wrappers below issue the 302 redirect to signedUrl.
|
|
148
148
|
r.queryHandler(downloadByTokenQuery);
|
|
149
149
|
r.queryHandler(downloadByJobQuery);
|
|
150
|
-
//
|
|
151
|
-
//
|
|
150
|
+
// User self-access under Article 15 and operator access to invalid download
|
|
151
|
+
// attempts (DPO).
|
|
152
152
|
r.queryHandler(myAuditLogQuery);
|
|
153
153
|
r.queryHandler(listDownloadAttemptsQuery);
|
|
154
154
|
// Read-only GDPR operator inspector (SystemAdmin) — screens stay inert until an app navs them.
|
|
@@ -306,12 +306,12 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
306
306
|
// Magic-link path (anonymous): the email link carries the token as a
|
|
307
307
|
// URL fragment (`#token=<plain>`) instead of a query param — fragments
|
|
308
308
|
// never leave the browser, so they never hit proxy/access logs
|
|
309
|
-
//
|
|
309
|
+
// This route only serves the interstitial page; the
|
|
310
310
|
// external script reads the fragment client-side and exchanges the
|
|
311
311
|
// token via POST for the signed URL.
|
|
312
312
|
//
|
|
313
|
-
// No `?token=` query fallback: framework is zero-legacy
|
|
314
|
-
// live customer data / no
|
|
313
|
+
// No `?token=` query fallback: framework is zero-legacy — no
|
|
314
|
+
// live customer data / no emails in flight that would need
|
|
315
315
|
// bridging, so there's nothing a fallback would need to cover. A query
|
|
316
316
|
// param reaches the server as part of the request URL and gets written
|
|
317
317
|
// to proxy/access logs regardless of what the handler does with it
|
|
@@ -353,7 +353,7 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
353
353
|
},
|
|
354
354
|
});
|
|
355
355
|
// Exchange endpoint for the interstitial page: the token arrives here
|
|
356
|
-
// as a POST body instead of a query string
|
|
356
|
+
// as a POST body instead of a query string. A dedicated
|
|
357
357
|
// route rather than letting the browser JS call /api/query directly,
|
|
358
358
|
// for two reasons:
|
|
359
359
|
// 1. IP/UA audit — extractAuditMeta needs the raw request headers and
|
|
@@ -391,7 +391,7 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
391
391
|
// defaults to 0, ignoring headers entirely) nor a socket, so every
|
|
392
392
|
// fragment-POST download would fall to the shared "unknown" bucket,
|
|
393
393
|
// turning the per-user 30/min backstop into one global bucket an
|
|
394
|
-
// attacker can exhaust for everyone
|
|
394
|
+
// attacker can exhaust for everyone. Passing `clientIp`
|
|
395
395
|
// directly as env — the same server-resolved, trustedProxyHops-
|
|
396
396
|
// aware value the outer request already trusted — reaches
|
|
397
397
|
// extractSocketAddress() without going through header/hop-count
|
|
@@ -417,7 +417,7 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
417
417
|
unit: "seconds",
|
|
418
418
|
description: "Unix timestamp of the last export storage-cleanup pass that completed. Alert on time() minus this value exceeding the cron interval plus a buffer; the backlog-age gauge alone stays at its last value when the cron stops.",
|
|
419
419
|
});
|
|
420
|
-
//
|
|
420
|
+
// Worker for the asynchronous export pipeline, triggered by a cron job.
|
|
421
421
|
const RUN_EXPORT_JOBS_REASON = "processes pending export jobs across every tenant";
|
|
422
422
|
r.job({
|
|
423
423
|
name: "run-export-jobs",
|
|
@@ -429,9 +429,9 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
429
429
|
throw new Error("run-export-jobs: ctx.registry required (JobContext incomplete)");
|
|
430
430
|
}
|
|
431
431
|
const T = (await import("@cosmicdrift/kumiko-framework/time")).getTemporal();
|
|
432
|
-
// SYSTEM_USER_ID
|
|
433
|
-
//
|
|
434
|
-
//
|
|
432
|
+
// SYSTEM_USER_ID is the framework-wide convention. The job discriminator is
|
|
433
|
+
// recorded in the secret-read audit through
|
|
434
|
+
// handlerName="user-data-rights:run-export-jobs".
|
|
435
435
|
const exportUserId = SYSTEM_USER_ID;
|
|
436
436
|
const exportDb = ctx.db.unsafeRaw(); // @cast-boundary db-operator — jobs never run inside a DbTx
|
|
437
437
|
const exportRegistry = ctx.registry;
|
|
@@ -441,12 +441,13 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
441
441
|
const exportMetrics = ctx.meter
|
|
442
442
|
? createMetricsHandle(ctx.meter, "user-data-rights")
|
|
443
443
|
: createNoopMetricsHandle();
|
|
444
|
-
//
|
|
445
|
-
//
|
|
446
|
-
//
|
|
447
|
-
//
|
|
448
|
-
// The cron runs across tenants
|
|
449
|
-
// recipient's tenant
|
|
444
|
+
// Without explicit send*Email options, a mounted mail transport sends the
|
|
445
|
+
// export notifications itself using the default templates. The resolver
|
|
446
|
+
// builds the per-tenant transport from configResolver, bridging it in
|
|
447
|
+
// the same way as buildStorageProvider.
|
|
448
|
+
// The cron runs across tenants, so each mail needs a notify function
|
|
449
|
+
// bound to the recipient's tenant for template overrides and channel
|
|
450
|
+
// configuration.
|
|
450
451
|
const notifyFactory = ctx._notifyFactory;
|
|
451
452
|
const exportNotifyFor = notifyFactory !== undefined && isGdprMailDeliveryAvailable(exportRegistry)
|
|
452
453
|
? (tenantId) => notifyFactory(createSystemUser(tenantId), tenantId)
|
|
@@ -483,9 +484,9 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
483
484
|
handlerName: "user-data-rights:run-export-jobs",
|
|
484
485
|
}),
|
|
485
486
|
now: T.Now.instant(),
|
|
486
|
-
// App-
|
|
487
|
-
//
|
|
488
|
-
//
|
|
487
|
+
// App-author callbacks take precedence; otherwise the mail-foundation default
|
|
488
|
+
// is used. Both are optional: without a mail transport or callback,
|
|
489
|
+
// clients continue polling via export-status.query.
|
|
489
490
|
...(sendExportReadyEmail && { sendExportReadyEmail }),
|
|
490
491
|
...(sendExportFailedEmail && { sendExportFailedEmail }),
|
|
491
492
|
...(opts.appExportDownloadUrl !== undefined && {
|
|
@@ -498,11 +499,11 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
498
499
|
});
|
|
499
500
|
},
|
|
500
501
|
});
|
|
501
|
-
//
|
|
502
|
-
//
|
|
503
|
-
// userDataRights.runForget
|
|
504
|
-
//
|
|
505
|
-
//
|
|
502
|
+
// Automatic Article 17 forget cron. After the grace period, it runs
|
|
503
|
+
// runForgetCleanup without supervision; the manual
|
|
504
|
+
// userDataRights.runForget API remains available for operator runs.
|
|
505
|
+
// Without this cron, deletion requests would remain in
|
|
506
|
+
// DeletionRequested indefinitely and Article 17 erasure would never run.
|
|
506
507
|
const RUN_FORGET_CLEANUP_REASON = "executes overdue Art.17 forget requests across every tenant";
|
|
507
508
|
r.job({
|
|
508
509
|
name: "run-forget-cleanup",
|
|
@@ -568,7 +569,7 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
568
569
|
escapeHatchAuditLog: ctx.log,
|
|
569
570
|
actor: ctx.systemUser.id,
|
|
570
571
|
});
|
|
571
|
-
// Persist each incomplete report on the job-run log
|
|
572
|
+
// Persist each incomplete report on the job-run log. The write
|
|
572
573
|
// handler returns incomplete[] in its response; the cron path used to
|
|
573
574
|
// discard the array (only a console.warn for rolledBack cases).
|
|
574
575
|
// ctx.log feeds createJobRunLogger → store_job_run_logs.
|
|
@@ -5,17 +5,17 @@ import { isWithinGracePeriod } from "../../shared/index.js";
|
|
|
5
5
|
import { USER_STATUS, userTable } from "../../user/index.js";
|
|
6
6
|
import { updateUserLifecycle } from "../lib/update-user-lifecycle.js";
|
|
7
7
|
const APPEND_LIFECYCLE_EVENT_REASON = "appends the user lifecycle event on the SYSTEM_TENANT_ID user stream";
|
|
8
|
-
// POST /api/user/cancel-deletion
|
|
8
|
+
// POST /api/user/cancel-deletion.
|
|
9
9
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
10
|
+
// During the grace period, the user can withdraw a pending deletion request.
|
|
11
|
+
// This restores:
|
|
12
12
|
// - status = "active"
|
|
13
13
|
// - gracePeriodEnd = null
|
|
14
14
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
15
|
+
// After the grace period, return 422 because cleanup may already have triggered
|
|
16
|
+
// irreversible hooks.
|
|
17
17
|
//
|
|
18
|
-
//
|
|
18
|
+
// Cancelling as an "active" user also returns 422 because no deletion is pending.
|
|
19
19
|
export const cancelDeletionWrite = defineWriteHandler({
|
|
20
20
|
name: "cancel-deletion",
|
|
21
21
|
schema: z.object({}),
|
|
@@ -48,12 +48,12 @@ export const cancelDeletionWrite = defineWriteHandler({
|
|
|
48
48
|
await updateUserLifecycle(ctx.db.unsafeRaw(), event.user.id, {
|
|
49
49
|
status: USER_STATUS.Active,
|
|
50
50
|
gracePeriodEnd: null,
|
|
51
|
-
//
|
|
51
|
+
// Closes the replay-after-cancel window — a still-TTL-valid email
|
|
52
52
|
// token verified against the nulled requestId can no longer arm a second grace period.
|
|
53
53
|
pendingDeletionRequestId: null,
|
|
54
54
|
});
|
|
55
|
-
// gracePeriodEnd=null
|
|
56
|
-
// ISO
|
|
55
|
+
// gracePeriodEnd=null in the response mirrors request-deletion's
|
|
56
|
+
// ISO timestamp so the frontend can handle both endpoints uniformly.
|
|
57
57
|
const gracePeriodEnd = null;
|
|
58
58
|
return {
|
|
59
59
|
isSuccess: true,
|
|
@@ -4,15 +4,15 @@ import * as z from "zod";
|
|
|
4
4
|
import { USER_STATUS, userTable } from "../../user/index.js";
|
|
5
5
|
import { redeemDeletionToken } from "../deletion-token.js";
|
|
6
6
|
import { resolveGracePeriod, startDeletionGracePeriod } from "./deletion-grace-period.js";
|
|
7
|
-
//
|
|
8
|
-
// expired /
|
|
7
|
+
// Use the same generic 422 for every token-error path (malformed / bad_signature /
|
|
8
|
+
// expired / missing secret) so callers cannot determine whether a token belongs to a user.
|
|
9
9
|
function invalidToken() {
|
|
10
10
|
return new UnprocessableError("invalid_or_expired_token");
|
|
11
11
|
}
|
|
12
|
-
// userId
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
12
|
+
// userId comes from the not-yet-verified token (attacker-controlled input). A failed
|
|
13
|
+
// lookup—for example, a type-mismatched id on an int/uuid column—must not surface as a
|
|
14
|
+
// 500; null is treated by the caller as "no pending request" (generic 422). HMAC
|
|
15
|
+
// verification remains the actual gate.
|
|
16
16
|
async function readPendingDeletionRequestId(ctx, userId) {
|
|
17
17
|
try {
|
|
18
18
|
const row = await ctx.db
|
|
@@ -32,7 +32,7 @@ async function readPendingDeletionRequestId(ctx, userId) {
|
|
|
32
32
|
// the actual grace-period transition, so a crash after the spend can no
|
|
33
33
|
// longer lose the write step.
|
|
34
34
|
//
|
|
35
|
-
// Replay protection
|
|
35
|
+
// Replay protection: the row's requestId is part of the verify key.
|
|
36
36
|
// We read it via the (unverified, lookup-only) userId from the token, reject
|
|
37
37
|
// a missing entry, and verify the token against the CURRENT requestId. After
|
|
38
38
|
// a cancel-deletion (status → Active, pendingDeletionRequestId → null), a
|
|
@@ -42,7 +42,7 @@ async function readPendingDeletionRequestId(ctx, userId) {
|
|
|
42
42
|
// the same requestId and both verify the HMAC — commitDeletion spends the
|
|
43
43
|
// anchor AND writes the transition atomically (expect: status===Active &&
|
|
44
44
|
// pendingDeletionRequestId===requestId), so exactly one wins. The loser gets
|
|
45
|
-
// the same generic 422 as an invalid token — no status leak
|
|
45
|
+
// the same generic 422 as an invalid token — no status leak.
|
|
46
46
|
export function createConfirmDeletionByTokenHandler(opts = {}) {
|
|
47
47
|
return defineWriteHandler({
|
|
48
48
|
name: "confirm-deletion-by-token",
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
// GET /api/query → user-data-rights:query:download-by-job
|
|
1
|
+
// GET /api/query → user-data-rights:query:download-by-job
|
|
2
2
|
//
|
|
3
|
-
// **UI
|
|
4
|
-
//
|
|
5
|
-
// job.userId
|
|
3
|
+
// **UI download flow** (session-auth): The user polls the status, sees
|
|
4
|
+
// "done", and clicks "Download" with the jobId. The server checks
|
|
5
|
+
// `session.userId == job.userId`; no token is needed because the session
|
|
6
|
+
// provides authentication.
|
|
6
7
|
//
|
|
7
|
-
// **Cross-
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// Plan-Decision (Atom 4b Plan, User-Choice).
|
|
8
|
+
// **Cross-tenant same user:** Jobs are tenant-agnostic (one job per userId
|
|
9
|
+
// across all memberships). If Alice starts an export in Tenant A, later
|
|
10
|
+
// signs in to Tenant B, and clicks Download, the server accepts it:
|
|
11
|
+
// `session.userId == job.userId` is sufficient; no tenant-match check applies.
|
|
12
12
|
//
|
|
13
|
-
// **Cross-
|
|
14
|
-
// (
|
|
15
|
-
//
|
|
13
|
+
// **Cross-user isolation:** If session.userId != job.userId, return 404
|
|
14
|
+
// (not 403) to avoid leaking whether the job exists. Use the same error as
|
|
15
|
+
// for an unknown job ID.
|
|
16
16
|
//
|
|
17
|
-
//
|
|
18
|
-
// 1.
|
|
19
|
-
// 2. job.userId === session.userId (cross-user
|
|
20
|
-
// 3. job.status === "done"
|
|
21
|
-
// 4. job.downloadStorageKey != null
|
|
22
|
-
// 5. tokenRow
|
|
23
|
-
// 6.
|
|
17
|
+
// Verification pipeline:
|
|
18
|
+
// 1. Fetch the job by jobId
|
|
19
|
+
// 2. Check job.userId === session.userId (cross-user isolation)
|
|
20
|
+
// 3. Check job.status === "done"
|
|
21
|
+
// 4. Check job.downloadStorageKey != null
|
|
22
|
+
// 5. Look up tokenRow (audit-row update only)
|
|
23
|
+
// 6. Best-effort audit update: useCount + 1, IP, UA, lastUsedAt
|
|
24
24
|
// 7. Return {url, expiresAt}
|
|
25
25
|
import { requestContext } from "@cosmicdrift/kumiko-framework/api";
|
|
26
26
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/db";
|
|
@@ -44,7 +44,7 @@ export const downloadByJobQuery = defineQueryHandler({
|
|
|
44
44
|
reason: "each signed-in user downloads only their own finished export job; the job " +
|
|
45
45
|
"lookup checks jobRow.userId against the caller's own id and 404s otherwise",
|
|
46
46
|
},
|
|
47
|
-
}, // openToAll
|
|
47
|
+
}, // openToAll requires authentication; anonymous access is not allowed
|
|
48
48
|
description: "Returns a short-lived signed download URL for the calling user's own finished data-export job named by job id, backing the download button in the privacy center once export-status reports the job done.",
|
|
49
49
|
escapeHatch: {
|
|
50
50
|
grants: ["unsafeRaw"],
|
|
@@ -56,9 +56,8 @@ export const downloadByJobQuery = defineQueryHandler({
|
|
|
56
56
|
const userId = query.user.id;
|
|
57
57
|
const jobId = query.payload.jobId;
|
|
58
58
|
const tenantId = query.user.tenantId;
|
|
59
|
-
// IP
|
|
60
|
-
//
|
|
61
|
-
// Wert (603/2).
|
|
59
|
+
// IP and UA from the request-scoped context populated by
|
|
60
|
+
// requestIdMiddleware; these are server-trusted rather than client-supplied.
|
|
62
61
|
const auditIp = requestContext.get()?.ip ?? null;
|
|
63
62
|
const auditUa = requestContext.get()?.userAgent ?? null;
|
|
64
63
|
const runner = ctx.db.unsafeRaw();
|
|
@@ -149,10 +148,10 @@ export const downloadByJobQuery = defineQueryHandler({
|
|
|
149
148
|
contentDisposition: `attachment; filename="user-data-export-${jobRow.id}.zip"`,
|
|
150
149
|
});
|
|
151
150
|
const signedUrlExpiresAt = T.Instant.fromEpochMilliseconds(now.epochMilliseconds + SIGNED_URL_TTL_SECONDS * 1000);
|
|
152
|
-
// Step 6:
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
//
|
|
151
|
+
// Step 6: Look up tokenRow for the audit update. The UI flow has no plain
|
|
152
|
+
// token, but its useCount must still be incremented so audit fields remain
|
|
153
|
+
// consistent and UI clicks count as uses. The jobId lookup is safe because
|
|
154
|
+
// the UNIQUE index guarantees at most one row.
|
|
156
155
|
const tokenRow = await fetchOne(runner, exportDownloadTokensTable, {
|
|
157
156
|
jobId,
|
|
158
157
|
});
|
|
@@ -168,9 +167,9 @@ export const downloadByJobQuery = defineQueryHandler({
|
|
|
168
167
|
userAgent: auditUa,
|
|
169
168
|
});
|
|
170
169
|
}
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
170
|
+
// If tokenRow is missing (which should not happen when the preceding token
|
|
171
|
+
// creation flow completed), skip the audit update and continue the download;
|
|
172
|
+
// an audit failure must not block the user.
|
|
174
173
|
return {
|
|
175
174
|
url: signedUrl,
|
|
176
175
|
expiresAt: signedUrlExpiresAt.toString(),
|
|
@@ -1,30 +1,29 @@
|
|
|
1
|
-
// GET /api/query → user-data-rights:query:download-by-token
|
|
1
|
+
// GET /api/query → user-data-rights:query:download-by-token.
|
|
2
2
|
//
|
|
3
|
-
// **Magic-
|
|
4
|
-
// `#token=<plain>` (URL fragment
|
|
5
|
-
//
|
|
3
|
+
// **Magic-link path** (anonymous): The user clicks an email link with
|
|
4
|
+
// `#token=<plain>` (URL fragment). The worker persisted the token hash in the database
|
|
5
|
+
// when marking the job done. Verification:
|
|
6
6
|
//
|
|
7
7
|
// 1. hashDownloadToken(plain) → fetchOne in download-tokens
|
|
8
|
-
// 2. expiresAt > now (
|
|
8
|
+
// 2. expiresAt > now (multiple uses within the TTL)
|
|
9
9
|
// 3. job.status === "done"
|
|
10
|
-
// 4. job.downloadStorageKey != null (storage
|
|
11
|
-
// 5. provider.getSignedUrl
|
|
12
|
-
// 6. Audit
|
|
10
|
+
// 4. job.downloadStorageKey != null (storage was not cleared)
|
|
11
|
+
// 5. provider.getSignedUrl is required (501 when the provider is local-fs)
|
|
12
|
+
// 6. Audit update: useCount + 1, lastUsedAt, IP, UA (best-effort, race-tolerant)
|
|
13
13
|
// 7. Return {url, expiresAt}
|
|
14
14
|
//
|
|
15
|
-
// **
|
|
16
|
-
// - Token-
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
// Tenant-Filter nötig.
|
|
15
|
+
// **Security:**
|
|
16
|
+
// - Token-hash comparison via DB-fetchOne is not constant-time, but timing attacks
|
|
17
|
+
// against SHA256 bytes require >>10k requests plus stable latency and are not
|
|
18
|
+
// exploitable in a web-app context.
|
|
19
|
+
// - Invalid tokens return 404 without leaking existence, using the same code path
|
|
20
|
+
// as a token that was not found.
|
|
21
|
+
// - The token is tenant-agnostic: UUID + SHA256 makes it globally unique, so no
|
|
22
|
+
// tenant filter is needed.
|
|
24
23
|
//
|
|
25
|
-
// **r.httpRoute
|
|
26
|
-
//
|
|
27
|
-
//
|
|
24
|
+
// **r.httpRoute wrapper** (see feature.ts) redirects to signedUrl with a 302. The user
|
|
25
|
+
// clicks the email link once, the browser follows the redirect, and the download
|
|
26
|
+
// starts. This query handler returns only the JSON.
|
|
28
27
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/db";
|
|
29
28
|
import { defineQueryHandler, SYSTEM_USER_ID, } from "@cosmicdrift/kumiko-framework/engine";
|
|
30
29
|
import { NotFoundError, UnprocessableError } from "@cosmicdrift/kumiko-framework/errors";
|
|
@@ -35,7 +34,7 @@ import { makeTenantStorageProviderResolver } from "../lib/storage-provider-resol
|
|
|
35
34
|
import { exportDownloadTokensTable } from "../schema/download-token.js";
|
|
36
35
|
import { EXPORT_JOB_STATUS, exportJobsTable } from "../schema/export-job.js";
|
|
37
36
|
import { hashDownloadToken } from "../token-helpers.js";
|
|
38
|
-
const SIGNED_URL_TTL_SECONDS = 300; // 5 min —
|
|
37
|
+
const SIGNED_URL_TTL_SECONDS = 300; // 5 min — short enough to limit replay, long enough for slow connections
|
|
39
38
|
const DOWNLOAD_BY_TOKEN_REASON = "the download token, its job and audit rows are keyed by token hash / job id, not the caller's tenant — the anonymous magic-link path has no tenant context at all";
|
|
40
39
|
export const downloadByTokenQuery = defineQueryHandler({
|
|
41
40
|
name: "download-by-token",
|
|
@@ -54,12 +53,11 @@ export const downloadByTokenQuery = defineQueryHandler({
|
|
|
54
53
|
personalData: "public-intake",
|
|
55
54
|
},
|
|
56
55
|
agent: { expose: false },
|
|
57
|
-
// Brute-
|
|
58
|
-
// 32-byte
|
|
59
|
-
// defense
|
|
60
|
-
// 30 attempts/min/IP
|
|
61
|
-
//
|
|
62
|
-
// Memory `feedback_security_default_on`.
|
|
56
|
+
// Brute-force protection for token-hash probing. This anonymous endpoint uses a
|
|
57
|
+
// 32-byte random token with a 256-bit search space, but rate limiting provides
|
|
58
|
+
// defense in depth and protects against storm patterns that generate database load.
|
|
59
|
+
// 30 attempts/min/IP allows legitimate users several retries after connection
|
|
60
|
+
// failures while blocking automated probing loops.
|
|
63
61
|
rateLimit: { per: "ip+handler", limit: 30, windowSeconds: 60 },
|
|
64
62
|
escapeHatch: {
|
|
65
63
|
grants: ["unsafeRaw"],
|
|
@@ -75,26 +73,26 @@ export const downloadByTokenQuery = defineQueryHandler({
|
|
|
75
73
|
tokenHash: hash,
|
|
76
74
|
});
|
|
77
75
|
if (!tokenRow) {
|
|
78
|
-
// Invalid token — 404
|
|
79
|
-
//
|
|
80
|
-
//
|
|
76
|
+
// Invalid token — 404 without leaking existence. Use a generic NotFoundError
|
|
77
|
+
// so all failure paths have the same external shape, preventing probing that
|
|
78
|
+
// distinguishes "Token does not exist" from "Job has failed".
|
|
81
79
|
throw new NotFoundError("export-download", undefined, {
|
|
82
80
|
i18nKey: "userDataRights.errors.download.notFound",
|
|
83
81
|
});
|
|
84
82
|
}
|
|
85
83
|
const auditIp = query.payload.auditMeta?.ip ?? null;
|
|
86
84
|
const auditUa = query.payload.auditMeta?.userAgent ?? null;
|
|
87
|
-
// Step 2: TTL
|
|
85
|
+
// Step 2: TTL check.
|
|
88
86
|
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
87
|
+
// 410 Gone would be semantically right (the download existed and is now
|
|
88
|
+
// gone), but the framework has no GoneError class. NotFoundError with
|
|
89
|
+
// i18nKey "expired" is the compromise: the UI renders by i18nKey, not by
|
|
90
|
+
// HTTP status, so the user sees "Your download has expired" instead of a
|
|
91
|
+
// generic "not found".
|
|
94
92
|
if (tokenRow.expiresAt.epochMilliseconds <= now.epochMilliseconds) {
|
|
95
|
-
//
|
|
96
|
-
// tenantId
|
|
97
|
-
//
|
|
93
|
+
// Auditing cannot be skipped yet because jobRow has not been loaded and the
|
|
94
|
+
// tenantId is unknown. Load the job to obtain audit context; if it is also
|
|
95
|
+
// missing, skipping the audit is acceptable (best-effort).
|
|
98
96
|
const jobForAudit = await fetchOne(runner, exportJobsTable, {
|
|
99
97
|
id: tokenRow.jobId,
|
|
100
98
|
});
|
|
@@ -193,9 +191,9 @@ export const downloadByTokenQuery = defineQueryHandler({
|
|
|
193
191
|
contentDisposition: `attachment; filename="user-data-export-${jobRow.id}.zip"`,
|
|
194
192
|
});
|
|
195
193
|
const signedUrlExpiresAt = T.Instant.fromEpochMilliseconds(now.epochMilliseconds + SIGNED_URL_TTL_SECONDS * 1000);
|
|
196
|
-
// Step 6:
|
|
197
|
-
//
|
|
198
|
-
//
|
|
194
|
+
// Step 6: Best-effort audit update. auditMeta comes from the httpRoute wrapper
|
|
195
|
+
// (trusted source). Direct API callers can falsify it, but the audit data is not
|
|
196
|
+
// security-sensitive.
|
|
199
197
|
await recordDownloadUse({
|
|
200
198
|
db: runner,
|
|
201
199
|
tokenId: tokenRow.id,
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
// GET /api/user/export-status
|
|
1
|
+
// GET /api/user/export-status — user polling.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
3
|
+
// Returns the calling user's most recent ExportJob (active jobs first,
|
|
4
|
+
// otherwise the newest done/failed job).
|
|
5
5
|
//
|
|
6
|
-
// **Cross-User-Isolation:**
|
|
7
|
-
//
|
|
8
|
-
// uses ctx.db.unsafeRaw
|
|
9
|
-
// "Cross-Tenant-Semantik").
|
|
6
|
+
// **Cross-User-Isolation:** The filter is `userId === query.user.id`, so no
|
|
7
|
+
// user can view another user's job status, even by guessing an ID. The
|
|
8
|
+
// pre-check uses ctx.db.unsafeRaw because ExportJob is tenant-agnostic.
|
|
10
9
|
//
|
|
11
|
-
// **Read-Only-Endpoint:**
|
|
12
|
-
//
|
|
10
|
+
// **Read-Only-Endpoint:** Polling only; it does not change state. The endpoint
|
|
11
|
+
// is idempotent and cacheable. The UI typically polls every 2-5s while running.
|
|
13
12
|
import { selectMany } from "@cosmicdrift/kumiko-framework/db";
|
|
14
13
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
15
14
|
import * as z from "zod";
|
|
@@ -5,7 +5,7 @@ import { USER_STATUS, userTable } from "../../user/index.js";
|
|
|
5
5
|
import { denyIfTargetOutsideAdminTenant } from "../lib/deny-if-target-outside-admin-tenant.js";
|
|
6
6
|
import { updateUserLifecycle } from "../lib/update-user-lifecycle.js";
|
|
7
7
|
const LIFT_RESTRICTION_ESCAPE_HATCH_REASON = "checks the target user's membership in the admin's tenant and appends the user lifecycle status change on the SYSTEM_TENANT_ID user stream, both via DbRunner helpers outside the admin's own tenant scope.";
|
|
8
|
-
// POST /api/user/lift-restriction
|
|
8
|
+
// POST /api/user/lift-restriction — GDPR Art. 18 Reverse.
|
|
9
9
|
//
|
|
10
10
|
// Operator-only. A Restricted user's own session is unconditionally
|
|
11
11
|
// rejected by sessionChecker (BLOCKED_STATUSES) the moment their status
|
|
@@ -13,16 +13,15 @@ const LIFT_RESTRICTION_ESCAPE_HATCH_REASON = "checks the target user's membershi
|
|
|
13
13
|
// endpoint. There is no self-service path today, so lifting a restriction
|
|
14
14
|
// always targets someone else's account by id.
|
|
15
15
|
//
|
|
16
|
-
// Tenant scope: `access.admin` includes TenantAdmin (tenant-scoped) even
|
|
17
|
-
//
|
|
18
|
-
// from tenant A could unrestrict/reactivate a user who has never been a
|
|
19
|
-
//
|
|
20
|
-
// the
|
|
21
|
-
// `event.user.tenantId` (row existence only — no active-status field).
|
|
16
|
+
// Tenant scope: `access.admin` includes TenantAdmin (tenant-scoped) even though the User-entity is
|
|
17
|
+
// global — see user-data-rights.md "Cross-Tenant Semantics". Without a membership check, a
|
|
18
|
+
// TenantAdmin from tenant A could unrestrict/reactivate a user who has never been a member of
|
|
19
|
+
// tenant A. Only SystemAdmin (platform-wide) skips the check; the target must have a membership row
|
|
20
|
+
// in the acting admin's `event.user.tenantId` (row existence only — no active-status field).
|
|
22
21
|
//
|
|
23
22
|
// State-Transitions:
|
|
24
23
|
// Restricted → Active ✓
|
|
25
|
-
// Active → ... ✗ 422 not_restricted (
|
|
24
|
+
// Active → ... ✗ 422 not_restricted (Idempotency Guard)
|
|
26
25
|
// DeletionRequested → ... ✗ 422 not_restricted
|
|
27
26
|
// Deleted → ... ✗ 422 not_restricted
|
|
28
27
|
export const liftRestrictionWrite = defineWriteHandler({
|
|
@@ -4,8 +4,8 @@ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { decryptStoredPii } from "../../shared/index.js";
|
|
6
6
|
import { downloadAttemptsTable } from "../schema/download-attempt.js";
|
|
7
|
-
// Operator
|
|
8
|
-
//
|
|
7
|
+
// Operator query for invalid download attempts. Provides tenant-isolated visibility for data
|
|
8
|
+
// protection officers investigating brute-force probing.
|
|
9
9
|
const MAX_LIMIT = 100;
|
|
10
10
|
const REQUEST_ID = "user-data-rights:list-download-attempts";
|
|
11
11
|
export const listDownloadAttemptsQuery = defineQueryHandler({
|