@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.
Files changed (163) hide show
  1. package/package.json +7 -6
  2. package/src/auth-email-password/__tests__/account-unlock.integration.test.ts +283 -0
  3. package/src/auth-email-password/__tests__/auth-user-row.test.ts +36 -0
  4. package/src/auth-email-password/__tests__/auth.integration.test.ts +21 -0
  5. package/src/auth-email-password/__tests__/email-verification.integration.test.ts +1 -1
  6. package/src/auth-email-password/__tests__/password-policy.test.ts +15 -0
  7. package/src/auth-email-password/__tests__/password-reset.integration.test.ts +15 -1
  8. package/src/auth-email-password/__tests__/session-strict-mode.integration.test.ts +7 -14
  9. package/src/auth-email-password/__tests__/signup-flow.integration.test.ts +12 -0
  10. package/src/auth-email-password/constants.ts +15 -0
  11. package/src/auth-email-password/email-templates.ts +32 -0
  12. package/src/auth-email-password/errors.ts +9 -0
  13. package/src/auth-email-password/feature.ts +37 -5
  14. package/src/auth-email-password/handlers/change-password.write.ts +2 -1
  15. package/src/auth-email-password/handlers/confirm-account-unlock.write.ts +59 -0
  16. package/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -0
  17. package/src/auth-email-password/handlers/invite-accept.write.ts +6 -2
  18. package/src/auth-email-password/handlers/invite-signup-complete.write.ts +1 -0
  19. package/src/auth-email-password/handlers/request-account-unlock.write.ts +38 -0
  20. package/src/auth-email-password/handlers/reset-password.write.ts +2 -1
  21. package/src/auth-email-password/handlers/signup-confirm.write.ts +2 -1
  22. package/src/auth-email-password/i18n.ts +42 -0
  23. package/src/auth-email-password/index.ts +2 -0
  24. package/src/auth-email-password/lockout-store.ts +10 -5
  25. package/src/auth-email-password/password-policy.ts +2108 -0
  26. package/src/auth-email-password/signed-token.ts +1 -0
  27. package/src/auth-email-password/unlock-token.ts +28 -0
  28. package/src/auth-email-password/web/__tests__/auth-client.test.ts +525 -0
  29. package/src/auth-email-password/web/__tests__/auth-gate.test.tsx +65 -3
  30. package/src/auth-email-password/web/__tests__/confirm-account-unlock-screen.test.tsx +57 -0
  31. package/src/auth-email-password/web/__tests__/invite-accept-screen.test.tsx +126 -2
  32. package/src/auth-email-password/web/__tests__/login-screen.test.tsx +125 -0
  33. package/src/auth-email-password/web/__tests__/request-account-unlock-screen.test.tsx +131 -0
  34. package/src/auth-email-password/web/auth-client.ts +33 -0
  35. package/src/auth-email-password/web/confirm-account-unlock-screen.tsx +95 -0
  36. package/src/auth-email-password/web/index.ts +6 -0
  37. package/src/auth-email-password/web/login-screen.tsx +15 -0
  38. package/src/auth-email-password/web/request-account-unlock-screen.tsx +112 -0
  39. package/src/auth-foundation/__tests__/feature.test.ts +33 -0
  40. package/src/auth-foundation/__tests__/session-store.test.ts +78 -0
  41. package/src/auth-foundation/__tests__/token-verifier.test.ts +117 -0
  42. package/src/auth-foundation/boot-checks.ts +91 -0
  43. package/src/auth-foundation/feature.ts +103 -0
  44. package/src/auth-foundation/index.ts +23 -0
  45. package/src/auth-foundation/types.ts +107 -0
  46. package/src/auth-mfa/__tests__/errors-i18n-parity.test.ts +8 -0
  47. package/src/auth-mfa/__tests__/verify.integration.test.ts +44 -0
  48. package/src/auth-mfa/handlers/verify.write.ts +1 -1
  49. package/src/auth-mfa/web/__tests__/mfa-client.test.ts +118 -0
  50. package/src/auth-mfa/web/__tests__/mfa-enable-screen.test.tsx +173 -0
  51. package/src/auth-mfa/web/__tests__/mfa-verify-screen.test.tsx +100 -0
  52. package/src/billing-foundation/__tests__/subscription-provider-contract.ts +69 -0
  53. package/src/channel-email/pii-guard.ts +6 -3
  54. package/src/config/__tests__/app-overrides.test.ts +11 -0
  55. package/src/config/__tests__/backing-secrets.integration.test.ts +18 -0
  56. package/src/config/__tests__/cascade.integration.test.ts +39 -0
  57. package/src/config/__tests__/config.integration.test.ts +14 -0
  58. package/src/config/__tests__/pii-encrypted.integration.test.ts +23 -2
  59. package/src/config/__tests__/reencrypt-job-concurrent-write-race.integration.test.ts +20 -3
  60. package/src/config/__tests__/reencrypt-job-kek-rotation.integration.test.ts +2 -3
  61. package/src/config/feature.ts +2 -4
  62. package/src/config/handlers/set.write.ts +7 -1
  63. package/src/config/resolver.ts +1 -1
  64. package/src/custom-fields/__tests__/user-data-rights.integration.test.ts +2 -0
  65. package/src/delivery/handlers/log.query.ts +1 -1
  66. package/src/files-provider-s3/__tests__/s3-provider.integration.test.ts +7 -0
  67. package/src/folders/web/__tests__/folder-manager.test.tsx +27 -2
  68. package/src/inbound-mail-foundation/__tests__/account-state.test.ts +30 -0
  69. package/src/inbound-mail-foundation/__tests__/inbound-mail-foundation.integration.test.ts +57 -6
  70. package/src/inbound-mail-foundation/__tests__/inbound-mail-provider-contract.ts +93 -0
  71. package/src/inbound-mail-foundation/__tests__/watch-supervisor.integration.test.ts +154 -0
  72. package/src/inbound-mail-foundation/handlers/ingest-message.write.ts +60 -50
  73. package/src/inbound-provider-imap/__tests__/imap-live.integration.test.ts +12 -0
  74. package/src/inbound-provider-imap/__tests__/plugin-mocked.test.ts +331 -0
  75. package/src/inbound-provider-inmemory/__tests__/feature.test.ts +48 -0
  76. package/src/inbound-provider-inmemory/feature.ts +2 -2
  77. package/src/jobs/__tests__/reindex-entity-job.integration.test.ts +53 -1
  78. package/src/jobs/handlers/detail.query.ts +1 -1
  79. package/src/jobs/handlers/list.query.ts +1 -1
  80. package/src/mail-foundation/__tests__/mail-transport-contract.ts +42 -0
  81. package/src/mail-transport-inmemory/__tests__/feature.test.ts +19 -0
  82. package/src/mail-transport-smtp/__tests__/feature.test.ts +60 -0
  83. package/src/managed-pages/handlers/branding.query.ts +5 -1
  84. package/src/personal-access-tokens/__tests__/feature.test.ts +18 -0
  85. package/src/personal-access-tokens/__tests__/pat.integration.test.ts +8 -11
  86. package/src/personal-access-tokens/feature.ts +23 -8
  87. package/src/personal-access-tokens/handlers/list.query.ts +1 -1
  88. package/src/personal-access-tokens/index.ts +0 -14
  89. package/src/personal-access-tokens/resolver.ts +2 -2
  90. package/src/secrets/__tests__/rotate.integration.test.ts +31 -0
  91. package/src/sessions/__tests__/cleanup.integration.test.ts +2 -1
  92. package/src/sessions/__tests__/password-auto-revoke.integration.test.ts +5 -3
  93. package/src/sessions/__tests__/rebuild-survival.integration.test.ts +11 -2
  94. package/src/sessions/__tests__/session-store-registration.test.ts +21 -0
  95. package/src/sessions/__tests__/sessions-screens.boot.test.ts +10 -1
  96. package/src/sessions/__tests__/sessions.integration.test.ts +28 -17
  97. package/src/sessions/__tests__/test-helpers.ts +15 -2
  98. package/src/sessions/feature.ts +31 -3
  99. package/src/sessions/handlers/detail.query.ts +2 -2
  100. package/src/sessions/handlers/list.query.ts +4 -2
  101. package/src/sessions/handlers/mine.query.ts +4 -2
  102. package/src/sessions/testing.ts +14 -1
  103. package/src/shared/__tests__/chunked-entity-migration.test.ts +145 -0
  104. package/src/shared/decrypt-stored-pii.ts +12 -5
  105. package/src/subscription-mollie/__tests__/feature.test.ts +87 -0
  106. package/src/subscription-stripe/__tests__/feature.test.ts +120 -1
  107. package/src/tags/web/__tests__/tag-manager.test.tsx +277 -8
  108. package/src/tenant/__tests__/members-query-kms.integration.test.ts +98 -0
  109. package/src/tenant/handlers/invitations.query.ts +4 -2
  110. package/src/tenant/handlers/members.query.ts +2 -2
  111. package/src/tenant/seeding.ts +2 -2
  112. package/src/tenant-lifecycle/__tests__/tenant-lifecycle.integration.test.ts +2 -0
  113. package/src/tenant-lifecycle/boot-checks.ts +53 -0
  114. package/src/tenant-lifecycle/feature.ts +7 -0
  115. package/src/tenant-lifecycle/handlers/request-destruction.write.ts +1 -1
  116. package/src/tier-engine/feature.ts +2 -2
  117. package/src/user-data-rights/COMPLIANCE.md +20 -0
  118. package/src/user-data-rights/__tests__/anonymous-deletion-kms.integration.test.ts +2 -0
  119. package/src/user-data-rights/__tests__/anonymous-deletion.integration.test.ts +3 -0
  120. package/src/user-data-rights/__tests__/audit-log.integration.test.ts +2 -0
  121. package/src/user-data-rights/__tests__/boot-checks.test.ts +172 -0
  122. package/src/user-data-rights/__tests__/cross-data-matrix.integration.test.ts +2 -0
  123. package/src/user-data-rights/__tests__/download-by-token-resolver-trust.integration.test.ts +2 -0
  124. package/src/user-data-rights/__tests__/download.integration.test.ts +59 -4
  125. package/src/user-data-rights/__tests__/export-encrypted-fields.integration.test.ts +2 -0
  126. package/src/user-data-rights/__tests__/export-job-idempotency.integration.test.ts +2 -0
  127. package/src/user-data-rights/__tests__/export-job-list-cross-tenant.integration.test.ts +2 -0
  128. package/src/user-data-rights/__tests__/file-binary-forget-cleanup.integration.test.ts +2 -0
  129. package/src/user-data-rights/__tests__/file-binary-forget-failure.integration.test.ts +2 -0
  130. package/src/user-data-rights/__tests__/file-retention.integration.test.ts +2 -0
  131. package/src/user-data-rights/__tests__/file-storage-unification.integration.test.ts +2 -0
  132. package/src/user-data-rights/__tests__/forget-cleanup-hook-ordering.integration.test.ts +2 -0
  133. package/src/user-data-rights/__tests__/forget-hook-registry.integration.test.ts +2 -0
  134. package/src/user-data-rights/__tests__/inspector-screens.boot.test.ts +10 -1
  135. package/src/user-data-rights/__tests__/mail-default-bridge.integration.test.ts +2 -0
  136. package/src/user-data-rights/__tests__/read-users-rebuild-survives-lifecycle.integration.test.ts +15 -7
  137. package/src/user-data-rights/__tests__/request-cancel-deletion.integration.test.ts +2 -0
  138. package/src/user-data-rights/__tests__/request-deletion-callback.integration.test.ts +2 -0
  139. package/src/user-data-rights/__tests__/request-export.integration.test.ts +2 -0
  140. package/src/user-data-rights/__tests__/restriction-flow.integration.test.ts +48 -15
  141. package/src/user-data-rights/__tests__/run-export-jobs-cron-context.integration.test.ts +2 -0
  142. package/src/user-data-rights/__tests__/run-export-jobs.integration.test.ts +184 -1
  143. package/src/user-data-rights/__tests__/run-forget-cleanup.integration.test.ts +2 -0
  144. package/src/user-data-rights/__tests__/run-user-export.integration.test.ts +2 -0
  145. package/src/user-data-rights/__tests__/tenant-model-erasure.integration.test.ts +2 -0
  146. package/src/user-data-rights/__tests__/user-data-rights.integration.test.ts +9 -1
  147. package/src/user-data-rights/boot-checks.ts +86 -0
  148. package/src/user-data-rights/feature.ts +116 -6
  149. package/src/user-data-rights/handlers/deletion-grace-period.ts +1 -1
  150. package/src/user-data-rights/handlers/download-by-token.query.ts +1 -1
  151. package/src/user-data-rights/handlers/lift-restriction.write.ts +13 -19
  152. package/src/user-data-rights/handlers/restrict-account.write.ts +30 -8
  153. package/src/user-data-rights/run-export-jobs.ts +3 -3
  154. package/src/user-data-rights/run-forget-cleanup.ts +1 -1
  155. package/src/user-data-rights/web/__tests__/privacy-center-screen.test.tsx +1 -0
  156. package/src/user-data-rights-defaults/__tests__/bundled-entities.userdata-hooks.integration.test.ts +3 -0
  157. package/src/user-data-rights-defaults/__tests__/feature-mounted.test.ts +24 -0
  158. package/src/user-data-rights-defaults/__tests__/user-data-rights-defaults.integration.test.ts +2 -0
  159. package/src/user-data-rights-defaults/hooks/tenant-invitation.userdata-hook.ts +1 -1
  160. package/src/user-profile/__tests__/change-email.integration.test.ts +2 -0
  161. package/src/user-profile/__tests__/profile-screen.test.tsx +1 -0
  162. package/src/user-profile/handlers/change-email.write.ts +1 -1
  163. package/src/config/__tests__/encrypted-legacy-migration.integration.test.ts +0 -179
