@cosmicdrift/kumiko-bundled-features 0.149.1 → 0.150.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 (74) hide show
  1. package/package.json +7 -6
  2. package/src/admin-shell/__tests__/admin-shell-security.integration.test.ts +1 -1
  3. package/src/audit/__tests__/audit-security.integration.test.ts +1 -1
  4. package/src/auth-email-password/__tests__/account-lockout-no-redis.integration.test.ts +1 -1
  5. package/src/auth-email-password/__tests__/account-lockout.integration.test.ts +1 -1
  6. package/src/auth-email-password/__tests__/auth-claims.integration.test.ts +1 -1
  7. package/src/auth-email-password/__tests__/auth.integration.test.ts +1 -1
  8. package/src/auth-email-password/__tests__/email-verification.integration.test.ts +1 -1
  9. package/src/auth-email-password/__tests__/invite-flow-kms.integration.test.ts +1 -1
  10. package/src/auth-email-password/__tests__/invite-flow.integration.test.ts +1 -1
  11. package/src/auth-email-password/__tests__/multi-roles.integration.test.ts +1 -1
  12. package/src/auth-email-password/__tests__/password-reset.integration.test.ts +1 -1
  13. package/src/auth-email-password/__tests__/seed-admin.integration.test.ts +1 -1
  14. package/src/auth-email-password/__tests__/session-callbacks.integration.test.ts +1 -1
  15. package/src/auth-email-password/handlers/change-password.write.ts +1 -1
  16. package/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -2
  17. package/src/auth-email-password/handlers/login.write.ts +1 -1
  18. package/src/auth-email-password/handlers/reset-password.write.ts +1 -1
  19. package/src/auth-email-password/i18n.ts +4 -0
  20. package/src/auth-email-password/index.ts +6 -1
  21. package/src/auth-email-password/seeding.ts +1 -1
  22. package/src/auth-email-password/web/auth-gate.tsx +2 -0
  23. package/src/auth-email-password/web/login-screen.tsx +2 -0
  24. package/src/auth-mfa/__tests__/base32.test.ts +28 -0
  25. package/src/auth-mfa/__tests__/disable-and-regenerate.integration.test.ts +16 -0
  26. package/src/auth-mfa/__tests__/enable-flow.integration.test.ts +60 -0
  27. package/src/auth-mfa/__tests__/pii-subject-encryption.integration.test.ts +2 -0
  28. package/src/auth-mfa/__tests__/reencrypt-job.integration.test.ts +2 -0
  29. package/src/auth-mfa/__tests__/session-auto-revoke.integration.test.ts +2 -1
  30. package/src/auth-mfa/__tests__/totp.test.ts +11 -2
  31. package/src/auth-mfa/__tests__/verify.integration.test.ts +67 -1
  32. package/src/auth-mfa/base32.ts +7 -1
  33. package/src/auth-mfa/constants.ts +14 -0
  34. package/src/auth-mfa/errors.ts +9 -16
  35. package/src/auth-mfa/feature.ts +4 -0
  36. package/src/auth-mfa/handlers/enable-confirm.write.ts +20 -1
  37. package/src/auth-mfa/handlers/verify.write.ts +26 -3
  38. package/src/auth-mfa/index.ts +0 -1
  39. package/src/auth-mfa/recovery-codes.ts +12 -2
  40. package/src/auth-mfa/schema/user-mfa.ts +3 -0
  41. package/src/auth-mfa/testing.ts +5 -0
  42. package/src/auth-mfa/web/__tests__/mfa-error-keys.test.ts +20 -0
  43. package/src/auth-mfa/web/i18n.ts +2 -0
  44. package/src/auth-mfa/web/mfa-enable-screen.tsx +37 -28
  45. package/src/auth-mfa/web/mfa-error-keys.ts +7 -5
  46. package/src/auth-mfa/web/mfa-verify-screen.tsx +11 -0
  47. package/src/auth-mfa/web/qrcode-browser.d.ts +7 -0
  48. package/src/auth-mfa-user-data/__tests__/hooks.integration.test.ts +2 -0
  49. package/src/config/handlers/cascade.query.ts +2 -1
  50. package/src/config/handlers/values.query.ts +2 -1
  51. package/src/inbound-mail-foundation/handlers/ingest-message.write.ts +15 -2
  52. package/src/legal-pages/feature.ts +2 -1
  53. package/src/managed-pages/feature.ts +1 -6
  54. package/src/seo/feature.ts +1 -6
  55. package/src/sessions/__tests__/sessions-screens.boot.test.ts +51 -0
  56. package/src/sessions/__tests__/sessions.integration.test.ts +44 -0
  57. package/src/sessions/__tests__/test-helpers.ts +1 -1
  58. package/src/sessions/constants.ts +5 -0
  59. package/src/sessions/feature.ts +67 -1
  60. package/src/sessions/handlers/detail.query.ts +36 -0
  61. package/src/sessions/i18n.ts +23 -0
  62. package/src/shared/index.ts +1 -0
  63. package/src/shared/system-query.ts +5 -0
  64. package/src/tenant/__tests__/tenant-security.integration.test.ts +1 -1
  65. package/src/user-data-rights/__tests__/download-by-token-resolver-trust.integration.test.ts +163 -0
  66. package/src/user-data-rights/__tests__/download.integration.test.ts +39 -0
  67. package/src/user-data-rights/__tests__/read-users-rebuild-survives-lifecycle.integration.test.ts +1 -1
  68. package/src/user-data-rights/__tests__/restriction-flow.integration.test.ts +1 -1
  69. package/src/user-data-rights/handlers/download-by-job.query.ts +5 -2
  70. package/src/user-data-rights/handlers/download-by-token.query.ts +5 -4
  71. package/src/user-data-rights/lib/tenant-file-provider.ts +62 -0
  72. package/src/user-profile/__tests__/change-email.integration.test.ts +1 -1
  73. package/src/user-profile/handlers/change-email.write.ts +2 -2
  74. package/src/auth-email-password/password-hashing.ts +0 -4
