@cosmicdrift/kumiko-bundled-features 0.165.1 → 0.165.3
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 -7
- package/src/__tests__/access-invalidation-consumer.integration.test.ts +1 -1
- package/src/__tests__/access-invalidation-stream.integration.test.ts +80 -78
- package/src/admin-shell/changes.json +8 -0
- package/src/agent-tools/changes.json +1 -0
- package/src/audit/__tests__/audit.integration.test.ts +5 -2
- package/src/audit/changes.json +8 -0
- package/src/auth-email-password/__tests__/login-gates.test.ts +39 -0
- package/src/auth-email-password/__tests__/session-callbacks.integration.test.ts +3 -0
- package/src/auth-email-password/changes.json +26 -0
- package/src/auth-email-password/handlers/login.write.ts +196 -163
- package/src/auth-email-password/web/__tests__/confirm-account-unlock-screen.test.tsx +3 -11
- package/src/auth-email-password/web/__tests__/fetch-mock.ts +14 -0
- package/src/auth-email-password/web/__tests__/forgot-password-screen.test.tsx +3 -11
- package/src/auth-email-password/web/__tests__/request-account-unlock-screen.test.tsx +3 -11
- package/src/auth-email-password/web/__tests__/reset-password-screen.test.tsx +3 -11
- package/src/auth-email-password/web/__tests__/verify-email-screen.test.tsx +3 -11
- package/src/auth-foundation/changes.json +8 -0
- package/src/auth-mfa/__tests__/reencrypt-job.integration.test.ts +84 -9
- package/src/auth-mfa/changes.json +38 -0
- package/src/auth-mfa/web/__tests__/mfa-verify-screen.test.tsx +3 -11
- package/src/auth-mfa-user-data/changes.json +1 -0
- package/src/billing-foundation/changes.json +1 -0
- package/src/cap-counter/changes.json +1 -0
- package/src/channel-email/changes.json +1 -0
- package/src/channel-in-app/changes.json +1 -0
- package/src/channel-push/changes.json +1 -0
- package/src/compliance-profiles/changes.json +1 -0
- package/src/config/changes.json +9 -0
- package/src/config/handlers/reencrypt.job.ts +3 -3
- package/src/crypto-shredding/changes.json +8 -0
- package/src/crypto-shredding/handlers/forget-subject.write.ts +13 -0
- package/src/custom-fields/changes.json +8 -0
- package/src/data-retention/changes.json +8 -0
- package/src/delivery/changes.json +8 -0
- package/src/document-ingest-foundation/changes.json +1 -0
- package/src/feature-toggles/changes.json +26 -0
- package/src/file-foundation/changes.json +1 -0
- package/src/file-provider-inmemory/changes.json +1 -0
- package/src/file-provider-s3/changes.json +1 -0
- package/src/file-provider-s3-env/changes.json +1 -0
- package/src/files/changes.json +1 -0
- package/src/files-provider-s3/changes.json +1 -0
- package/src/folders/changes.json +1 -0
- package/src/folders-user-data/changes.json +1 -0
- package/src/foundation-shared/changes.json +1 -0
- package/src/inbound-mail-foundation/__tests__/watch-supervisor.integration.test.ts +3 -1
- package/src/inbound-mail-foundation/changes.json +8 -0
- package/src/inbound-mail-foundation/entities.ts +3 -3
- package/src/inbound-mail-foundation/watch-supervisor.ts +3 -1
- package/src/inbound-provider-imap/changes.json +1 -0
- package/src/inbound-provider-inmemory/changes.json +1 -0
- package/src/jobs/__tests__/jobs-catalog.integration.test.ts +159 -0
- package/src/jobs/__tests__/jobs-screens.boot.test.ts +1 -0
- package/src/jobs/__tests__/jobs-security.integration.test.ts +4 -2
- package/src/jobs/changes.json +8 -0
- package/src/jobs/constants.ts +2 -0
- package/src/jobs/feature.ts +3 -1
- package/src/jobs/handlers/catalog.query.ts +43 -0
- package/src/jobs/handlers/trigger.write.ts +26 -2
- package/src/jobs/i18n.ts +33 -0
- package/src/jobs/web/__tests__/job-runs-screen.test.tsx +223 -0
- package/src/jobs/web/job-runs-screen.tsx +218 -84
- package/src/ledger/changes.json +1 -0
- package/src/legal-pages/changes.json +1 -0
- package/src/mail-foundation/changes.json +1 -0
- package/src/mail-transport-inmemory/changes.json +1 -0
- package/src/mail-transport-smtp/changes.json +1 -0
- package/src/managed-pages/changes.json +20 -0
- package/src/page-render/changes.json +14 -0
- package/src/personal-access-tokens/changes.json +8 -0
- package/src/personal-access-tokens/feature.ts +2 -2
- package/src/personal-access-tokens/schema/api-token.ts +3 -3
- package/src/presets/changes.json +1 -0
- package/src/presets/dsgvo-self-service.ts +1 -1
- package/src/rate-limiting/changes.json +1 -0
- package/src/readiness/changes.json +1 -0
- package/src/renderer-foundation/changes.json +1 -0
- package/src/renderer-simple/changes.json +1 -0
- package/src/secrets/changes.json +1 -0
- package/src/seo/changes.json +20 -0
- package/src/sessions/__tests__/sessions.integration.test.ts +19 -3
- package/src/sessions/changes.json +8 -0
- package/src/sessions/feature.ts +9 -6
- package/src/sessions/handlers/revoke-all-for-user.write.ts +9 -1
- package/src/sessions/schema/user-session.ts +6 -7
- package/src/shared/changes.json +1 -0
- package/src/step-dispatcher/changes.json +1 -0
- package/src/subscription-mollie/changes.json +1 -0
- package/src/subscription-stripe/changes.json +1 -0
- package/src/tags/changes.json +1 -0
- package/src/template-resolver/changes.json +1 -0
- package/src/tenant/changes.json +27 -0
- package/src/tenant-lifecycle/changes.json +1 -0
- package/src/tenant-settings/__tests__/tenant-settings.integration.test.ts +46 -67
- package/src/tenant-settings/changes.json +1 -0
- package/src/text-content/changes.json +14 -0
- package/src/tier-engine/changes.json +1 -0
- package/src/user/changes.json +1 -0
- package/src/user/schema/user.ts +8 -5
- package/src/user-data-rights/__tests__/audit-log.integration.test.ts +7 -3
- package/src/user-data-rights/__tests__/cross-data-matrix.integration.test.ts +0 -1
- package/src/user-data-rights/__tests__/download.integration.test.ts +13 -1
- package/src/user-data-rights/__tests__/export-job-idempotency.integration.test.ts +0 -1
- package/src/user-data-rights/__tests__/request-cancel-deletion.integration.test.ts +0 -1
- package/src/user-data-rights/__tests__/request-deletion-callback.integration.test.ts +0 -1
- package/src/user-data-rights/__tests__/request-export.integration.test.ts +0 -1
- package/src/user-data-rights/__tests__/restriction-flow.integration.test.ts +22 -2
- package/src/user-data-rights/__tests__/run-export-jobs.integration.test.ts +0 -2
- package/src/user-data-rights/__tests__/run-forget-cleanup.integration.test.ts +0 -1
- package/src/user-data-rights/__tests__/run-user-export.integration.test.ts +0 -1
- package/src/user-data-rights/changes.json +21 -0
- package/src/user-data-rights/feature.ts +7 -6
- package/src/user-data-rights/handlers/lift-restriction.write.ts +6 -22
- package/src/user-data-rights/handlers/restrict-account.write.ts +6 -22
- package/src/user-data-rights/handlers/run-forget-cleanup.write.ts +1 -0
- package/src/user-data-rights/lib/deny-if-target-outside-admin-tenant.ts +32 -0
- package/src/user-data-rights/run-forget-cleanup.ts +23 -0
- package/src/user-data-rights-defaults/changes.json +8 -0
- package/src/user-profile/changes.json +8 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-bundled-features",
|
|
3
|
-
"version": "0.165.
|
|
3
|
+
"version": "0.165.3",
|
|
4
4
|
"description": "Built-in features — tenant, user, auth, delivery. The stuff you'd rewrite anyway, already typed.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -119,11 +119,11 @@
|
|
|
119
119
|
"./step-dispatcher": "./src/step-dispatcher/index.ts"
|
|
120
120
|
},
|
|
121
121
|
"dependencies": {
|
|
122
|
-
"@cosmicdrift/kumiko-dispatcher-live": "0.165.
|
|
123
|
-
"@cosmicdrift/kumiko-framework": "0.165.
|
|
124
|
-
"@cosmicdrift/kumiko-headless": "0.165.
|
|
125
|
-
"@cosmicdrift/kumiko-renderer": "0.165.
|
|
126
|
-
"@cosmicdrift/kumiko-renderer-web": "0.165.
|
|
122
|
+
"@cosmicdrift/kumiko-dispatcher-live": "0.165.3",
|
|
123
|
+
"@cosmicdrift/kumiko-framework": "0.165.3",
|
|
124
|
+
"@cosmicdrift/kumiko-headless": "0.165.3",
|
|
125
|
+
"@cosmicdrift/kumiko-renderer": "0.165.3",
|
|
126
|
+
"@cosmicdrift/kumiko-renderer-web": "0.165.3",
|
|
127
127
|
"@mollie/api-client": "^4.5.0",
|
|
128
128
|
"imapflow": "^1.3.3",
|
|
129
129
|
"mailparser": "^3.9.8",
|
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
"LICENSE"
|
|
149
149
|
],
|
|
150
150
|
"peerDependencies": {
|
|
151
|
-
"@cosmicdrift/kumiko-types": "^0.165.
|
|
151
|
+
"@cosmicdrift/kumiko-types": "^0.165.3"
|
|
152
152
|
},
|
|
153
153
|
"devDependencies": {
|
|
154
154
|
"@testing-library/user-event": "^14.6.1",
|
|
@@ -72,7 +72,7 @@ let invalidated: string[];
|
|
|
72
72
|
let unsubscribe: (() => void) | undefined;
|
|
73
73
|
|
|
74
74
|
function trackInvalidation(userId: string): void {
|
|
75
|
-
unsubscribe = stack.sseBroker.subscribeAccessInvalidation(userId, () => {
|
|
75
|
+
unsubscribe = stack.sseBroker.subscribeAccessInvalidation?.(userId, () => {
|
|
76
76
|
invalidated.push(userId);
|
|
77
77
|
});
|
|
78
78
|
}
|
|
@@ -53,25 +53,28 @@ const callbacks = createLateBoundHolder<SessionCallbacks>("session-callbacks");
|
|
|
53
53
|
const encryptionKey = randomBytes(32).toString("base64");
|
|
54
54
|
const TENANT = testTenantId(1);
|
|
55
55
|
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
releaseNextChunk = resolve;
|
|
56
|
+
// Per-stream gate (gateId in payload) so parallel/second streams don't share
|
|
57
|
+
// one module-level Promise — Map keyed by the id the test picks.
|
|
58
|
+
const chunkGates = new Map<string, { promise: Promise<void>; release: () => void }>();
|
|
59
|
+
|
|
60
|
+
function createGate(gateId: string): () => void {
|
|
61
|
+
let release!: () => void;
|
|
62
|
+
const promise = new Promise<void>((resolve) => {
|
|
63
|
+
release = resolve;
|
|
65
64
|
});
|
|
65
|
+
chunkGates.set(gateId, { promise, release });
|
|
66
|
+
return release;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
const streamProbeFeature = defineFeature("stream-probe", (r) => {
|
|
69
70
|
r.streamHandler(
|
|
70
71
|
"probe:tail",
|
|
71
|
-
z.object({
|
|
72
|
-
async function* () {
|
|
72
|
+
z.object({ gateId: z.string().min(1) }),
|
|
73
|
+
async function* (event) {
|
|
73
74
|
yield { phase: "open" as const };
|
|
74
|
-
|
|
75
|
+
const gate = chunkGates.get(event.payload.gateId);
|
|
76
|
+
if (!gate) throw new Error(`missing gate ${event.payload.gateId}`);
|
|
77
|
+
await gate.promise;
|
|
75
78
|
// Reached only if mid-stream access check failed to cut the generator.
|
|
76
79
|
yield { phase: "should-not-reach-client" as const };
|
|
77
80
|
},
|
|
@@ -144,74 +147,73 @@ beforeEach(async () => {
|
|
|
144
147
|
userSessionTable,
|
|
145
148
|
eventsTable,
|
|
146
149
|
]);
|
|
147
|
-
|
|
150
|
+
chunkGates.clear();
|
|
148
151
|
});
|
|
149
152
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
153
|
+
async function openProbeStream(token: string): Promise<{
|
|
154
|
+
iter: AsyncIterator<{ phase: string }>;
|
|
155
|
+
release: () => void;
|
|
156
|
+
}> {
|
|
157
|
+
const gateId = generateToken();
|
|
158
|
+
const release = createGate(gateId);
|
|
159
|
+
const { fetch, csrfToken } = buildLiveFetch(token);
|
|
160
|
+
const dispatcher = createLiveDispatcher({ fetch, readCsrf: () => csrfToken });
|
|
161
|
+
const iter = dispatcher
|
|
162
|
+
.stream<{ phase: string }>("stream-probe:stream:probe:tail", { gateId })
|
|
163
|
+
[Symbol.asyncIterator]();
|
|
164
|
+
const first = await iter.next();
|
|
165
|
+
expect(first.done).toBe(false);
|
|
166
|
+
expect(first.value).toEqual({ phase: "open" });
|
|
167
|
+
return { iter, release };
|
|
168
|
+
}
|
|
165
169
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
170
|
+
describe("access-invalidation mid-stream SSE teardown (#1561)", () => {
|
|
171
|
+
const cases = [
|
|
172
|
+
{
|
|
173
|
+
name: "session revoke mid-stream terminates the open HTTP SSE stream",
|
|
174
|
+
setup: async () => {
|
|
175
|
+
await h.seedUser("stream-revoke@example.com", "pw-long-enough");
|
|
176
|
+
const { token, sid } = await h.login("stream-revoke@example.com", "pw-long-enough");
|
|
177
|
+
return {
|
|
178
|
+
token,
|
|
179
|
+
revoke: async () => {
|
|
180
|
+
const revokeRes = await h.authedPost("/api/write", token, {
|
|
181
|
+
type: SessionHandlers.revoke,
|
|
182
|
+
payload: { id: sid },
|
|
183
|
+
});
|
|
184
|
+
expect(revokeRes.status).toBe(200);
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "tenant role strip mid-stream terminates the open HTTP SSE stream",
|
|
191
|
+
setup: async () => {
|
|
192
|
+
const { userId } = await h.seedUser("stream-roles@example.com", "pw-long-enough");
|
|
193
|
+
const { token } = await h.login("stream-roles@example.com", "pw-long-enough");
|
|
194
|
+
return {
|
|
195
|
+
token,
|
|
196
|
+
revoke: async () => {
|
|
197
|
+
const admin = await withMintedSession(sessionCreator, TestUsers.systemAdmin);
|
|
198
|
+
await stack.http.writeOk(
|
|
199
|
+
TenantHandlers.updateMemberRoles,
|
|
200
|
+
{ userId, tenantId: TENANT, roles: ["Admin"] },
|
|
201
|
+
admin,
|
|
202
|
+
);
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
] as const;
|
|
208
|
+
|
|
209
|
+
for (const c of cases) {
|
|
210
|
+
test(c.name, async () => {
|
|
211
|
+
const { token, revoke } = await c.setup();
|
|
212
|
+
const { iter, release } = await openProbeStream(token);
|
|
213
|
+
await revoke();
|
|
214
|
+
await stack.eventDispatcher?.runOnce();
|
|
215
|
+
release();
|
|
216
|
+
await expect(iter.next()).rejects.toMatchObject({ code: "access_denied" });
|
|
169
217
|
});
|
|
170
|
-
|
|
171
|
-
await stack.eventDispatcher?.runOnce();
|
|
172
|
-
|
|
173
|
-
releaseNextChunk?.();
|
|
174
|
-
|
|
175
|
-
let thrown: unknown;
|
|
176
|
-
try {
|
|
177
|
-
await iter.next();
|
|
178
|
-
} catch (e) {
|
|
179
|
-
thrown = e;
|
|
180
|
-
}
|
|
181
|
-
expect(thrown).toMatchObject({ code: "access_denied" });
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
test("tenant role strip mid-stream terminates the open HTTP SSE stream", async () => {
|
|
185
|
-
const { userId } = await h.seedUser("stream-roles@example.com", "pw-long-enough");
|
|
186
|
-
const { token } = await h.login("stream-roles@example.com", "pw-long-enough");
|
|
187
|
-
|
|
188
|
-
const { fetch, csrfToken } = buildLiveFetch(token);
|
|
189
|
-
const dispatcher = createLiveDispatcher({ fetch, readCsrf: () => csrfToken });
|
|
190
|
-
|
|
191
|
-
const iter = dispatcher
|
|
192
|
-
.stream<{ phase: string }>("stream-probe:stream:probe:tail", {})
|
|
193
|
-
[Symbol.asyncIterator]();
|
|
194
|
-
|
|
195
|
-
const first = await iter.next();
|
|
196
|
-
expect(first.done).toBe(false);
|
|
197
|
-
expect(first.value).toEqual({ phase: "open" });
|
|
198
|
-
|
|
199
|
-
const admin = await withMintedSession(sessionCreator, TestUsers.systemAdmin);
|
|
200
|
-
await stack.http.writeOk(
|
|
201
|
-
TenantHandlers.updateMemberRoles,
|
|
202
|
-
{ userId, tenantId: TENANT, roles: ["Admin"] },
|
|
203
|
-
admin,
|
|
204
|
-
);
|
|
205
|
-
await stack.eventDispatcher?.runOnce();
|
|
206
|
-
|
|
207
|
-
releaseNextChunk?.();
|
|
208
|
-
|
|
209
|
-
let thrown: unknown;
|
|
210
|
-
try {
|
|
211
|
-
await iter.next();
|
|
212
|
-
} catch (e) {
|
|
213
|
-
thrown = e;
|
|
214
|
-
}
|
|
215
|
-
expect(thrown).toMatchObject({ code: "access_denied" });
|
|
216
|
-
});
|
|
218
|
+
}
|
|
217
219
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.125.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "admin-shell: neues bundled-feature für rollen-gated Tenant- und Platform-Workspaces mit O…",
|
|
6
|
+
"detail": "admin-shell: neues bundled-feature für rollen-gated Tenant- und Platform-Workspaces mit Overview-Home-Screens, Nav-Icons und Server-i18n. Komponiert Screens aus `tenant`, `audit`, `jobs` und optional `tier-engine` — mount nach diesen Features. Overview-Queries laufen über eine fest kodierte Allowlist pro Workspace, um versehentliche Cross-Workspace-Datenzugriffe zu verhindern."
|
|
7
|
+
}
|
|
8
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[]
|
|
@@ -212,7 +212,7 @@ describe("audit: list query", () => {
|
|
|
212
212
|
];
|
|
213
213
|
for (const [name, createdAt] of stamps) {
|
|
214
214
|
const id = byName.get(name);
|
|
215
|
-
|
|
215
|
+
if (!id) throw new Error(`missing event for ${name}`);
|
|
216
216
|
await asRawClient(stack.db).unsafe(
|
|
217
217
|
`UPDATE kumiko_events SET created_at = $1::timestamptz WHERE id = $2::bigint`,
|
|
218
218
|
[createdAt, id],
|
|
@@ -244,7 +244,10 @@ describe("audit: list query", () => {
|
|
|
244
244
|
admin,
|
|
245
245
|
);
|
|
246
246
|
expect(untilBefore.rows).toHaveLength(1);
|
|
247
|
-
|
|
247
|
+
const untilRow = untilBefore.rows[0];
|
|
248
|
+
expect(untilRow).toBeDefined();
|
|
249
|
+
if (!untilRow) throw new Error("expected untilBefore row");
|
|
250
|
+
expect((untilRow.payload as { name?: string }).name).toBe("before-window");
|
|
248
251
|
});
|
|
249
252
|
|
|
250
253
|
test("rejects inverted from/to range with validation_error", async () => {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.127.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "Harden admin operator UI: stricter boot i18n/entityList validation, job run logger wiring…",
|
|
6
|
+
"detail": "Harden admin operator UI: stricter boot i18n/entityList validation, job run logger wiring, audit/job filters, shell breadcrumbs, and bundled entityList/i18n standards."
|
|
7
|
+
}
|
|
8
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { USER_STATUS } from "../../user";
|
|
3
|
+
import type { AuthUserRow } from "../auth-user-row";
|
|
4
|
+
import { gateEnforceAccountStatus, gateEnforceEmailVerified } from "../handlers/login.write";
|
|
5
|
+
|
|
6
|
+
function row(over: Partial<AuthUserRow> = {}): AuthUserRow {
|
|
7
|
+
return { id: "00000000-0000-4000-8000-000000000001", passwordHash: "x", ...over };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe("login.write gates (fw#1284)", () => {
|
|
11
|
+
test("gateEnforceEmailVerified rejects when strict and unverified", () => {
|
|
12
|
+
const g = gateEnforceEmailVerified(row({ emailVerified: false }), true);
|
|
13
|
+
expect(g.ok).toBe(false);
|
|
14
|
+
if (!g.ok) expect(g.result.isSuccess).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("gateEnforceEmailVerified passes when not strict", () => {
|
|
18
|
+
expect(gateEnforceEmailVerified(row({ emailVerified: false }), false).ok).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("gateEnforceAccountStatus rejects restricted", () => {
|
|
22
|
+
const g = gateEnforceAccountStatus(row({ status: USER_STATUS.Restricted }));
|
|
23
|
+
expect(g.ok).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("gateEnforceAccountStatus rejects deletion_requested as invalid_creds shape", () => {
|
|
27
|
+
const g = gateEnforceAccountStatus(row({ status: USER_STATUS.DeletionRequested }));
|
|
28
|
+
expect(g.ok).toBe(false);
|
|
29
|
+
if (!g.ok) {
|
|
30
|
+
expect(g.result.isSuccess).toBe(false);
|
|
31
|
+
// anti-enumeration — same family as invalid credentials
|
|
32
|
+
expect(g.result).toMatchObject({ isSuccess: false });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("gateEnforceAccountStatus passes active", () => {
|
|
37
|
+
expect(gateEnforceAccountStatus(row({ status: USER_STATUS.Active })).ok).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -133,6 +133,9 @@ beforeAll(async () => {
|
|
|
133
133
|
password: "warmup-password-not-asserted-on",
|
|
134
134
|
tenants: [],
|
|
135
135
|
});
|
|
136
|
+
// Pins the authHeader sid-cache key: one create for the seed actor, then
|
|
137
|
+
// we reset. If the cache key drifts, this fails instead of leaking into tests.
|
|
138
|
+
expect(store.created).toHaveLength(1);
|
|
136
139
|
store.live.clear();
|
|
137
140
|
store.created.length = 0;
|
|
138
141
|
store.revoked.length = 0;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.162.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "auth-email-password: runtime toggle for self-registration.",
|
|
6
|
+
"detail": "auth-email-password: runtime toggle for self-registration. A new handler-less companion feature (`auth-self-registration`, toggleable, default on) lets an operator flip self-signup off at runtime via feature-toggles without redeploying. `signup-request` silently no-ops when off (matching its always-200 anti-enumeration contract), and a new anonymous-readable query (`auth-email-password:query:signup-registration-status`) lets the public signup page hide its own link/form."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.154.1",
|
|
10
|
+
"type": "fix",
|
|
11
|
+
"title": "Export `LoginScreen`/`LoginScreenProps`/`AuthLegalLink` from `@cosmicdrift/kumiko-bundled…",
|
|
12
|
+
"detail": "Export `LoginScreen`/`LoginScreenProps`/`AuthLegalLink` from `@cosmicdrift/kumiko-bundled-features/auth-email-password/web`. Every other auth screen (ForgotPasswordScreen, SignupScreen, ResetPasswordScreen, …) was already exported from the barrel with its props type; `LoginScreen` was missed. `makeAuthGate`'s second parameter is typed `LoginScreenProps`, so consumers passing a typed `loginScreenProps` override (e.g. `@cosmicdriftgamestudio/kumiko-designer`) couldn't import the type at all. - @cosmicdrift/kumiko-framework@0.154.1 - @cosmicdrift/kumiko-dispatcher-live@0.154.1 - @cosmicdrift/kumiko-headless@0.154.1 - @cosmicdrift/kumiko-renderer@0.154.1 - @cosmicdrift/kumiko-renderer-web@0.154.1"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"version": "0.151.0",
|
|
16
|
+
"type": "improvement",
|
|
17
|
+
"title": "`auth-email-password/web` no longer exports `LoginScreen`/`LoginScreenProps` directly — u…",
|
|
18
|
+
"detail": "`auth-email-password/web` no longer exports `LoginScreen`/`LoginScreenProps` directly — use the new `createLoginRoute({ loginScreenProps, mfaVerifyScreen, unaffected (they already build on the same logic internally now). Why: a raw `<LoginScreen />` render has no MFA-challenge handling unless the caller remembers to hand-wire `onMfaChallenge` + swap in a verify screen itself — exactly how kumiko-framework#266's login-time MFA step went missing in a real app's standalone apex/marketing login route (it renders outside `emailPasswordClient`'s own gate, which already handled this correctly). `createLoginRoute` is the one place this logic lives now, for both the gated and standalone cases — there's no lower-level piece left to misuse. The `apex-surface-auth` recipe is updated to match."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"version": "0.131.0",
|
|
22
|
+
"type": "improvement",
|
|
23
|
+
"title": "App-Mounting 2.0 Säule A: Mid-Level-Widget-Kit in renderer-web (StatCard, MiniStat, Secti…",
|
|
24
|
+
"detail": "App-Mounting 2.0 Säule A: Mid-Level-Widget-Kit in renderer-web (StatCard, MiniStat, SectionCard, StatusBadge, ProgressBar, CollapsibleSection, DetailList, ModeSwitch, StatusBarChart, TimeseriesChart, EmptyState/LoadingState/ErrorState, QueryTable) + Status-Farb-Tokens (--color-status-\\*). Neue Hooks useMutation + useDisclosure. Neues Core-Primitive Link (default/button/muted), Button-Variant \"link\", Text-Variant \"muted\"; auth-email-password nutzt sie (authButtonClass/authMutedLinkClass entfernt)."
|
|
25
|
+
}
|
|
26
|
+
]
|