@cosmicdrift/kumiko-bundled-features 0.158.2 → 0.159.1
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 +7 -6
- package/src/auth-email-password/__tests__/account-unlock.integration.test.ts +283 -0
- package/src/auth-email-password/__tests__/auth-user-row.test.ts +36 -0
- package/src/auth-email-password/__tests__/auth.integration.test.ts +21 -0
- package/src/auth-email-password/__tests__/email-verification.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 +15 -1
- 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 +12 -0
- package/src/auth-email-password/constants.ts +15 -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 +37 -5
- package/src/auth-email-password/handlers/change-password.write.ts +2 -1
- package/src/auth-email-password/handlers/confirm-account-unlock.write.ts +59 -0
- package/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -0
- package/src/auth-email-password/handlers/invite-accept.write.ts +6 -2
- package/src/auth-email-password/handlers/invite-signup-complete.write.ts +1 -0
- package/src/auth-email-password/handlers/request-account-unlock.write.ts +38 -0
- package/src/auth-email-password/handlers/reset-password.write.ts +2 -1
- package/src/auth-email-password/handlers/signup-confirm.write.ts +2 -1
- package/src/auth-email-password/i18n.ts +42 -0
- package/src/auth-email-password/index.ts +2 -0
- 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/signed-token.ts +1 -0
- package/src/auth-email-password/unlock-token.ts +28 -0
- package/src/auth-email-password/web/__tests__/auth-gate.test.tsx +65 -3
- package/src/auth-email-password/web/auth-client.ts +33 -0
- package/src/auth-email-password/web/confirm-account-unlock-screen.tsx +95 -0
- package/src/auth-email-password/web/index.ts +6 -0
- package/src/auth-email-password/web/login-screen.tsx +15 -0
- package/src/auth-email-password/web/request-account-unlock-screen.tsx +112 -0
- package/src/auth-foundation/__tests__/feature.test.ts +33 -0
- package/src/auth-foundation/__tests__/session-store.test.ts +78 -0
- package/src/auth-foundation/__tests__/token-verifier.test.ts +117 -0
- package/src/auth-foundation/boot-checks.ts +91 -0
- package/src/auth-foundation/feature.ts +103 -0
- package/src/auth-foundation/index.ts +23 -0
- package/src/auth-foundation/types.ts +107 -0
- package/src/auth-mfa/__tests__/errors-i18n-parity.test.ts +8 -0
- package/src/auth-mfa/__tests__/verify.integration.test.ts +20 -0
- package/src/auth-mfa/web/__tests__/mfa-client.test.ts +118 -0
- package/src/auth-mfa/web/__tests__/mfa-enable-screen.test.tsx +173 -0
- package/src/auth-mfa/web/__tests__/mfa-verify-screen.test.tsx +100 -0
- package/src/billing-foundation/__tests__/subscription-provider-contract.ts +69 -0
- package/src/channel-email/pii-guard.ts +6 -3
- package/src/config/__tests__/config.integration.test.ts +14 -0
- package/src/config/__tests__/pii-encrypted.integration.test.ts +3 -2
- package/src/config/__tests__/reencrypt-job-concurrent-write-race.integration.test.ts +20 -3
- package/src/config/__tests__/reencrypt-job-kek-rotation.integration.test.ts +2 -3
- package/src/config/feature.ts +2 -4
- package/src/config/handlers/set.write.ts +7 -1
- package/src/config/resolver.ts +1 -1
- package/src/custom-fields/__tests__/user-data-rights.integration.test.ts +2 -0
- package/src/delivery/handlers/log.query.ts +1 -1
- package/src/files-provider-s3/__tests__/s3-provider.integration.test.ts +7 -0
- package/src/folders/web/__tests__/folder-manager.test.tsx +27 -2
- package/src/inbound-mail-foundation/__tests__/account-state.test.ts +30 -0
- package/src/inbound-mail-foundation/__tests__/inbound-mail-foundation.integration.test.ts +57 -6
- package/src/inbound-mail-foundation/__tests__/inbound-mail-provider-contract.ts +93 -0
- package/src/inbound-mail-foundation/handlers/ingest-message.write.ts +60 -50
- package/src/inbound-provider-imap/__tests__/imap-live.integration.test.ts +12 -0
- package/src/inbound-provider-inmemory/__tests__/feature.test.ts +48 -0
- package/src/inbound-provider-inmemory/feature.ts +2 -2
- package/src/jobs/__tests__/reindex-entity-job.integration.test.ts +53 -1
- package/src/jobs/handlers/detail.query.ts +1 -1
- package/src/jobs/handlers/list.query.ts +1 -1
- package/src/mail-foundation/__tests__/mail-transport-contract.ts +42 -0
- package/src/mail-transport-inmemory/__tests__/feature.test.ts +19 -0
- package/src/mail-transport-smtp/__tests__/feature.test.ts +60 -0
- package/src/managed-pages/handlers/branding.query.ts +5 -1
- package/src/personal-access-tokens/__tests__/feature.test.ts +18 -0
- package/src/personal-access-tokens/__tests__/pat.integration.test.ts +8 -11
- package/src/personal-access-tokens/feature.ts +22 -7
- package/src/personal-access-tokens/handlers/list.query.ts +1 -1
- package/src/personal-access-tokens/index.ts +0 -14
- package/src/personal-access-tokens/resolver.ts +2 -2
- package/src/secrets/__tests__/rotate.integration.test.ts +31 -0
- package/src/sessions/__tests__/cleanup.integration.test.ts +2 -1
- package/src/sessions/__tests__/password-auto-revoke.integration.test.ts +5 -3
- package/src/sessions/__tests__/rebuild-survival.integration.test.ts +11 -2
- package/src/sessions/__tests__/session-store-registration.test.ts +21 -0
- package/src/sessions/__tests__/sessions-screens.boot.test.ts +10 -1
- package/src/sessions/__tests__/sessions.integration.test.ts +28 -17
- package/src/sessions/__tests__/test-helpers.ts +15 -2
- package/src/sessions/feature.ts +31 -3
- package/src/sessions/handlers/detail.query.ts +2 -2
- package/src/sessions/handlers/list.query.ts +4 -2
- package/src/sessions/handlers/mine.query.ts +4 -2
- package/src/sessions/testing.ts +14 -1
- package/src/shared/__tests__/chunked-entity-migration.test.ts +145 -0
- package/src/shared/decrypt-stored-pii.ts +12 -5
- package/src/subscription-mollie/__tests__/feature.test.ts +87 -0
- package/src/subscription-stripe/__tests__/feature.test.ts +120 -1
- package/src/tenant/__tests__/members-query-kms.integration.test.ts +98 -0
- package/src/tenant/handlers/invitations.query.ts +4 -2
- package/src/tenant/handlers/members.query.ts +2 -2
- package/src/tenant/seeding.ts +2 -2
- package/src/tenant-lifecycle/__tests__/tenant-lifecycle.integration.test.ts +2 -0
- package/src/tenant-lifecycle/boot-checks.ts +53 -0
- package/src/tenant-lifecycle/feature.ts +7 -0
- package/src/tenant-lifecycle/handlers/request-destruction.write.ts +1 -1
- package/src/tier-engine/feature.ts +2 -2
- package/src/user-data-rights/COMPLIANCE.md +20 -0
- package/src/user-data-rights/__tests__/anonymous-deletion-kms.integration.test.ts +2 -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 +2 -0
- package/src/user-data-rights/__tests__/boot-checks.test.ts +172 -0
- package/src/user-data-rights/__tests__/cross-data-matrix.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/download-by-token-resolver-trust.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/download.integration.test.ts +59 -4
- package/src/user-data-rights/__tests__/export-encrypted-fields.integration.test.ts +2 -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 +2 -0
- package/src/user-data-rights/__tests__/file-binary-forget-failure.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/file-retention.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/file-storage-unification.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/forget-cleanup-hook-ordering.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/forget-hook-registry.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/inspector-screens.boot.test.ts +10 -1
- package/src/user-data-rights/__tests__/mail-default-bridge.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/read-users-rebuild-survives-lifecycle.integration.test.ts +15 -7
- 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 +2 -0
- package/src/user-data-rights/__tests__/request-export.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/restriction-flow.integration.test.ts +48 -15
- 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 +4 -1
- package/src/user-data-rights/__tests__/run-forget-cleanup.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/run-user-export.integration.test.ts +2 -0
- 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 +86 -0
- package/src/user-data-rights/feature.ts +116 -6
- package/src/user-data-rights/handlers/deletion-grace-period.ts +1 -1
- package/src/user-data-rights/handlers/download-by-token.query.ts +1 -1
- package/src/user-data-rights/handlers/lift-restriction.write.ts +13 -19
- package/src/user-data-rights/handlers/restrict-account.write.ts +30 -8
- package/src/user-data-rights/run-export-jobs.ts +3 -3
- package/src/user-data-rights/run-forget-cleanup.ts +1 -1
- package/src/user-data-rights-defaults/__tests__/bundled-entities.userdata-hooks.integration.test.ts +3 -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 +2 -0
- package/src/user-data-rights-defaults/hooks/tenant-invitation.userdata-hook.ts +1 -1
- package/src/user-profile/__tests__/change-email.integration.test.ts +2 -0
- package/src/user-profile/handlers/change-email.write.ts +1 -1
- package/src/config/__tests__/encrypted-legacy-migration.integration.test.ts +0 -179
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
type TierResolverPlugin,
|
|
63
63
|
type TrialGate,
|
|
64
64
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
65
|
-
import {
|
|
65
|
+
import { getUnscopedAggregateStreamMaxVersion } from "@cosmicdrift/kumiko-framework/event-store";
|
|
66
66
|
import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
67
67
|
import { z } from "zod";
|
|
68
68
|
import { tenantTable } from "../tenant";
|
|
@@ -365,7 +365,7 @@ export function createTierEngineFeature<
|
|
|
365
365
|
|
|
366
366
|
// Idempotency: stream-existence-check vor create. Pattern aus
|
|
367
367
|
// seedTenant.ts. Bei re-replay (rebuild) nicht versionsbumpen.
|
|
368
|
-
const streamVersion = await
|
|
368
|
+
const streamVersion = await getUnscopedAggregateStreamMaxVersion(rawDb, aggregateId);
|
|
369
369
|
// skip: idempotent — tier-assignment stream already seeded (rebuild/replay).
|
|
370
370
|
if (streamVersion > 0) return;
|
|
371
371
|
|
|
@@ -45,6 +45,26 @@ Dieses Dokument bündelt die **technischen Fakten** zur DSGVO-Pipeline
|
|
|
45
45
|
| `read_tenant_retention_overrides` | Per-Tenant Retention-Override pro Entity | nein | unbounded (Konfiguration) | Aufbewahrungspflicht-Edge-Cases |
|
|
46
46
|
| Storage-Provider (Local / S3) | Export-ZIPs + File-Binaries | indirekt (Inhalt) | Local: per `exportDownloadTtl` Cleanup. S3: lifecycle-Policy (App-Author-Verantwortung) | Daten-Export-Auslieferung |
|
|
47
47
|
|
|
48
|
+
### Bekanntes Residuum: `createdBy` / `metadata.userId` in `kumiko_events`
|
|
49
|
+
|
|
50
|
+
Nach der Forget-Pipeline (Art. 17, Abschnitt 1) bleiben `kumiko_events.created_by`
|
|
51
|
+
und `metadata.userId` (Event-Store-Schema `events-schema.ts`, EventMetadata-Feld)
|
|
52
|
+
als Klartext-UUID stehen. Die Forget-Hooks aus `user-data-rights-defaults` fassen
|
|
53
|
+
`kumiko_events` nicht an — `createdBy`/`metadata.userId` sind keine über
|
|
54
|
+
`defineEvent(..., { piiFields })` deklarierten Payload-PII-Felder (siehe
|
|
55
|
+
`packages/framework/src/crypto/event-pii.ts`) und laufen deshalb nicht durch die
|
|
56
|
+
Payload-Verschlüsselung. Löschung dieser Rows erfolgt ausschließlich über die
|
|
57
|
+
zeitbasierte `data-retention`-Policy (`keepFor`, s. `kumiko_events`-Zeile in Abschnitt
|
|
58
|
+
2 oben), unabhängig vom User-Forget-Antrag.
|
|
59
|
+
|
|
60
|
+
Technische Basis: Der `read_users`-Row zur betroffenen UUID wird beim Forget-Run
|
|
61
|
+
in-place anonymisiert, nicht hard-gelöscht (Sentinel-Pattern, siehe Abschnitt 4
|
|
62
|
+
„Integrität"). Email, Displayname und Passwort-Hash werden dabei entfernt/ersetzt —
|
|
63
|
+
das ist der Schlüssel, der die Verknüpfung kappt: Sobald diese Felder weg sind, gibt
|
|
64
|
+
es keinen Rückweg von der UUID in `created_by` zu einer realen Identität mehr. Die
|
|
65
|
+
UUID bleibt als stabiler, aber orphaned Pseudonym-Wert bestehen, bis die
|
|
66
|
+
Retention-Policy irgendwann das Event selbst löscht.
|
|
67
|
+
|
|
48
68
|
---
|
|
49
69
|
|
|
50
70
|
## 3. Compliance-Profile
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// KLARTEXT schicken (vorher: Ciphertext-Adresse → Mail unzustellbar).
|
|
5
5
|
|
|
6
6
|
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
7
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
7
8
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
8
9
|
import {
|
|
9
10
|
configureBlindIndexKey,
|
|
@@ -58,6 +59,7 @@ beforeAll(async () => {
|
|
|
58
59
|
createUserFeature(),
|
|
59
60
|
createDataRetentionFeature(),
|
|
60
61
|
createComplianceProfilesFeature(),
|
|
62
|
+
authFoundationFeature,
|
|
61
63
|
createSessionsFeature(),
|
|
62
64
|
createUserDataRightsFeature({
|
|
63
65
|
deletionTokenSecret: DELETION_SECRET,
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// Beweist end-to-end via echte /api/write-Calls OHNE Auth (anonymousAccess).
|
|
7
7
|
|
|
8
8
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
9
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
9
10
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
10
11
|
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
11
12
|
import {
|
|
@@ -53,6 +54,7 @@ beforeAll(async () => {
|
|
|
53
54
|
createUserFeature(),
|
|
54
55
|
createDataRetentionFeature(),
|
|
55
56
|
createComplianceProfilesFeature(),
|
|
57
|
+
authFoundationFeature,
|
|
56
58
|
createSessionsFeature(),
|
|
57
59
|
createUserDataRightsFeature({
|
|
58
60
|
deletionTokenSecret: DELETION_SECRET,
|
|
@@ -331,6 +333,7 @@ describe("anonymous deletion flow — not configured (kein Secret)", () => {
|
|
|
331
333
|
createUserFeature(),
|
|
332
334
|
createDataRetentionFeature(),
|
|
333
335
|
createComplianceProfilesFeature(),
|
|
336
|
+
authFoundationFeature,
|
|
334
337
|
createSessionsFeature(),
|
|
335
338
|
createUserDataRightsFeature({
|
|
336
339
|
sendDeletionVerificationEmail: async (args) => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// S2.U7 — my-audit-log + invalid-attempt-audit + list-download-attempts.
|
|
2
2
|
|
|
3
3
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
4
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
4
5
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
5
6
|
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
6
7
|
import {
|
|
@@ -39,6 +40,7 @@ beforeAll(async () => {
|
|
|
39
40
|
createUserFeature(),
|
|
40
41
|
createDataRetentionFeature(),
|
|
41
42
|
createComplianceProfilesFeature(),
|
|
43
|
+
authFoundationFeature,
|
|
42
44
|
createSessionsFeature(),
|
|
43
45
|
|
|
44
46
|
createUserDataRightsFeature(),
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { integer, type SchemaTable, table, uuid } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
+
import {
|
|
4
|
+
createEntity,
|
|
5
|
+
createTextField,
|
|
6
|
+
defineFeature,
|
|
7
|
+
EXT_TENANT_DATA,
|
|
8
|
+
EXT_USER_DATA,
|
|
9
|
+
validateBoot,
|
|
10
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
11
|
+
import { createFilesFeature } from "@cosmicdrift/kumiko-framework/files";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { EXT_SESSION_STORE } from "../../auth-foundation";
|
|
14
|
+
import { createComplianceProfilesFeature } from "../../compliance-profiles/feature";
|
|
15
|
+
import { createConfigFeature } from "../../config/feature";
|
|
16
|
+
import { createDataRetentionFeature } from "../../data-retention/feature";
|
|
17
|
+
import { createSessionsFeature } from "../../sessions/feature";
|
|
18
|
+
import { createTenantFeature } from "../../tenant/feature";
|
|
19
|
+
import { createTenantLifecycleFeature } from "../../tenant-lifecycle/feature";
|
|
20
|
+
import { createUserFeature } from "../../user/feature";
|
|
21
|
+
import { createUserDataRightsDefaultsFeature } from "../../user-data-rights-defaults/feature";
|
|
22
|
+
import { createUserDataRightsFeature } from "../feature";
|
|
23
|
+
|
|
24
|
+
// GDPR-storage guards V2 (export-without-erase) and V3 (pii-entity-without-
|
|
25
|
+
// hook) moved off the framework-internal boot-validator onto user-data-
|
|
26
|
+
// rights' own `r.bootCheck()` (#1314); V4 (tenantOwned-entity-without-hook)
|
|
27
|
+
// onto tenant-lifecycle's own `r.bootCheck()` — tenant-lifecycle owns
|
|
28
|
+
// EXT_TENANT_DATA, so its own mount preserves the original guard's trigger
|
|
29
|
+
// exactly (gating on a sibling feature would silently narrow coverage).
|
|
30
|
+
// Unlike the old direct-function tests, these only run inside a real
|
|
31
|
+
// validateBoot() pass — exercising the actual `r.bootCheck()` plumbing, not
|
|
32
|
+
// just the guard logic.
|
|
33
|
+
// Stub instead of the real auth-foundation — these boot-checks are about
|
|
34
|
+
// GDPR-storage guards, not auth; only sessions' sessionStore registration
|
|
35
|
+
// needs an owner present.
|
|
36
|
+
const sessionStoreOwnerStub = defineFeature("auth-foundation", (r) => {
|
|
37
|
+
r.extendsRegistrar(EXT_SESSION_STORE, {});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function baseFeatures() {
|
|
41
|
+
return [
|
|
42
|
+
createConfigFeature(),
|
|
43
|
+
createUserFeature(),
|
|
44
|
+
sessionStoreOwnerStub,
|
|
45
|
+
createSessionsFeature(),
|
|
46
|
+
createDataRetentionFeature(),
|
|
47
|
+
createComplianceProfilesFeature(),
|
|
48
|
+
createFilesFeature(),
|
|
49
|
+
createUserDataRightsFeature(),
|
|
50
|
+
createUserDataRightsDefaultsFeature(),
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Minimal tenant-lifecycle assembly with NO user-data-rights* mounted at all
|
|
55
|
+
// — proves V4 fires from tenant-lifecycle's own mount, not from a sibling
|
|
56
|
+
// feature accidentally re-gating it.
|
|
57
|
+
function minimalTenantLifecycleFeatures() {
|
|
58
|
+
return [
|
|
59
|
+
createConfigFeature(),
|
|
60
|
+
createTenantFeature(),
|
|
61
|
+
createComplianceProfilesFeature(),
|
|
62
|
+
createTenantLifecycleFeature(),
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe("GDPR-storage boot guards V2-V4 (via r.bootCheck)", () => {
|
|
67
|
+
test("known-good assembly boot-validates (baseline)", () => {
|
|
68
|
+
expect(() => validateBoot(baseFeatures())).not.toThrow();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("V2: export hook without delete hook → Art.17 throw", () => {
|
|
72
|
+
const bad = defineFeature("bad-export", (r) => {
|
|
73
|
+
r.requires("user-data-rights");
|
|
74
|
+
r.useExtension(EXT_USER_DATA, "badEntity", { export: async () => null });
|
|
75
|
+
});
|
|
76
|
+
expect(() => validateBoot([...baseFeatures(), bad])).toThrow(/Art\.17/);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("V3: pii entity without any EXT_USER_DATA hook → throws the guard's own message", () => {
|
|
80
|
+
const bad = defineFeature("crm", (r) => {
|
|
81
|
+
r.entity("contact", createEntity({ fields: { email: createTextField({ pii: true }) } }));
|
|
82
|
+
});
|
|
83
|
+
expect(() => validateBoot([...baseFeatures(), bad])).toThrow(/EXT_USER_DATA hook.*Art\.17 gap/);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("V3: pii field on a projection-only entity (no r.entity) is still caught", () => {
|
|
87
|
+
const projectionTable = table("crm_contact_summary", {
|
|
88
|
+
id: uuid("id").primaryKey(),
|
|
89
|
+
count: integer("count").notNull().default(0),
|
|
90
|
+
}) as unknown as SchemaTable;
|
|
91
|
+
const bad = defineFeature("crm", (r) => {
|
|
92
|
+
const contactCreated = r.defineEvent("contact-created", z.unknown());
|
|
93
|
+
r.projection({
|
|
94
|
+
name: "contact-summary",
|
|
95
|
+
source: "contact",
|
|
96
|
+
table: projectionTable,
|
|
97
|
+
apply: { [contactCreated.name]: async () => {} },
|
|
98
|
+
entity: createEntity({ fields: { email: createTextField({ pii: true }) } }),
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
expect(() => validateBoot([...baseFeatures(), bad])).toThrow(/EXT_USER_DATA hook.*Art\.17 gap/);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("V3: projection-only pii entity WITH EXT_USER_DATA hook registered for its entity name → no throw", () => {
|
|
105
|
+
const projectionTable = table("crm_contact_summary_hooked", {
|
|
106
|
+
id: uuid("id").primaryKey(),
|
|
107
|
+
count: integer("count").notNull().default(0),
|
|
108
|
+
}) as unknown as SchemaTable;
|
|
109
|
+
const hooked = defineFeature("crm-hooked", (r) => {
|
|
110
|
+
r.requires("user-data-rights");
|
|
111
|
+
const contactCreated = r.defineEvent("contact-hooked-created", z.unknown());
|
|
112
|
+
r.projection({
|
|
113
|
+
name: "contact-summary",
|
|
114
|
+
source: "contact",
|
|
115
|
+
table: projectionTable,
|
|
116
|
+
apply: { [contactCreated.name]: async () => {} },
|
|
117
|
+
entity: createEntity({ fields: { email: createTextField({ pii: true }) } }),
|
|
118
|
+
});
|
|
119
|
+
r.useExtension(EXT_USER_DATA, "contact-summary", {
|
|
120
|
+
export: async () => null,
|
|
121
|
+
delete: async () => {},
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
expect(() => validateBoot([...baseFeatures(), hooked])).not.toThrow();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("V4: tenantOwned entity without EXT_TENANT_DATA hook, tenant-lifecycle mounted → throws the guard's own message", () => {
|
|
128
|
+
const bad = defineFeature("billing", (r) => {
|
|
129
|
+
r.entity(
|
|
130
|
+
"subscription",
|
|
131
|
+
createEntity({ fields: { providerCustomerId: createTextField({ tenantOwned: true }) } }),
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
expect(() => validateBoot([...minimalTenantLifecycleFeatures(), bad])).toThrow(
|
|
135
|
+
/EXT_TENANT_DATA destroy hook/,
|
|
136
|
+
);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("V4: fires with no user-data-rights* mounted at all — trigger is tenant-lifecycle's own mount", () => {
|
|
140
|
+
const bad = defineFeature("billing", (r) => {
|
|
141
|
+
r.entity(
|
|
142
|
+
"subscription",
|
|
143
|
+
createEntity({ fields: { providerCustomerId: createTextField({ tenantOwned: true }) } }),
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
const features = minimalTenantLifecycleFeatures().concat(bad);
|
|
147
|
+
expect(features.some((f) => f.name.startsWith("user-data-rights"))).toBe(false);
|
|
148
|
+
expect(() => validateBoot(features)).toThrow(/EXT_TENANT_DATA destroy hook/);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("V4: tenant-lifecycle not mounted → tenantOwned entity does not throw", () => {
|
|
152
|
+
const notMounted = defineFeature("billing-standalone", (r) => {
|
|
153
|
+
r.entity(
|
|
154
|
+
"subscription",
|
|
155
|
+
createEntity({ fields: { providerCustomerId: createTextField({ tenantOwned: true }) } }),
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
expect(() => validateBoot([...baseFeatures(), notMounted])).not.toThrow();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("V4: tenantOwned entity WITH EXT_TENANT_DATA hook registered → no throw", () => {
|
|
162
|
+
const hooked = defineFeature("billing-hooked", (r) => {
|
|
163
|
+
r.requires("tenant-lifecycle");
|
|
164
|
+
r.entity(
|
|
165
|
+
"subscription",
|
|
166
|
+
createEntity({ fields: { providerCustomerId: createTextField({ tenantOwned: true }) } }),
|
|
167
|
+
);
|
|
168
|
+
r.useExtension(EXT_TENANT_DATA, "subscription", { destroy: async () => {} });
|
|
169
|
+
});
|
|
170
|
+
expect(() => validateBoot([...minimalTenantLifecycleFeatures(), hooked])).not.toThrow();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// Alices Forget; Bobs Daten landen NICHT in Alices Export-Bundle.
|
|
14
14
|
|
|
15
15
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
16
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
16
17
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
17
18
|
import { defineUnmanagedTable } from "@cosmicdrift/kumiko-framework/db";
|
|
18
19
|
import {
|
|
@@ -118,6 +119,7 @@ beforeAll(async () => {
|
|
|
118
119
|
createFilesFeature(),
|
|
119
120
|
createDataRetentionFeature(),
|
|
120
121
|
createComplianceProfilesFeature(),
|
|
122
|
+
authFoundationFeature,
|
|
121
123
|
createSessionsFeature(),
|
|
122
124
|
|
|
123
125
|
createUserDataRightsFeature(),
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
// for `jobRow.requestedFromTenantId` instead.
|
|
13
13
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
14
14
|
import { randomBytes } from "node:crypto";
|
|
15
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
15
16
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
16
17
|
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
17
18
|
import {
|
|
@@ -76,6 +77,7 @@ beforeAll(async () => {
|
|
|
76
77
|
createComplianceProfilesFeature(),
|
|
77
78
|
fileFoundationFeature,
|
|
78
79
|
fileProviderInMemoryFeature,
|
|
80
|
+
authFoundationFeature,
|
|
79
81
|
createSessionsFeature(),
|
|
80
82
|
createUserDataRightsFeature(),
|
|
81
83
|
],
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
11
11
|
import { randomBytes } from "node:crypto";
|
|
12
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
12
13
|
import { asRawClient, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
13
14
|
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
14
15
|
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
@@ -115,6 +116,7 @@ beforeAll(async () => {
|
|
|
115
116
|
fileFoundationFeature,
|
|
116
117
|
fileProviderInMemoryFeature,
|
|
117
118
|
noSignedUrlProviderFeature,
|
|
119
|
+
authFoundationFeature,
|
|
118
120
|
createSessionsFeature(),
|
|
119
121
|
|
|
120
122
|
createUserDataRightsFeature(),
|
|
@@ -464,13 +466,25 @@ describe("r.httpRoute :: /user-export/by-token (Magic-Link e2e)", () => {
|
|
|
464
466
|
expect(body.error?.i18nKey).toBe("userDataRights.errors.download.notFound");
|
|
465
467
|
});
|
|
466
468
|
|
|
467
|
-
test("
|
|
469
|
+
test("no query token (fragment-Pfad) → serviert Interstitial-HTML statt 400", async () => {
|
|
468
470
|
const res = await stack.app.fetch(
|
|
469
471
|
new Request("http://test/user-export/by-token", { method: "GET" }),
|
|
470
472
|
);
|
|
471
|
-
expect(res.status).toBe(
|
|
472
|
-
|
|
473
|
-
|
|
473
|
+
expect(res.status).toBe(200);
|
|
474
|
+
expect(res.headers.get("content-type")).toContain("text/html");
|
|
475
|
+
const body = await res.text();
|
|
476
|
+
expect(body).toContain("/user-export/by-token.js");
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
test("by-token.js → same-origin exchange-script, application/javascript", async () => {
|
|
480
|
+
const res = await stack.app.fetch(
|
|
481
|
+
new Request("http://test/user-export/by-token.js", { method: "GET" }),
|
|
482
|
+
);
|
|
483
|
+
expect(res.status).toBe(200);
|
|
484
|
+
expect(res.headers.get("content-type")).toContain("application/javascript");
|
|
485
|
+
const body = await res.text();
|
|
486
|
+
expect(body).toContain("location.hash");
|
|
487
|
+
expect(body).toContain("/user-export/by-token");
|
|
474
488
|
});
|
|
475
489
|
|
|
476
490
|
test("Audit-Update: useCount + IP/UA aus httpRoute-Headers (nicht aus payload)", async () => {
|
|
@@ -495,6 +509,47 @@ describe("r.httpRoute :: /user-export/by-token (Magic-Link e2e)", () => {
|
|
|
495
509
|
expect(row?.lastUsedFromIp).toBe("198.51.100.7");
|
|
496
510
|
expect(row?.lastUsedUserAgent).toBe("e2e-test/2.0");
|
|
497
511
|
});
|
|
512
|
+
|
|
513
|
+
test("POST-Exchange (Fragment-Pfad): happy-path + IP/UA-Audit aus httpRoute-Headers", async () => {
|
|
514
|
+
const { jobId, plainToken } = await seedDoneJobWithToken();
|
|
515
|
+
|
|
516
|
+
const res = await stack.app.fetch(
|
|
517
|
+
new Request("http://test/user-export/by-token", {
|
|
518
|
+
method: "POST",
|
|
519
|
+
headers: {
|
|
520
|
+
"content-type": "application/json",
|
|
521
|
+
"user-agent": "e2e-test/3.0",
|
|
522
|
+
"x-forwarded-for": "203.0.113.9, 10.0.0.1",
|
|
523
|
+
},
|
|
524
|
+
body: JSON.stringify({ token: plainToken }),
|
|
525
|
+
}),
|
|
526
|
+
);
|
|
527
|
+
expect(res.status).toBe(200);
|
|
528
|
+
const body = (await res.json()) as { data?: { url?: string } };
|
|
529
|
+
expect(body.data?.url).toBeTruthy();
|
|
530
|
+
|
|
531
|
+
const [row] = (await selectMany(stack.db, exportDownloadTokensTable, { jobId })) as Array<{
|
|
532
|
+
useCount: number;
|
|
533
|
+
lastUsedFromIp: string | null;
|
|
534
|
+
lastUsedUserAgent: string | null;
|
|
535
|
+
}>;
|
|
536
|
+
expect(row?.useCount).toBe(1);
|
|
537
|
+
expect(row?.lastUsedFromIp).toBe("203.0.113.9");
|
|
538
|
+
expect(row?.lastUsedUserAgent).toBe("e2e-test/3.0");
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
test("POST-Exchange: missing token → 400", async () => {
|
|
542
|
+
const res = await stack.app.fetch(
|
|
543
|
+
new Request("http://test/user-export/by-token", {
|
|
544
|
+
method: "POST",
|
|
545
|
+
headers: { "content-type": "application/json" },
|
|
546
|
+
body: JSON.stringify({}),
|
|
547
|
+
}),
|
|
548
|
+
);
|
|
549
|
+
expect(res.status).toBe(400);
|
|
550
|
+
const body = (await res.json()) as { error?: string };
|
|
551
|
+
expect(body.error).toBe("missing_token");
|
|
552
|
+
});
|
|
498
553
|
});
|
|
499
554
|
|
|
500
555
|
describe("download-by-job :: cross-user + cross-tenant", () => {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// (never the raw ciphertext) without one.
|
|
5
5
|
|
|
6
6
|
import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
|
|
7
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
7
8
|
import { asRawClient, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
8
9
|
import {
|
|
9
10
|
buildEntityTable,
|
|
@@ -74,6 +75,7 @@ beforeAll(async () => {
|
|
|
74
75
|
createUserFeature(),
|
|
75
76
|
createDataRetentionFeature(),
|
|
76
77
|
createComplianceProfilesFeature(),
|
|
78
|
+
authFoundationFeature,
|
|
77
79
|
createSessionsFeature(),
|
|
78
80
|
createUserDataRightsFeature(),
|
|
79
81
|
vaultFeature,
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// reale Postgres + Drizzle-customType-Codec-Path braucht.
|
|
11
11
|
|
|
12
12
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
13
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
13
14
|
import { asRawClient, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
14
15
|
import { extractPgError } from "@cosmicdrift/kumiko-framework/db";
|
|
15
16
|
import {
|
|
@@ -43,6 +44,7 @@ beforeAll(async () => {
|
|
|
43
44
|
createUserFeature(),
|
|
44
45
|
createDataRetentionFeature(),
|
|
45
46
|
createComplianceProfilesFeature(),
|
|
47
|
+
authFoundationFeature,
|
|
46
48
|
createSessionsFeature(),
|
|
47
49
|
|
|
48
50
|
createUserDataRightsFeature(),
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// tenant A saw only tenant A's export jobs, never tenant B's.
|
|
7
7
|
|
|
8
8
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
9
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
9
10
|
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
10
11
|
import {
|
|
11
12
|
createTestUser,
|
|
@@ -40,6 +41,7 @@ beforeAll(async () => {
|
|
|
40
41
|
createUserFeature(),
|
|
41
42
|
createDataRetentionFeature(),
|
|
42
43
|
createComplianceProfilesFeature(),
|
|
44
|
+
authFoundationFeature,
|
|
43
45
|
createSessionsFeature(),
|
|
44
46
|
createUserDataRightsFeature(),
|
|
45
47
|
],
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// Datei ihre Bytes dauerhaft auf Disk (Issue gefunden im Review zu #177).
|
|
7
7
|
|
|
8
8
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
9
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
9
10
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
10
11
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
11
12
|
import { type JobContext, SYSTEM_USER_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -78,6 +79,7 @@ beforeAll(async () => {
|
|
|
78
79
|
createTestFileProviderFeature(provider, "test"),
|
|
79
80
|
createDataRetentionFeature(),
|
|
80
81
|
createComplianceProfilesFeature(),
|
|
82
|
+
authFoundationFeature,
|
|
81
83
|
createSessionsFeature(),
|
|
82
84
|
createUserDataRightsFeature(),
|
|
83
85
|
createUserDataRightsDefaultsFeature(),
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// still unvollständig).
|
|
11
11
|
|
|
12
12
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
13
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
13
14
|
import { asRawClient, fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
14
15
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
15
16
|
import { SYSTEM_USER_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -86,6 +87,7 @@ beforeAll(async () => {
|
|
|
86
87
|
createTestFileProviderFeature(flakyProvider, "test"),
|
|
87
88
|
createDataRetentionFeature(),
|
|
88
89
|
createComplianceProfilesFeature(),
|
|
90
|
+
authFoundationFeature,
|
|
89
91
|
createSessionsFeature(),
|
|
90
92
|
createUserDataRightsFeature(),
|
|
91
93
|
createUserDataRightsDefaultsFeature(),
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
// die Policy entscheidet pro Tenant.
|
|
17
17
|
|
|
18
18
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
19
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
19
20
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
20
21
|
import {
|
|
21
22
|
createEventStoreExecutor,
|
|
@@ -68,6 +69,7 @@ beforeAll(async () => {
|
|
|
68
69
|
createFilesFeature(),
|
|
69
70
|
createDataRetentionFeature(),
|
|
70
71
|
createComplianceProfilesFeature(),
|
|
72
|
+
authFoundationFeature,
|
|
71
73
|
createSessionsFeature(),
|
|
72
74
|
createUserDataRightsFeature(),
|
|
73
75
|
createUserDataRightsDefaultsFeature(),
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// assertion below go red.
|
|
12
12
|
|
|
13
13
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
14
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
14
15
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
15
16
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
16
17
|
import { type SessionUser, SYSTEM_USER_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -94,6 +95,7 @@ beforeAll(async () => {
|
|
|
94
95
|
createTestFileProviderFeature(provider, "test"),
|
|
95
96
|
createDataRetentionFeature(),
|
|
96
97
|
createComplianceProfilesFeature(),
|
|
98
|
+
authFoundationFeature,
|
|
97
99
|
createSessionsFeature(),
|
|
98
100
|
createUserDataRightsFeature(),
|
|
99
101
|
createUserDataRightsDefaultsFeature(),
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// 0 rows after the owner column is nulled).
|
|
15
15
|
|
|
16
16
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
17
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
17
18
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
18
19
|
import {
|
|
19
20
|
createEntity,
|
|
@@ -148,6 +149,7 @@ beforeAll(async () => {
|
|
|
148
149
|
stack = await setupTestStack({
|
|
149
150
|
features: [
|
|
150
151
|
createUserFeature(),
|
|
152
|
+
authFoundationFeature,
|
|
151
153
|
createSessionsFeature(),
|
|
152
154
|
createDataRetentionFeature(),
|
|
153
155
|
createComplianceProfilesFeature(),
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// functional registry instance; the end-to-end cascade is covered consumer-side.
|
|
9
9
|
|
|
10
10
|
import { afterAll, beforeAll, beforeEach, expect, test } from "bun:test";
|
|
11
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
11
12
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
12
13
|
import {
|
|
13
14
|
createEntity,
|
|
@@ -66,6 +67,7 @@ beforeAll(async () => {
|
|
|
66
67
|
stack = await setupTestStack({
|
|
67
68
|
features: [
|
|
68
69
|
createUserFeature(),
|
|
70
|
+
authFoundationFeature,
|
|
69
71
|
createSessionsFeature(),
|
|
70
72
|
createDataRetentionFeature(),
|
|
71
73
|
createComplianceProfilesFeature(),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { access, validateBoot } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { access, defineFeature, validateBoot } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { createFilesFeature } from "@cosmicdrift/kumiko-framework/files";
|
|
4
|
+
import { EXT_SESSION_STORE } from "../../auth-foundation";
|
|
4
5
|
import { createComplianceProfilesFeature } from "../../compliance-profiles/feature";
|
|
5
6
|
import { createConfigFeature } from "../../config/feature";
|
|
6
7
|
import { createDataRetentionFeature } from "../../data-retention/feature";
|
|
@@ -17,6 +18,13 @@ import { createUserDataRightsFeature } from "../feature";
|
|
|
17
18
|
// entities (export-job, download-attempt) are r.entity, not direct-write stores,
|
|
18
19
|
// so an entityList binding is rebuild-safe (unlike jobs/sessions read-models).
|
|
19
20
|
|
|
21
|
+
// Stub instead of the real auth-foundation — this test is about screen
|
|
22
|
+
// wiring, not auth; only sessions' sessionStore registration needs an owner
|
|
23
|
+
// present.
|
|
24
|
+
const sessionStoreOwnerStub = defineFeature("auth-foundation", (r) => {
|
|
25
|
+
r.extendsRegistrar(EXT_SESSION_STORE, {});
|
|
26
|
+
});
|
|
27
|
+
|
|
20
28
|
describe("user-data-rights read-only inspector screens", () => {
|
|
21
29
|
// A realistic assembly: mounting user-data-rights without its default hooks
|
|
22
30
|
// now fails the V3 boot gate (the framework `user` entity holds PII with no
|
|
@@ -25,6 +33,7 @@ describe("user-data-rights read-only inspector screens", () => {
|
|
|
25
33
|
const features = [
|
|
26
34
|
createConfigFeature(),
|
|
27
35
|
createUserFeature(),
|
|
36
|
+
sessionStoreOwnerStub,
|
|
28
37
|
createSessionsFeature(),
|
|
29
38
|
createDataRetentionFeature(),
|
|
30
39
|
createComplianceProfilesFeature(),
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// #624: "App mountet mail-foundation+transport → GDPR-Mails ohne Callback-Code".
|
|
11
11
|
|
|
12
12
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
13
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
13
14
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
14
15
|
import { fileRefsTable } from "@cosmicdrift/kumiko-framework/files";
|
|
15
16
|
import {
|
|
@@ -56,6 +57,7 @@ beforeAll(async () => {
|
|
|
56
57
|
createFilesFeature(),
|
|
57
58
|
createDataRetentionFeature(),
|
|
58
59
|
createComplianceProfilesFeature(),
|
|
60
|
+
authFoundationFeature,
|
|
59
61
|
createSessionsFeature(),
|
|
60
62
|
mailFoundationFeature,
|
|
61
63
|
mailTransportInMemoryFeature,
|