@@ -1,179 +0,0 @@
1
- // Pins the legacy → envelope migration story for encrypted config values:
2
- // 1. rows written under the old CONFIG_ENCRYPTION_KEY format stay readable
3
- // as long as the legacy key is wired as the cipher's fallback,
4
- // 2. reading an encrypted row WITHOUT any cipher throws (the pre-envelope
5
- // code silently returned the ciphertext as the value),
6
- // 3. the reencrypt job migrates legacy rows onto the envelope format
7
- // through the event-store executor (event payload carries the new
8
- // envelope, so a projection rebuild no longer needs the legacy key),
9
- // and is idempotent on re-run.
10
-
11
- import { afterAll, beforeAll, describe, expect, test } from "bun:test";
12
- import { randomBytes } from "node:crypto";
13
- import { asRawClient, fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
14
- import {
15
- createEncryptionProvider,
16
- createEventStoreExecutor,
17
- createTenantDb,
18
- } from "@cosmicdrift/kumiko-framework/db";
19
- import {
20
- type AppContext,
21
- access,
22
- createRegistry,
23
- createSystemUser,
24
- createTenantConfig,
25
- defineFeature,
26
- type TenantId,
27
- } from "@cosmicdrift/kumiko-framework/engine";
28
- import {
29
- createEnvelopeCipher,
30
- type EnvelopeCipher,
31
- isStoredEnvelope,
32
- } from "@cosmicdrift/kumiko-framework/secrets";
33
- import {
34
- setupTestStack,
35
- type TestStack,
36
- testTenantId,
37
- unsafePushTables,
38
- } from "@cosmicdrift/kumiko-framework/stack";
39
- import { createTestMasterKeyProvider } from "@cosmicdrift/kumiko-framework/testing";
40
- import { createConfigFeature } from "../feature";
41
- import { reencryptJob } from "../handlers/reencrypt.job";
42
- import { createConfigResolver } from "../resolver";
43
- import { configValueEntity, configValuesTable } from "../table";
44
-
45
- const KEY = "enc-mig:config:secret-pass";
46
- const TENANT: TenantId = testTenantId(1);
47
- const PLAINTEXT = "legacy-smtp-password";
48
-
49
- const keyDef = createTenantConfig("text", {
50
- encrypted: true,
51
- read: access.all,
52
- write: access.all,
53
- });
54
-
55
- // legacy single-key provider — the pre-envelope CONFIG_ENCRYPTION_KEY world
56
- const legacy = createEncryptionProvider(randomBytes(32).toString("base64"));
57
- const masterKeyProvider = createTestMasterKeyProvider();
58
- const cipherWithLegacy = createEnvelopeCipher(masterKeyProvider, { legacy });
59
- const cipherWithoutLegacy = createEnvelopeCipher(masterKeyProvider);
60
-
61
- const encMigFeature = defineFeature("enc-mig", (r) => {
62
- r.config({ keys: { "secret-pass": keyDef } });
63
- });
64
-
65
- const executor = createEventStoreExecutor(configValuesTable, configValueEntity, {
66
- entityName: "config-value",
67
- });
68
-
69
- let stack: TestStack;
70
-
71
- async function seedLegacyRow(): Promise<string> {
72
- const legacyValue = legacy.encrypt(JSON.stringify(PLAINTEXT));
73
- const systemUser = createSystemUser(TENANT);
74
- const tdb = createTenantDb(stack.db, TENANT, "system");
75
- const result = await executor.create(
76
- { key: KEY, value: legacyValue, tenantId: TENANT, userId: null },
77
- systemUser,
78
- tdb,
79
- );
80
- if (!result.isSuccess) throw new Error(`seed failed: ${result.error.code}`);
81
- return (result.data as { id: string }).id;
82
- }
83
-
84
- async function readStoredValue(): Promise<string> {
85
- const row = await fetchOne<{ value: string }>(stack.db, configValuesTable, {
86
- key: KEY,
87
- tenantId: TENANT,
88
- });
89
- if (!row) throw new Error("row missing");
90
- return row.value;
91
- }
92
-
93
- type ReencryptCtx = Pick<AppContext, "db" | "masterKeyProvider" | "configEncryption" | "registry">;
94
- function jobCtx(cipher: EnvelopeCipher): Parameters<typeof reencryptJob>[1] {
95
- const ctx: ReencryptCtx = {
96
- db: stack.db,
97
- masterKeyProvider,
98
- configEncryption: cipher,
99
- registry: stack.registry,
100
- };
101
- return ctx as unknown as Parameters<typeof reencryptJob>[1];
102
- }
103
-
104
- beforeAll(async () => {
105
- stack = await setupTestStack({ features: [createConfigFeature(), encMigFeature] });
106
- await unsafePushTables(stack.db, { configValuesTable });
107
- await seedLegacyRow();
108
- });
109
-
110
- afterAll(async () => {
111
- await stack.cleanup();
112
- });
113
-
114
- describe("legacy encrypted config rows", () => {
115
- test("stay readable through the cipher's legacy fallback (get + cascade path)", async () => {
116
- const resolver = createConfigResolver({ cipher: cipherWithLegacy });
117
- const value = await resolver.get(KEY, keyDef, TENANT, "u1", stack.db);
118
- expect(value).toBe(PLAINTEXT);
119
-
120
- const cascade = await resolver.getCascade(KEY, keyDef, TENANT, "u1", stack.db);
121
- const active = cascade.levels.find((l) => l.hasValue);
122
- expect(active?.value).toBe(PLAINTEXT);
123
- });
124
-
125
- test("throw without any cipher instead of leaking ciphertext as the value", async () => {
126
- const resolver = createConfigResolver({});
127
- await expect(resolver.get(KEY, keyDef, TENANT, "u1", stack.db)).rejects.toThrow(
128
- /encrypted but no cipher/,
129
- );
130
- await expect(resolver.getCascade(KEY, keyDef, TENANT, "u1", stack.db)).rejects.toThrow(
131
- /encrypted but no cipher/,
132
- );
133
- });
134
-
135
- test("throw with a remediation hint when the legacy key is missing", async () => {
136
- const resolver = createConfigResolver({ cipher: cipherWithoutLegacy });
137
- await expect(resolver.get(KEY, keyDef, TENANT, "u1", stack.db)).rejects.toThrow(/legacy/);
138
- });
139
- });
140
-
141
- describe("config:reencrypt job", () => {
142
- test("migrates the legacy row to envelope format via the executor", async () => {
143
- expect((await readStoredValue()).startsWith("{")).toBe(false);
144
-
145
- await reencryptJob({}, jobCtx(cipherWithLegacy));
146
-
147
- const stored = await readStoredValue();
148
- expect(stored.startsWith("{")).toBe(true);
149
- expect(isStoredEnvelope(JSON.parse(stored))).toBe(true);
150
-
151
- // decryptable WITHOUT the legacy key now — retiring CONFIG_ENCRYPTION_KEY
152
- // after a clean run is safe
153
- const resolver = createConfigResolver({ cipher: cipherWithoutLegacy });
154
- expect(await resolver.get(KEY, keyDef, TENANT, "u1", stack.db)).toBe(PLAINTEXT);
155
- });
156
-
157
- test("event payload carries the new envelope — rebuild-safe without legacy key", async () => {
158
- // The migration wrote a configValue.updated event whose payload holds
159
- // the envelope value; replaying events verbatim reproduces the
160
- // migrated state, so the final projection never needs the legacy key.
161
- const registryCheck = createRegistry([createConfigFeature(), encMigFeature]);
162
- expect(registryCheck.getConfigKey(KEY)?.encrypted).toBe(true);
163
-
164
- const events = await asRawClient(stack.db).unsafe<{
165
- payload: { changes?: { value?: string } };
166
- }>(
167
- `SELECT payload FROM kumiko_events WHERE aggregate_type = 'config-value' AND type LIKE '%.updated' ORDER BY created_at DESC LIMIT 1`,
168
- );
169
- const lastValue = events[0]?.payload?.changes?.value;
170
- expect(typeof lastValue).toBe("string");
171
- expect((lastValue as string).startsWith("{")).toBe(true);
172
- });
173
-
174
- test("is idempotent — a second run migrates nothing", async () => {
175
- const before = await readStoredValue();
176
- await reencryptJob({}, jobCtx(cipherWithLegacy));
177
- expect(await readStoredValue()).toBe(before);
178
- });
179
- });