@cosmicdrift/kumiko-bundled-features 0.158.2 → 0.160.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +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-client.test.ts +525 -0
- package/src/auth-email-password/web/__tests__/auth-gate.test.tsx +65 -3
- package/src/auth-email-password/web/__tests__/confirm-account-unlock-screen.test.tsx +57 -0
- package/src/auth-email-password/web/__tests__/invite-accept-screen.test.tsx +126 -2
- package/src/auth-email-password/web/__tests__/login-screen.test.tsx +125 -0
- package/src/auth-email-password/web/__tests__/request-account-unlock-screen.test.tsx +131 -0
- 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 +44 -0
- package/src/auth-mfa/handlers/verify.write.ts +1 -1
- 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__/app-overrides.test.ts +11 -0
- package/src/config/__tests__/backing-secrets.integration.test.ts +18 -0
- package/src/config/__tests__/cascade.integration.test.ts +39 -0
- package/src/config/__tests__/config.integration.test.ts +14 -0
- package/src/config/__tests__/pii-encrypted.integration.test.ts +23 -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/__tests__/watch-supervisor.integration.test.ts +154 -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-imap/__tests__/plugin-mocked.test.ts +331 -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 +23 -8
- 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/tags/web/__tests__/tag-manager.test.tsx +277 -8
- 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 +184 -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/web/__tests__/privacy-center-screen.test.tsx +1 -0
- 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/__tests__/profile-screen.test.tsx +1 -0
- package/src/user-profile/handlers/change-email.write.ts +1 -1
- package/src/config/__tests__/encrypted-legacy-migration.integration.test.ts +0 -179
package/src/user-data-rights/__tests__/read-users-rebuild-survives-lifecycle.integration.test.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
15
15
|
import { randomBytes } from "node:crypto";
|
|
16
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
16
17
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
17
18
|
import {
|
|
18
19
|
createRegistry,
|
|
@@ -58,7 +59,7 @@ import { createDataRetentionFeature } from "../../data-retention";
|
|
|
58
59
|
import { createSessionsFeature } from "../../sessions";
|
|
59
60
|
import { userSessionEntity, userSessionTable } from "../../sessions/schema/user-session";
|
|
60
61
|
import { createSessionCallbacks, type SessionCallbacks } from "../../sessions/session-callbacks";
|
|
61
|
-
import { sessionCallbacksFromLateBound } from "../../sessions/testing";
|
|
62
|
+
import { sessionCallbacksFromLateBound, withMintedSession } from "../../sessions/testing";
|
|
62
63
|
import { hashPassword } from "../../shared";
|
|
63
64
|
import { createTenantFeature, tenantMembershipsTable } from "../../tenant";
|
|
64
65
|
import { tenantEntity } from "../../tenant/schema/tenant";
|
|
@@ -91,6 +92,7 @@ function buildFeatures() {
|
|
|
91
92
|
createDataRetentionFeature(),
|
|
92
93
|
createComplianceProfilesFeature(),
|
|
93
94
|
createAuthEmailPasswordFeature(),
|
|
95
|
+
authFoundationFeature,
|
|
94
96
|
createSessionsFeature(),
|
|
95
97
|
createUserDataRightsFeature(),
|
|
96
98
|
];
|
|
@@ -137,6 +139,12 @@ beforeEach(async () => {
|
|
|
137
139
|
]);
|
|
138
140
|
});
|
|
139
141
|
|
|
142
|
+
// Any actor hitting a write handler needs a live sid once sessionChecker
|
|
143
|
+
// is wired.
|
|
144
|
+
function mintActor(user: { id: string; tenantId: TenantId; roles: readonly string[] }) {
|
|
145
|
+
return withMintedSession((u, meta) => callbacks.get().sessionCreator(u, meta), user);
|
|
146
|
+
}
|
|
147
|
+
|
|
140
148
|
describe("#494 :: read_users-Rebuild bewahrt Lifecycle-State", () => {
|
|
141
149
|
test("Restricted-Status ueberlebt einen Projection-Rebuild (T_create != T_active)", async () => {
|
|
142
150
|
// Diskriminierende Praemisse: user.created landet auf systemAdmin.tenantId
|
|
@@ -149,7 +157,7 @@ describe("#494 :: read_users-Rebuild bewahrt Lifecycle-State", () => {
|
|
|
149
157
|
const created = await stack.http.writeOk<{ id: string }>(
|
|
150
158
|
UserHandlers.create,
|
|
151
159
|
{ email: ALICE_EMAIL, passwordHash: hash, displayName: "Alice" },
|
|
152
|
-
TestUsers.systemAdmin,
|
|
160
|
+
await mintActor(TestUsers.systemAdmin),
|
|
153
161
|
);
|
|
154
162
|
// Mitgliedschaft + Lifecycle auf einem ANDEREN, aktiven Tenant.
|
|
155
163
|
await seedTenantMembership(stack.db, {
|
|
@@ -158,7 +166,7 @@ describe("#494 :: read_users-Rebuild bewahrt Lifecycle-State", () => {
|
|
|
158
166
|
roles: ["Member"],
|
|
159
167
|
});
|
|
160
168
|
|
|
161
|
-
const aliceActive = { id: created.id, tenantId: T_ACTIVE, roles: ["Member"] };
|
|
169
|
+
const aliceActive = await mintActor({ id: created.id, tenantId: T_ACTIVE, roles: ["Member"] });
|
|
162
170
|
const restricted = await stack.http.writeOk<{ status: string }>(RESTRICT, {}, aliceActive);
|
|
163
171
|
expect(restricted.status).toBe(USER_STATUS.Restricted);
|
|
164
172
|
|
|
@@ -187,7 +195,7 @@ describe("#494 :: read_users-Rebuild bewahrt Lifecycle-State", () => {
|
|
|
187
195
|
const created = await stack.http.writeOk<{ id: string }>(
|
|
188
196
|
UserHandlers.create,
|
|
189
197
|
{ email: "grace.rebuild@example.com", passwordHash: hash, displayName: "Grace" },
|
|
190
|
-
TestUsers.systemAdmin,
|
|
198
|
+
await mintActor(TestUsers.systemAdmin),
|
|
191
199
|
);
|
|
192
200
|
|
|
193
201
|
const T = getTemporal();
|
|
@@ -218,7 +226,7 @@ describe("#494 :: read_users-Rebuild bewahrt Lifecycle-State", () => {
|
|
|
218
226
|
const created = await stack.http.writeOk<{ id: string }>(
|
|
219
227
|
UserHandlers.create,
|
|
220
228
|
{ email: "legacy.rebuild@example.com", passwordHash: hash, displayName: "Legacy" },
|
|
221
|
-
TestUsers.systemAdmin,
|
|
229
|
+
await mintActor(TestUsers.systemAdmin),
|
|
222
230
|
);
|
|
223
231
|
|
|
224
232
|
// Prae-Fix-Zustand simulieren: roher Write OHNE Event.
|
|
@@ -256,14 +264,14 @@ describe("#494 :: read_users-Rebuild bewahrt Lifecycle-State", () => {
|
|
|
256
264
|
const goodRow = await stack.http.writeOk<{ id: string }>(
|
|
257
265
|
UserHandlers.create,
|
|
258
266
|
{ email: "healthy.rebuild@example.com", passwordHash: hash, displayName: "Healthy" },
|
|
259
|
-
TestUsers.systemAdmin,
|
|
267
|
+
await mintActor(TestUsers.systemAdmin),
|
|
260
268
|
);
|
|
261
269
|
await updateRows(stack.db, userTable, { status: USER_STATUS.Restricted }, { id: goodRow.id });
|
|
262
270
|
|
|
263
271
|
const badRow = await stack.http.writeOk<{ id: string }>(
|
|
264
272
|
UserHandlers.create,
|
|
265
273
|
{ email: "corrupt.rebuild@example.com", passwordHash: hash, displayName: "Corrupt" },
|
|
266
|
-
TestUsers.systemAdmin,
|
|
274
|
+
await mintActor(TestUsers.systemAdmin),
|
|
267
275
|
);
|
|
268
276
|
await updateRows(stack.db, userTable, { status: USER_STATUS.Restricted }, { id: badRow.id });
|
|
269
277
|
// userEntity is systemStream:true (schema/user.ts) — its event stream
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// hier; der Frist-Ablauf-Cleanup folgt mit S2.U5b.
|
|
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 { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
14
15
|
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
15
16
|
import {
|
|
@@ -57,6 +58,7 @@ const features = [
|
|
|
57
58
|
createUserFeature(),
|
|
58
59
|
createDataRetentionFeature(),
|
|
59
60
|
createComplianceProfilesFeature(),
|
|
61
|
+
authFoundationFeature,
|
|
60
62
|
createSessionsFeature(),
|
|
61
63
|
|
|
62
64
|
createUserDataRightsFeature(),
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// Properties — dieser Test verifiziert sie end-to-end.
|
|
9
9
|
|
|
10
10
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
11
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
11
12
|
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
12
13
|
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
13
14
|
import {
|
|
@@ -64,6 +65,7 @@ beforeAll(async () => {
|
|
|
64
65
|
createUserFeature(),
|
|
65
66
|
createDataRetentionFeature(),
|
|
66
67
|
createComplianceProfilesFeature(),
|
|
68
|
+
authFoundationFeature,
|
|
67
69
|
createSessionsFeature(),
|
|
68
70
|
|
|
69
71
|
createUserDataRightsFeature({ sendDeletionRequestedEmail }),
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
// hasJob=false wenn nichts da
|
|
19
19
|
|
|
20
20
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
21
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
21
22
|
import { asRawClient, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
22
23
|
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
23
24
|
import {
|
|
@@ -53,6 +54,7 @@ beforeAll(async () => {
|
|
|
53
54
|
createUserFeature(),
|
|
54
55
|
createDataRetentionFeature(),
|
|
55
56
|
createComplianceProfilesFeature(),
|
|
57
|
+
authFoundationFeature,
|
|
56
58
|
createSessionsFeature(),
|
|
57
59
|
|
|
58
60
|
createUserDataRightsFeature(),
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { afterAll, beforeAll, beforeEach, 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 { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
16
17
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
17
18
|
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
@@ -44,7 +45,7 @@ import { createSessionsFeature } from "../../sessions";
|
|
|
44
45
|
import { SessionHandlers } from "../../sessions/constants";
|
|
45
46
|
import { userSessionEntity, userSessionTable } from "../../sessions/schema/user-session";
|
|
46
47
|
import { createSessionCallbacks, type SessionCallbacks } from "../../sessions/session-callbacks";
|
|
47
|
-
import { sessionCallbacksFromLateBound } from "../../sessions/testing";
|
|
48
|
+
import { sessionCallbacksFromLateBound, withMintedSession } from "../../sessions/testing";
|
|
48
49
|
import { hashPassword } from "../../shared";
|
|
49
50
|
import { createTenantFeature, tenantMembershipsTable } from "../../tenant";
|
|
50
51
|
import { tenantEntity } from "../../tenant/schema/tenant";
|
|
@@ -77,6 +78,7 @@ beforeAll(async () => {
|
|
|
77
78
|
createDataRetentionFeature(),
|
|
78
79
|
createComplianceProfilesFeature(),
|
|
79
80
|
createAuthEmailPasswordFeature(),
|
|
81
|
+
authFoundationFeature,
|
|
80
82
|
createSessionsFeature(),
|
|
81
83
|
createUserDataRightsFeature(),
|
|
82
84
|
],
|
|
@@ -115,10 +117,14 @@ async function seedAliceWithMembership(
|
|
|
115
117
|
status: string = USER_STATUS.Active,
|
|
116
118
|
): Promise<{ userId: string }> {
|
|
117
119
|
const hash = await hashPassword(ALICE_PW);
|
|
120
|
+
const actor = await withMintedSession(
|
|
121
|
+
(user, meta) => callbacks.get().sessionCreator(user, meta),
|
|
122
|
+
TestUsers.systemAdmin,
|
|
123
|
+
);
|
|
118
124
|
const created = await stack.http.writeOk<{ id: string }>(
|
|
119
125
|
UserHandlers.create,
|
|
120
126
|
{ email: ALICE_EMAIL, passwordHash: hash, displayName: "Alice" },
|
|
121
|
-
|
|
127
|
+
actor,
|
|
122
128
|
);
|
|
123
129
|
if (status !== USER_STATUS.Active) {
|
|
124
130
|
await updateRows(stack.db, userTable, { status }, { id: created.id });
|
|
@@ -136,6 +142,12 @@ async function login(email: string, password: string): Promise<{ status: number;
|
|
|
136
142
|
return { status: res.status, body: await res.json() };
|
|
137
143
|
}
|
|
138
144
|
|
|
145
|
+
// Any actor hitting a write handler needs a live sid once sessionChecker
|
|
146
|
+
// is wired (same gate as seedAliceWithMembership's bootstrap actor above).
|
|
147
|
+
function mintActor(user: { id: string; tenantId: TenantId; roles: readonly string[] }) {
|
|
148
|
+
return withMintedSession((u, meta) => callbacks.get().sessionCreator(u, meta), user);
|
|
149
|
+
}
|
|
150
|
+
|
|
139
151
|
function reasonOf(err: { details?: unknown }): string | undefined {
|
|
140
152
|
return (err.details as { reason?: string } | undefined)?.reason;
|
|
141
153
|
}
|
|
@@ -156,11 +168,11 @@ describe("S2.U6 :: restrict-account state-transitions", () => {
|
|
|
156
168
|
expect(sessionsBefore.every((s) => s.revokedAt === null)).toBe(true);
|
|
157
169
|
|
|
158
170
|
// Restrict-Account.
|
|
159
|
-
const aliceUser = {
|
|
171
|
+
const aliceUser = await mintActor({
|
|
160
172
|
id: userId,
|
|
161
173
|
tenantId: TENANT,
|
|
162
174
|
roles: ["Member"],
|
|
163
|
-
};
|
|
175
|
+
});
|
|
164
176
|
const result = await stack.http.writeOk<{ userId: string; status: string }>(
|
|
165
177
|
RESTRICT,
|
|
166
178
|
{},
|
|
@@ -182,17 +194,36 @@ describe("S2.U6 :: restrict-account state-transitions", () => {
|
|
|
182
194
|
expect(sessionsAfter.every((s) => s.revokedAt !== null)).toBe(true);
|
|
183
195
|
});
|
|
184
196
|
|
|
197
|
+
test("non-admin targets another user's account: 403 admin_required_for_other_user", async () => {
|
|
198
|
+
const { userId: targetId } = await seedAliceWithMembership();
|
|
199
|
+
const attacker = await mintActor({
|
|
200
|
+
id: crypto.randomUUID(),
|
|
201
|
+
tenantId: TENANT,
|
|
202
|
+
roles: ["Member"],
|
|
203
|
+
});
|
|
204
|
+
const err = await stack.http.writeErr(RESTRICT, { userId: targetId }, attacker);
|
|
205
|
+
expect(err.httpStatus).toBe(403);
|
|
206
|
+
expect(reasonOf(err)).toBe("admin_required_for_other_user");
|
|
207
|
+
|
|
208
|
+
const userRow = (await selectMany(stack.db, userTable, { id: targetId })) as Array<{
|
|
209
|
+
status: string;
|
|
210
|
+
}>;
|
|
211
|
+
expect(userRow[0]?.status).toBe(USER_STATUS.Active);
|
|
212
|
+
});
|
|
213
|
+
|
|
185
214
|
test("Restricted → Restricted (Idempotenz-Guard): 422 already_restricted", async () => {
|
|
186
215
|
const { userId } = await seedAliceWithMembership(USER_STATUS.Restricted);
|
|
187
|
-
|
|
188
|
-
|
|
216
|
+
// Alice's own session is blocked while Restricted — an admin targets
|
|
217
|
+
// her account instead, same as production would have to.
|
|
218
|
+
const admin = await mintActor(TestUsers.admin);
|
|
219
|
+
const err = await stack.http.writeErr(RESTRICT, { userId }, admin);
|
|
189
220
|
expect(err.httpStatus).toBe(422);
|
|
190
221
|
expect(reasonOf(err)).toBe("already_restricted");
|
|
191
222
|
});
|
|
192
223
|
|
|
193
224
|
test("DeletionRequested → restrict-account: 422 user_not_in_active_state", async () => {
|
|
194
225
|
const { userId } = await seedAliceWithMembership(USER_STATUS.DeletionRequested);
|
|
195
|
-
const aliceUser = { id: userId, tenantId: TENANT, roles: ["Member"] };
|
|
226
|
+
const aliceUser = await mintActor({ id: userId, tenantId: TENANT, roles: ["Member"] });
|
|
196
227
|
const err = await stack.http.writeErr(RESTRICT, {}, aliceUser);
|
|
197
228
|
expect(reasonOf(err)).toBe("user_not_in_active_state");
|
|
198
229
|
});
|
|
@@ -201,9 +232,11 @@ describe("S2.U6 :: restrict-account state-transitions", () => {
|
|
|
201
232
|
describe("S2.U6 :: lift-restriction state-transitions", () => {
|
|
202
233
|
test("Restricted → Active: status flippt zurueck", async () => {
|
|
203
234
|
const { userId } = await seedAliceWithMembership(USER_STATUS.Restricted);
|
|
204
|
-
|
|
235
|
+
// lift-restriction is operator-only (access.admin) — Alice's own
|
|
236
|
+
// session is blocked while Restricted anyway.
|
|
237
|
+
const admin = await mintActor(TestUsers.admin);
|
|
205
238
|
|
|
206
|
-
const result = await stack.http.writeOk<{ status: string }>(LIFT, {},
|
|
239
|
+
const result = await stack.http.writeOk<{ status: string }>(LIFT, { userId }, admin);
|
|
207
240
|
expect(result.status).toBe(USER_STATUS.Active);
|
|
208
241
|
|
|
209
242
|
const userRow = (await selectMany(stack.db, userTable, { id: userId })) as Array<{
|
|
@@ -214,15 +247,15 @@ describe("S2.U6 :: lift-restriction state-transitions", () => {
|
|
|
214
247
|
|
|
215
248
|
test("Active → lift-restriction: 422 not_restricted (Idempotenz-Guard)", async () => {
|
|
216
249
|
const { userId } = await seedAliceWithMembership(USER_STATUS.Active);
|
|
217
|
-
const
|
|
218
|
-
const err = await stack.http.writeErr(LIFT, {},
|
|
250
|
+
const admin = await mintActor(TestUsers.admin);
|
|
251
|
+
const err = await stack.http.writeErr(LIFT, { userId }, admin);
|
|
219
252
|
expect(reasonOf(err)).toBe("not_restricted");
|
|
220
253
|
});
|
|
221
254
|
|
|
222
255
|
test("DeletionRequested → lift-restriction: 422 not_restricted", async () => {
|
|
223
256
|
const { userId } = await seedAliceWithMembership(USER_STATUS.DeletionRequested);
|
|
224
|
-
const
|
|
225
|
-
const err = await stack.http.writeErr(LIFT, {},
|
|
257
|
+
const admin = await mintActor(TestUsers.admin);
|
|
258
|
+
const err = await stack.http.writeErr(LIFT, { userId }, admin);
|
|
226
259
|
expect(reasonOf(err)).toBe("not_restricted");
|
|
227
260
|
});
|
|
228
261
|
});
|
|
@@ -286,11 +319,11 @@ describe("S2.U6 :: Cross-Feature sessions.revokeAllForUser direct", () => {
|
|
|
286
319
|
expect(liveBefore.length).toBe(2);
|
|
287
320
|
|
|
288
321
|
// System-Caller.
|
|
289
|
-
const systemUser = {
|
|
322
|
+
const systemUser = await mintActor({
|
|
290
323
|
id: "00000000-0000-4000-8000-000000000000",
|
|
291
324
|
tenantId: TENANT,
|
|
292
325
|
roles: ["SystemAdmin"],
|
|
293
|
-
};
|
|
326
|
+
});
|
|
294
327
|
const result = await stack.http.writeOk<{ count: number; userId: string }>(
|
|
295
328
|
SessionHandlers.revokeAllForUser,
|
|
296
329
|
{ userId },
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// jeden Export auf "failed" setzte).
|
|
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 { type JobContext, SYSTEM_USER_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
15
16
|
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
@@ -65,6 +66,7 @@ beforeAll(async () => {
|
|
|
65
66
|
fileProviderInMemoryFeature,
|
|
66
67
|
mailFoundationFeature,
|
|
67
68
|
mailTransportInMemoryFeature,
|
|
69
|
+
authFoundationFeature,
|
|
68
70
|
createSessionsFeature(),
|
|
69
71
|
// appExportDownloadUrl set → the default export-ready mail is enabled, so
|
|
70
72
|
// this also proves the export cron's mail bridge end-to-end (C6).
|
|
@@ -17,6 +17,7 @@ import { spawn } from "node:child_process";
|
|
|
17
17
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
18
18
|
import { tmpdir } from "node:os";
|
|
19
19
|
import { join } from "node:path";
|
|
20
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
20
21
|
import { asRawClient, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
21
22
|
import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
22
23
|
import {
|
|
@@ -59,6 +60,7 @@ beforeAll(async () => {
|
|
|
59
60
|
createUserFeature(),
|
|
60
61
|
createDataRetentionFeature(),
|
|
61
62
|
createComplianceProfilesFeature(),
|
|
63
|
+
authFoundationFeature,
|
|
62
64
|
createSessionsFeature(),
|
|
63
65
|
|
|
64
66
|
createUserDataRightsFeature(),
|
|
@@ -385,6 +387,47 @@ describe("runExportJobs :: storage-cleanup", () => {
|
|
|
385
387
|
expect(result.cleanedJobIds).not.toContain(jobId);
|
|
386
388
|
expect(await provider.exists(storageKey)).toBe(true);
|
|
387
389
|
});
|
|
390
|
+
|
|
391
|
+
test("storage-delete Throw → Job bleibt uncleaned (retry naechster Pass)", async () => {
|
|
392
|
+
const jobId = await seedPendingJob();
|
|
393
|
+
const T = getTemporal();
|
|
394
|
+
const longAgo = T.Instant.fromEpochMilliseconds(Date.now() - 365 * 24 * 60 * 60 * 1000);
|
|
395
|
+
const storageKey = `${tenantA}/exports/${jobId}.zip`;
|
|
396
|
+
const provider = await buildProvider(tenantA);
|
|
397
|
+
await provider.write(storageKey, new Uint8Array([7, 8, 9]));
|
|
398
|
+
|
|
399
|
+
await updateRows(
|
|
400
|
+
stack.db,
|
|
401
|
+
exportJobsTable,
|
|
402
|
+
{
|
|
403
|
+
status: EXPORT_JOB_STATUS.Done,
|
|
404
|
+
startedAt: longAgo,
|
|
405
|
+
completedAt: longAgo,
|
|
406
|
+
downloadStorageKey: storageKey,
|
|
407
|
+
expiresAt: longAgo,
|
|
408
|
+
},
|
|
409
|
+
{ id: jobId },
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
const result = await runExportJobs({
|
|
413
|
+
db: stack.db,
|
|
414
|
+
registry: stack.registry,
|
|
415
|
+
buildStorageProvider: async () => ({
|
|
416
|
+
...provider,
|
|
417
|
+
delete: async () => {
|
|
418
|
+
throw new Error("synthetic storage delete failure");
|
|
419
|
+
},
|
|
420
|
+
}),
|
|
421
|
+
now: NOW(),
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
expect(result.cleanedJobIds).not.toContain(jobId);
|
|
425
|
+
expect(await provider.exists(storageKey)).toBe(true);
|
|
426
|
+
const [row] = (await selectMany(stack.db, exportJobsTable, { id: jobId })) as Array<{
|
|
427
|
+
downloadStorageKey: string | null;
|
|
428
|
+
}>;
|
|
429
|
+
expect(row?.downloadStorageKey).toBe(storageKey);
|
|
430
|
+
});
|
|
388
431
|
});
|
|
389
432
|
|
|
390
433
|
describe("runExportJobs :: idempotency", () => {
|
|
@@ -706,6 +749,145 @@ describe("runExportJobs :: Atom 4a download-tokens", () => {
|
|
|
706
749
|
expect(row?.errorMessage).toMatch(/Token-Creation failed/);
|
|
707
750
|
});
|
|
708
751
|
|
|
752
|
+
test("failed-Job: sendExportFailedEmail wird mit errorMessage + userEmail gerufen", async () => {
|
|
753
|
+
const jobId = await seedPendingJob();
|
|
754
|
+
|
|
755
|
+
// Force a deterministic failure the same way as the UNIQUE-violation
|
|
756
|
+
// test above — pre-seed a token row for this jobId so the worker's
|
|
757
|
+
// tokenCrud.create hits the unique index and the job flips to failed.
|
|
758
|
+
await asRawClient(stack.db).unsafe(
|
|
759
|
+
`
|
|
760
|
+
INSERT INTO read_export_download_tokens
|
|
761
|
+
(id, tenant_id, job_id, token_hash, issued_at, expires_at, version, inserted_at, modified_at)
|
|
762
|
+
VALUES (
|
|
763
|
+
gen_random_uuid(),
|
|
764
|
+
$1,
|
|
765
|
+
$2,
|
|
766
|
+
$3,
|
|
767
|
+
now(),
|
|
768
|
+
now() + interval '7 days',
|
|
769
|
+
1,
|
|
770
|
+
now(),
|
|
771
|
+
now()
|
|
772
|
+
)
|
|
773
|
+
`,
|
|
774
|
+
[tenantA, jobId, "existing-hash"],
|
|
775
|
+
);
|
|
776
|
+
|
|
777
|
+
type SentArgs = {
|
|
778
|
+
userId: string;
|
|
779
|
+
userEmail: string;
|
|
780
|
+
jobId: string;
|
|
781
|
+
errorMessage: string;
|
|
782
|
+
tenantId: string;
|
|
783
|
+
};
|
|
784
|
+
const sentEmails: SentArgs[] = [];
|
|
785
|
+
|
|
786
|
+
const result = await runExportJobs({
|
|
787
|
+
db: stack.db,
|
|
788
|
+
registry: stack.registry,
|
|
789
|
+
buildStorageProvider: buildProvider,
|
|
790
|
+
now: NOW(),
|
|
791
|
+
sendExportFailedEmail: async (args) => {
|
|
792
|
+
sentEmails.push(args);
|
|
793
|
+
},
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
expect(result.failedJobIds).toContain(jobId);
|
|
797
|
+
expect(sentEmails).toHaveLength(1);
|
|
798
|
+
const sent = sentEmails[0];
|
|
799
|
+
if (!sent) throw new Error("expected 1 sent email");
|
|
800
|
+
expect(sent.userId).toBe(String(aliceUser.id));
|
|
801
|
+
expect(sent.userEmail).toBe("alice@example.com");
|
|
802
|
+
expect(sent.jobId).toBe(jobId);
|
|
803
|
+
expect(sent.errorMessage).toMatch(/Token-Creation failed/);
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
test("failed-Job ohne Callback: kein Email, Worker-Run succeeded", async () => {
|
|
807
|
+
const jobId = await seedPendingJob();
|
|
808
|
+
await asRawClient(stack.db).unsafe(
|
|
809
|
+
`
|
|
810
|
+
INSERT INTO read_export_download_tokens
|
|
811
|
+
(id, tenant_id, job_id, token_hash, issued_at, expires_at, version, inserted_at, modified_at)
|
|
812
|
+
VALUES (gen_random_uuid(), $1, $2, $3, now(), now() + interval '7 days', 1, now(), now())
|
|
813
|
+
`,
|
|
814
|
+
[tenantA, jobId, "existing-hash"],
|
|
815
|
+
);
|
|
816
|
+
|
|
817
|
+
const result = await runExportJobs({
|
|
818
|
+
db: stack.db,
|
|
819
|
+
registry: stack.registry,
|
|
820
|
+
buildStorageProvider: buildProvider,
|
|
821
|
+
now: NOW(),
|
|
822
|
+
// Kein sendExportFailedEmail
|
|
823
|
+
});
|
|
824
|
+
expect(result.failedJobIds).toContain(jobId);
|
|
825
|
+
// Kein Email, kein Throw — Worker laeuft normal durch
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
test("failed-Job, sendExportFailedEmail-Throw wird abgefangen (best-effort, kein Batch-Abbruch)", async () => {
|
|
829
|
+
const jobId = await seedPendingJob();
|
|
830
|
+
await asRawClient(stack.db).unsafe(
|
|
831
|
+
`
|
|
832
|
+
INSERT INTO read_export_download_tokens
|
|
833
|
+
(id, tenant_id, job_id, token_hash, issued_at, expires_at, version, inserted_at, modified_at)
|
|
834
|
+
VALUES (gen_random_uuid(), $1, $2, $3, now(), now() + interval '7 days', 1, now(), now())
|
|
835
|
+
`,
|
|
836
|
+
[tenantA, jobId, "existing-hash"],
|
|
837
|
+
);
|
|
838
|
+
|
|
839
|
+
// Should not throw despite the callback throwing — the try/catch
|
|
840
|
+
// around fireExportFailedCallback swallows it (operator-visibility
|
|
841
|
+
// via console.warn only).
|
|
842
|
+
const result = await runExportJobs({
|
|
843
|
+
db: stack.db,
|
|
844
|
+
registry: stack.registry,
|
|
845
|
+
buildStorageProvider: buildProvider,
|
|
846
|
+
now: NOW(),
|
|
847
|
+
sendExportFailedEmail: async () => {
|
|
848
|
+
throw new Error("synthetic email transport failure");
|
|
849
|
+
},
|
|
850
|
+
});
|
|
851
|
+
expect(result.failedJobIds).toContain(jobId);
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
test("failed-Job, user ohne email → sendExportFailedEmail skipped (kein Throw)", async () => {
|
|
855
|
+
await resetTestTables(stack.db, [userTable]);
|
|
856
|
+
await seedRow(stack.db, userTable, {
|
|
857
|
+
id: String(aliceUser.id),
|
|
858
|
+
tenantId: tenantA,
|
|
859
|
+
email: "" as string,
|
|
860
|
+
passwordHash: "hashed",
|
|
861
|
+
displayName: "Alice",
|
|
862
|
+
locale: "de",
|
|
863
|
+
emailVerified: true,
|
|
864
|
+
roles: '["Member"]',
|
|
865
|
+
status: USER_STATUS.Active,
|
|
866
|
+
});
|
|
867
|
+
const jobId = await seedPendingJob();
|
|
868
|
+
await asRawClient(stack.db).unsafe(
|
|
869
|
+
`
|
|
870
|
+
INSERT INTO read_export_download_tokens
|
|
871
|
+
(id, tenant_id, job_id, token_hash, issued_at, expires_at, version, inserted_at, modified_at)
|
|
872
|
+
VALUES (gen_random_uuid(), $1, $2, $3, now(), now() + interval '7 days', 1, now(), now())
|
|
873
|
+
`,
|
|
874
|
+
[tenantA, jobId, "existing-hash"],
|
|
875
|
+
);
|
|
876
|
+
|
|
877
|
+
let callbackInvoked = false;
|
|
878
|
+
const result = await runExportJobs({
|
|
879
|
+
db: stack.db,
|
|
880
|
+
registry: stack.registry,
|
|
881
|
+
buildStorageProvider: buildProvider,
|
|
882
|
+
now: NOW(),
|
|
883
|
+
sendExportFailedEmail: async () => {
|
|
884
|
+
callbackInvoked = true;
|
|
885
|
+
},
|
|
886
|
+
});
|
|
887
|
+
expect(result.failedJobIds).toContain(jobId);
|
|
888
|
+
expect(callbackInvoked).toBe(false);
|
|
889
|
+
});
|
|
890
|
+
|
|
709
891
|
test("failed-Job mit downloadStorageKey → ZIP wird im Cleanup-Pass entfernt (orphan-fix)", async () => {
|
|
710
892
|
// Atom 4a.fix orphan-cleanup: failed-Jobs haben downloadStorageKey
|
|
711
893
|
// gesetzt (path-pre-claim) — der ZIP-Pfad ist persistiert ab claim.
|
|
@@ -803,6 +985,7 @@ describe("runExportJobs :: Atom 3c file-binaries", () => {
|
|
|
803
985
|
createUserFeature(),
|
|
804
986
|
createDataRetentionFeature(),
|
|
805
987
|
createComplianceProfilesFeature(),
|
|
988
|
+
authFoundationFeature,
|
|
806
989
|
createSessionsFeature(),
|
|
807
990
|
|
|
808
991
|
createUserDataRightsFeature(),
|
|
@@ -1063,7 +1246,7 @@ describe("runExportJobs :: Atom 5 notification-callbacks", () => {
|
|
|
1063
1246
|
expect(sent.userEmail).toBe("alice@example.com");
|
|
1064
1247
|
expect(sent.jobId).toBe(jobId);
|
|
1065
1248
|
expect(sent.downloadUrl).toMatch(
|
|
1066
|
-
/^https:\/\/app\.example\.com\/user-export\/by-token
|
|
1249
|
+
/^https:\/\/app\.example\.com\/user-export\/by-token#token=[A-Za-z0-9_%-]+$/,
|
|
1067
1250
|
);
|
|
1068
1251
|
// plain-token aus dem callback-arg entspricht dem result.tokenByJobId
|
|
1069
1252
|
const plainFromResult = result.tokenByJobId.get(jobId);
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
// der end-to-end-Default-Pfad (delete).
|
|
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 { InMemoryKmsAdapter } from "@cosmicdrift/kumiko-framework/crypto";
|
|
21
22
|
import type { JobContext } from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -67,6 +68,7 @@ beforeAll(async () => {
|
|
|
67
68
|
createFilesFeature(),
|
|
68
69
|
createDataRetentionFeature(),
|
|
69
70
|
createComplianceProfilesFeature(),
|
|
71
|
+
authFoundationFeature,
|
|
70
72
|
createSessionsFeature(),
|
|
71
73
|
|
|
72
74
|
createUserDataRightsFeature(),
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
// ueber Pseudo-Tenant.
|
|
16
16
|
|
|
17
17
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
18
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
18
19
|
import { asRawClient } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
19
20
|
import {
|
|
20
21
|
configurePiiSubjectKms,
|
|
@@ -61,6 +62,7 @@ beforeAll(async () => {
|
|
|
61
62
|
createFilesFeature(),
|
|
62
63
|
createDataRetentionFeature(),
|
|
63
64
|
createComplianceProfilesFeature(),
|
|
65
|
+
authFoundationFeature,
|
|
64
66
|
createSessionsFeature(),
|
|
65
67
|
|
|
66
68
|
createUserDataRightsFeature(),
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// value (the failure mode that shipped the ctx.config export bug).
|
|
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 {
|
|
18
19
|
createEntity,
|
|
@@ -82,6 +83,7 @@ beforeAll(async () => {
|
|
|
82
83
|
stack = await setupTestStack({
|
|
83
84
|
features: [
|
|
84
85
|
createUserFeature(),
|
|
86
|
+
authFoundationFeature,
|
|
85
87
|
createSessionsFeature(),
|
|
86
88
|
createDataRetentionFeature(),
|
|
87
89
|
createComplianceProfilesFeature(),
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// Sprint-2-H1/H2-Hooks) kommt in S2.T1 (Cross-Data-Matrix).
|
|
8
8
|
|
|
9
9
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
10
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
10
11
|
import { setupTestStack, type TestStack } from "@cosmicdrift/kumiko-framework/stack";
|
|
11
12
|
import { createComplianceProfilesFeature } from "../../compliance-profiles";
|
|
12
13
|
import { createDataRetentionFeature } from "../../data-retention";
|
|
@@ -24,7 +25,14 @@ const userDataRights = createUserDataRightsFeature();
|
|
|
24
25
|
|
|
25
26
|
beforeAll(async () => {
|
|
26
27
|
stack = await setupTestStack({
|
|
27
|
-
features: [
|
|
28
|
+
features: [
|
|
29
|
+
authFoundationFeature,
|
|
30
|
+
userFeature,
|
|
31
|
+
sessionsFeature,
|
|
32
|
+
dataRetention,
|
|
33
|
+
complianceProfiles,
|
|
34
|
+
userDataRights,
|
|
35
|
+
],
|
|
28
36
|
});
|
|
29
37
|
});
|
|
30
38
|
|