@@ -31,8 +31,8 @@ import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
31
31
  import { NotFoundError, UnprocessableError } from "@cosmicdrift/kumiko-framework/errors";
32
32
  import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
33
33
  import { z } from "zod";
34
- import { createFileProviderForTenant } from "../../file-foundation";
35
34
  import { recordDownloadUse, recordInvalidAttempt } from "../audit-download";
35
+ import { resolveTenantFileProvider } from "../lib/tenant-file-provider";
36
36
  import { exportDownloadTokensTable } from "../schema/download-token";
37
37
  import { EXPORT_JOB_STATUS, exportJobsTable } from "../schema/export-job";
38
38
  import { hashDownloadToken } from "../token-helpers";
@@ -181,9 +181,10 @@ export const downloadByTokenQuery = defineQueryHandler({
181
181
  });
182
182
  }
183
183
 
184
- // Step 5: signed-URL via provider. createFileProviderForTenant nutzt
185
- // requestedFromTenantId (gleicher Tenant wie beim Worker-Storage-Write).
186
- const provider = await createFileProviderForTenant(
184
+ // Step 5: signed-URL via provider, explicitly bound to the job's
185
+ // tenant NOT the ambient request tenant (anonymous magic-link path
186
+ // has none under resolverTrust: "authoritative").
187
+ const provider = await resolveTenantFileProvider(
187
188
  ctx,
188
189
  jobRow.requestedFromTenantId,
189
190
  "user-data-rights:query:download-by-token",
@@ -0,0 +1,62 @@
1
+ // Resolves the GDPR-download file-storage provider explicitly for
2
+ // `jobRow.requestedFromTenantId` instead of the ambient request tenant.
3
+ //
4
+ // Both download handlers (by-token, by-job) used to call
5
+ // `createFileProviderForTenant(ctx, jobRow.requestedFromTenantId, ...)`,
6
+ // which reads the PROVIDER SELECTION from `ctx.config` — bound to the
7
+ // caller's own ambient tenant, not the job's tenant:
8
+ // - by-token (anonymous magic-link, httpRoute): under
9
+ // `resolverTrust: "authoritative"` without `defaultTenantId` there is
10
+ // no ambient tenant at all — `ctx.config` throws, 500s the whole
11
+ // GDPR download flow.
12
+ // - by-job (session-authed): a user can own jobs across tenants
13
+ // (cross-tenant-same-user); the ambient session tenant silently
14
+ // picks the WRONG tenant's provider config for a job requested from
15
+ // a different tenant.
16
+ //
17
+ // Fix: build a fresh config accessor bound explicitly to the given
18
+ // tenantId (same construction as `makeTenantStorageProviderResolver`,
19
+ // which the export/forget crons already use), instead of the ambient
20
+ // `ctx.config`. Deliberately NOT `ctx._fileProviderResolver` — that
21
+ // resolver is boot-built and per-tenant-cached for the process lifetime,
22
+ // so an operator switching `file-foundation:config:provider` mid-session
23
+ // wouldn't take effect until the cache evicts.
24
+ import type { DbConnection, TenantDb } from "@cosmicdrift/kumiko-framework/db";
25
+ import type { ConfigResolver, Registry, TenantId } from "@cosmicdrift/kumiko-framework/engine";
26
+ import { SYSTEM_USER_ID } from "@cosmicdrift/kumiko-framework/engine";
27
+ import type { FileStorageProvider } from "@cosmicdrift/kumiko-framework/files";
28
+ import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
29
+ import { createConfigAccessor } from "../../config";
30
+ import { createFileProviderForTenant } from "../../file-foundation";
31
+
32
+ export interface TenantFileProviderCtx {
33
+ readonly registry?: Registry;
34
+ readonly configResolver?: ConfigResolver;
35
+ readonly secrets?: SecretsContext;
36
+ readonly db: { readonly raw: DbConnection | TenantDb };
37
+ }
38
+
39
+ export async function resolveTenantFileProvider(
40
+ ctx: TenantFileProviderCtx,
41
+ tenantId: string,
42
+ handlerName: string,
43
+ ): Promise<FileStorageProvider> {
44
+ if (!ctx.registry || !ctx.configResolver) {
45
+ throw new Error(
46
+ `${handlerName}: ctx.registry/ctx.configResolver missing — cannot resolve the file provider for tenant ${tenantId}`,
47
+ );
48
+ }
49
+ const config = createConfigAccessor(
50
+ ctx.registry,
51
+ ctx.configResolver,
52
+ tenantId as TenantId, // @cast-boundary engine-payload: TenantId brand
53
+ SYSTEM_USER_ID,
54
+ ctx.db.raw,
55
+ ctx.secrets,
56
+ );
57
+ return createFileProviderForTenant(
58
+ { config, registry: ctx.registry, secrets: ctx.secrets, _userId: SYSTEM_USER_ID },
59
+ tenantId,
60
+ handlerName,
61
+ );
62
+ }
@@ -18,7 +18,6 @@ import {
18
18
  import { expectErrorIncludes, resetTestTables } from "@cosmicdrift/kumiko-framework/testing";
19
19
  import { AuthErrors, AuthHandlers } from "../../auth-email-password/constants";
20
20
  import { createAuthEmailPasswordFeature } from "../../auth-email-password/feature";
21
- import { hashPassword } from "../../auth-email-password/password-hashing";
22
21
  import {
23
22
  createComplianceProfilesFeature,
24
23
  tenantComplianceProfileEntity,
@@ -28,6 +27,7 @@ import { createConfigFeature } from "../../config";
28
27
  import { configValuesTable } from "../../config/table";
29
28
  import { createDataRetentionFeature } from "../../data-retention";
30
29
  import { createSessionsFeature } from "../../sessions";
30
+ import { hashPassword } from "../../shared";
31
31
  import { createTenantFeature } from "../../tenant";
32
32
  import { tenantMembershipsTable } from "../../tenant/membership-table";
33
33
  import { tenantEntity } from "../../tenant/schema/tenant";
@@ -6,8 +6,8 @@ import {
6
6
  } from "@cosmicdrift/kumiko-framework/engine";
7
7
  import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
8
8
  import { z } from "zod";
9
- import { AuthErrors, verifyPassword } from "../../auth-email-password";
10
- import { decryptStoredPii } from "../../shared";
9
+ import { AuthErrors } from "../../auth-email-password";
10
+ import { decryptStoredPii, verifyPassword } from "../../shared";
11
11
  import { UserErrors, UserHandlers, UserQueries } from "../../user";
12
12
  import { UserProfileErrors } from "../constants";
13
13
 
@@ -1,4 +0,0 @@
1
- // Moved to shared/ — reused across admin-shell, audit, sessions, tenant,
2
- // user-data-rights, user-profile, and now auth-mfa. Re-exported here for the
3
- // existing call sites within this feature that import via "./password-hashing".
4
- export { hashPassword, verifyDummyPassword, verifyPassword } from "../shared/password-hashing";