@open-mercato/core 0.6.6-develop.5612.1.d382eb2f33 → 0.6.6-develop.5617.1.62538c48ca

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 (58) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/api_key/index.js +2 -0
  3. package/dist/generated/entities/api_key/index.js.map +2 -2
  4. package/dist/generated/entities/attachment_partition/index.js +4 -0
  5. package/dist/generated/entities/attachment_partition/index.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +3 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/modules/api_keys/data/entities.js +9 -2
  9. package/dist/modules/api_keys/data/entities.js.map +2 -2
  10. package/dist/modules/api_keys/migrations/Migration20260523234901.js +17 -0
  11. package/dist/modules/api_keys/migrations/Migration20260523234901.js.map +7 -0
  12. package/dist/modules/api_keys/services/apiKeyService.js +26 -0
  13. package/dist/modules/api_keys/services/apiKeyService.js.map +2 -2
  14. package/dist/modules/attachments/api/partitions/route.js +54 -12
  15. package/dist/modules/attachments/api/partitions/route.js.map +2 -2
  16. package/dist/modules/attachments/data/entities.js +7 -0
  17. package/dist/modules/attachments/data/entities.js.map +2 -2
  18. package/dist/modules/attachments/migrations/Migration20260524000000.js +27 -0
  19. package/dist/modules/attachments/migrations/Migration20260524000000.js.map +7 -0
  20. package/dist/modules/auth/api/roles/route.js +6 -35
  21. package/dist/modules/auth/api/roles/route.js.map +2 -2
  22. package/dist/modules/auth/cli.js +26 -4
  23. package/dist/modules/auth/cli.js.map +2 -2
  24. package/dist/modules/auth/commands/roles.js +34 -11
  25. package/dist/modules/auth/commands/roles.js.map +2 -2
  26. package/dist/modules/auth/lib/roleTenantGuard.js +41 -13
  27. package/dist/modules/auth/lib/roleTenantGuard.js.map +3 -3
  28. package/dist/modules/auth/lib/setup-app.js +88 -26
  29. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  30. package/dist/modules/currencies/api/fetch-configs/route.js +4 -2
  31. package/dist/modules/currencies/api/fetch-configs/route.js.map +2 -2
  32. package/dist/modules/integrations/api/[id]/route.js +5 -1
  33. package/dist/modules/integrations/api/[id]/route.js.map +2 -2
  34. package/dist/modules/integrations/api/route.js +5 -1
  35. package/dist/modules/integrations/api/route.js.map +2 -2
  36. package/dist/modules/integrations/lib/credentials-service.js +6 -4
  37. package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
  38. package/generated/entities/api_key/index.ts +1 -0
  39. package/generated/entities/attachment_partition/index.ts +2 -0
  40. package/generated/entity-fields-registry.ts +3 -0
  41. package/package.json +7 -7
  42. package/src/modules/api_keys/data/entities.ts +15 -1
  43. package/src/modules/api_keys/migrations/.snapshot-open-mercato.json +18 -0
  44. package/src/modules/api_keys/migrations/Migration20260523234901.ts +17 -0
  45. package/src/modules/api_keys/services/apiKeyService.ts +63 -0
  46. package/src/modules/attachments/api/partitions/route.ts +58 -11
  47. package/src/modules/attachments/data/entities.ts +7 -0
  48. package/src/modules/attachments/migrations/.snapshot-open-mercato.json +28 -0
  49. package/src/modules/attachments/migrations/Migration20260524000000.ts +25 -0
  50. package/src/modules/auth/api/roles/route.ts +7 -38
  51. package/src/modules/auth/cli.ts +27 -4
  52. package/src/modules/auth/commands/roles.ts +39 -11
  53. package/src/modules/auth/lib/roleTenantGuard.ts +56 -17
  54. package/src/modules/auth/lib/setup-app.ts +160 -32
  55. package/src/modules/currencies/api/fetch-configs/route.ts +4 -2
  56. package/src/modules/integrations/api/[id]/route.ts +5 -2
  57. package/src/modules/integrations/api/route.ts +5 -2
  58. package/src/modules/integrations/lib/credentials-service.ts +15 -5
@@ -20,7 +20,7 @@ import {
20
20
  } from "@open-mercato/shared/lib/commands/customFieldSnapshots";
21
21
  import { extractUndoPayload } from "@open-mercato/shared/lib/commands/undo";
22
22
  import { resolveRedoSnapshot } from "@open-mercato/shared/lib/commands/redo";
23
- import { normalizeTenantId } from "@open-mercato/core/modules/auth/lib/tenantAccess";
23
+ import { resolveIsSuperAdmin, normalizeTenantId } from "@open-mercato/core/modules/auth/lib/tenantAccess";
24
24
  function resolveActorTenantScope(ctx) {
25
25
  if (ctx.systemActor === true) return null;
26
26
  const auth = ctx.auth;
@@ -46,6 +46,19 @@ function assertRoleNameAllowed(name) {
46
46
  throw new CrudHttpError(400, { error: "Role name is reserved" });
47
47
  }
48
48
  }
49
+ async function resolveActorScope(ctx) {
50
+ const isSuperAdmin = await resolveIsSuperAdmin(ctx);
51
+ const actorTenantId = normalizeTenantId(ctx.auth?.tenantId ?? null) ?? null;
52
+ return { isSuperAdmin, actorTenantId };
53
+ }
54
+ function buildScopedRoleFilter(roleId, scope) {
55
+ const filter = { id: roleId, deletedAt: null };
56
+ if (!scope.isSuperAdmin) {
57
+ ;
58
+ filter.tenantId = scope.actorTenantId;
59
+ }
60
+ return filter;
61
+ }
49
62
  const createSchema = z.object({
50
63
  name: z.string().min(2).max(100),
51
64
  tenantId: z.string().uuid().optional()
@@ -86,7 +99,12 @@ const createRoleCommand = {
86
99
  }
87
100
  const { parsed, custom } = parseWithCustomFields(createSchema, rawInput);
88
101
  assertRoleNameAllowed(parsed.name);
89
- const resolvedTenantId = parsed.tenantId ?? ctx.auth?.tenantId ?? null;
102
+ const scope = await resolveActorScope(ctx);
103
+ const requestedTenantId = parsed.tenantId ?? null;
104
+ if (!scope.isSuperAdmin && requestedTenantId && requestedTenantId !== scope.actorTenantId) {
105
+ throw new CrudHttpError(403, { error: "Not authorized to target this tenant." });
106
+ }
107
+ const resolvedTenantId = scope.isSuperAdmin ? requestedTenantId ?? scope.actorTenantId : scope.actorTenantId;
90
108
  if (!resolvedTenantId) {
91
109
  throw new CrudHttpError(400, { error: "tenantId is required \u2014 global roles are not supported" });
92
110
  }
@@ -233,7 +251,8 @@ const updateRoleCommand = {
233
251
  async prepare(rawInput, ctx) {
234
252
  const { parsed } = parseWithCustomFields(updateSchema, rawInput);
235
253
  const em = ctx.container.resolve("em");
236
- const existing = await findOneWithDecryption(em, Role, { id: parsed.id, deletedAt: null }, {}, { tenantId: null, organizationId: null });
254
+ const scope = await resolveActorScope(ctx);
255
+ const existing = await findOneWithDecryption(em, Role, buildScopedRoleFilter(parsed.id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null });
237
256
  if (!existing) throw new CrudHttpError(404, { error: "Role not found" });
238
257
  assertRoleTenantInScope(resolveActorTenantScope(ctx), existing.tenantId);
239
258
  const acls = await loadRoleAclSnapshots(em, parsed.id);
@@ -247,7 +266,8 @@ const updateRoleCommand = {
247
266
  async execute(rawInput, ctx) {
248
267
  const { parsed, custom } = parseWithCustomFields(updateSchema, rawInput);
249
268
  const em = ctx.container.resolve("em");
250
- const current = await findOneWithDecryption(em, Role, { id: parsed.id, deletedAt: null }, {}, { tenantId: null, organizationId: null });
269
+ const scope = await resolveActorScope(ctx);
270
+ const current = await findOneWithDecryption(em, Role, buildScopedRoleFilter(parsed.id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null });
251
271
  if (!current) throw new CrudHttpError(404, { error: "Role not found" });
252
272
  const actorTenantScope = resolveActorTenantScope(ctx);
253
273
  assertRoleTenantInScope(actorTenantScope, current.tenantId);
@@ -263,21 +283,22 @@ const updateRoleCommand = {
263
283
  }
264
284
  const wantsTenantChange = parsed.tenantId !== void 0 && parsed.tenantId !== current.tenantId;
265
285
  if (wantsTenantChange) {
286
+ if (!scope.isSuperAdmin) {
287
+ throw new CrudHttpError(403, { error: "Not authorized to target this tenant." });
288
+ }
266
289
  const assignments = await em.count(UserRole, { role: current, deletedAt: null });
267
290
  if (assignments > 0) {
268
291
  throw new CrudHttpError(400, { error: "Role cannot be moved to another tenant while users are assigned" });
269
292
  }
270
293
  await em.nativeDelete(RoleAcl, { role: parsed.id });
271
294
  }
272
- const updateWhere = { id: parsed.id, deletedAt: null };
273
- if (actorTenantScope) updateWhere.tenantId = actorTenantScope;
274
295
  const de = ctx.container.resolve("dataEngine");
275
296
  const role = await de.updateOrmEntity({
276
297
  entity: Role,
277
- where: updateWhere,
298
+ where: buildScopedRoleFilter(parsed.id, scope),
278
299
  apply: (entity) => {
279
300
  if (parsed.name !== void 0) entity.name = parsed.name;
280
- if (parsed.tenantId !== void 0) entity.tenantId = parsed.tenantId;
301
+ if (parsed.tenantId !== void 0 && scope.isSuperAdmin) entity.tenantId = parsed.tenantId;
281
302
  }
282
303
  });
283
304
  if (!role) throw new CrudHttpError(404, { error: "Role not found" });
@@ -396,7 +417,8 @@ const deleteRoleCommand = {
396
417
  async prepare(input, ctx) {
397
418
  const id = requireId(input, "Role id required");
398
419
  const em = ctx.container.resolve("em");
399
- const existing = await findOneWithDecryption(em, Role, { id, deletedAt: null }, {}, { tenantId: null, organizationId: null });
420
+ const scope = await resolveActorScope(ctx);
421
+ const existing = await findOneWithDecryption(em, Role, buildScopedRoleFilter(id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null });
400
422
  if (!existing) return {};
401
423
  const actorTenantScope = resolveActorTenantScope(ctx);
402
424
  if (actorTenantScope) {
@@ -414,7 +436,8 @@ const deleteRoleCommand = {
414
436
  async execute(input, ctx) {
415
437
  const id = requireId(input, "Role id required");
416
438
  const em = ctx.container.resolve("em");
417
- const role = await findOneWithDecryption(em, Role, { id, deletedAt: null }, {}, { tenantId: null, organizationId: null });
439
+ const scope = await resolveActorScope(ctx);
440
+ const role = await findOneWithDecryption(em, Role, buildScopedRoleFilter(id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null });
418
441
  if (!role) throw new CrudHttpError(404, { error: "Role not found" });
419
442
  assertRoleTenantInScope(resolveActorTenantScope(ctx), role.tenantId);
420
443
  const activeAssignments = await em.count(UserRole, { role, deletedAt: null });
@@ -423,7 +446,7 @@ const deleteRoleCommand = {
423
446
  const de = ctx.container.resolve("dataEngine");
424
447
  const deleted = await de.deleteOrmEntity({
425
448
  entity: Role,
426
- where: { id, deletedAt: null },
449
+ where: buildScopedRoleFilter(id, scope),
427
450
  soft: false
428
451
  });
429
452
  if (!deleted) throw new CrudHttpError(404, { error: "Role not found" });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/auth/commands/roles.ts"],
4
- "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n emitCrudUndoSideEffects,\n buildChanges,\n requireId,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport type { CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { Role, RoleAcl, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n loadCustomFieldSnapshot,\n buildCustomFieldResetMap,\n diffCustomFieldChanges,\n} from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { normalizeTenantId } from '@open-mercato/core/modules/auth/lib/tenantAccess'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\n\ntype SerializedRole = {\n name: string\n tenantId: string\n custom?: Record<string, unknown>\n}\n\ntype RoleAclSnapshot = {\n id: string | null\n tenantId: string\n features: string[] | null\n isSuperAdmin: boolean\n organizations: string[] | null\n}\n\ntype RoleUndoSnapshot = {\n id: string\n name: string\n tenantId: string\n acls: RoleAclSnapshot[]\n custom?: Record<string, unknown>\n}\n\ntype RoleSnapshots = {\n view: SerializedRole\n undo: RoleUndoSnapshot\n}\n\nfunction resolveActorTenantScope(ctx: CommandRuntimeContext): string | null {\n if (ctx.systemActor === true) return null\n const auth = ctx.auth\n if (!auth) return null\n if ((auth as { isSuperAdmin?: boolean }).isSuperAdmin === true) return null\n return normalizeTenantId(auth.tenantId ?? null) ?? null\n}\n\nfunction assertRoleTenantInScope(actorTenantScope: string | null, targetTenantId: unknown): void {\n if (!actorTenantScope) return\n const targetTenant = normalizeTenantId(targetTenantId) ?? null\n if (!targetTenant || targetTenant !== actorTenantScope) {\n throw new CrudHttpError(404, { error: 'Role not found' })\n }\n}\n\nconst RESERVED_ROLE_NAMES = new Set(['superadmin', 'admin'])\n\nfunction isReservedRoleName(name: string | undefined | null): boolean {\n if (typeof name !== 'string') return false\n const normalized = name.trim().toLowerCase()\n return normalized.length > 0 && RESERVED_ROLE_NAMES.has(normalized)\n}\n\nfunction assertRoleNameAllowed(name: string | undefined | null) {\n if (isReservedRoleName(name)) {\n throw new CrudHttpError(400, { error: 'Role name is reserved' })\n }\n}\n\nconst createSchema = z.object({\n name: z.string().min(2).max(100),\n tenantId: z.string().uuid().optional(),\n})\n\nconst updateSchema = z.object({\n id: z.string().uuid(),\n name: z.string().min(2).max(100).optional(),\n tenantId: z.string().uuid().optional(),\n})\n\nexport const roleCrudEvents: CrudEventsConfig = {\n module: 'auth',\n entity: 'role',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nexport const roleCrudIndexer: CrudIndexerConfig = {\n entityType: E.auth.role,\n buildUpsertPayload: (ctx) => ({\n entityType: E.auth.role,\n recordId: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n buildDeletePayload: (ctx) => ({\n entityType: E.auth.role,\n recordId: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nconst createRoleCommand: CommandHandler<Record<string, unknown>, Role> = {\n id: 'auth.roles.create',\n async execute(rawInput, ctx) {\n const rawBody = rawInput && typeof rawInput === 'object' ? rawInput as Record<string, unknown> : {}\n if ('tenantId' in rawBody && rawBody.tenantId === null) {\n throw new CrudHttpError(400, { error: 'tenantId cannot be null \u2014 global roles are not supported' })\n }\n const { parsed, custom } = parseWithCustomFields(createSchema, rawInput)\n assertRoleNameAllowed(parsed.name)\n const resolvedTenantId = parsed.tenantId ?? ctx.auth?.tenantId ?? null\n if (!resolvedTenantId) {\n throw new CrudHttpError(400, { error: 'tenantId is required \u2014 global roles are not supported' })\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const role = await de.createOrmEntity({\n entity: Role,\n data: {\n name: parsed.name,\n tenantId: resolvedTenantId,\n },\n })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: String(role.id),\n organizationId: null,\n tenantId: resolvedTenantId,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: role,\n identifiers: {\n id: String(role.id),\n organizationId: null,\n tenantId: resolvedTenantId,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return role\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n return serializeRole(result, custom)\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n const snapshot = captureRoleSnapshots(result, [], custom)\n return {\n actionLabel: translate('auth.audit.roles.create', 'Create role'),\n resourceKind: 'auth.role',\n resourceId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n snapshotAfter: snapshot.view,\n payload: {\n undo: {\n after: snapshot.undo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<RoleUndoPayload>(logEntry)?.after\n if (!undo) return\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await em.nativeDelete(RoleAcl, { role: undo.id as unknown as Role })\n if (undo.custom && Object.keys(undo.custom).length) {\n const reset = buildCustomFieldResetMap(undefined, undo.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: undo.id,\n organizationId: null,\n tenantId: undo.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n }\n await de.deleteOrmEntity({\n entity: Role,\n where: { id: undo.id, deletedAt: null } as FilterQuery<Role>,\n soft: false,\n })\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<RoleUndoSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for role create' })\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let role = await findOneWithDecryption(em, Role, { id: after.id }, {}, { tenantId: null, organizationId: null })\n if (role) {\n role.deletedAt = null\n role.name = after.name\n role.tenantId = after.tenantId\n await em.flush()\n } else {\n role = await de.createOrmEntity({\n entity: Role,\n data: {\n id: after.id,\n name: after.name,\n tenantId: after.tenantId,\n },\n })\n }\n await restoreRoleAcls(em, after.id, after.acls)\n if (after.custom && Object.keys(after.custom).length) {\n const reset = buildCustomFieldResetMap(after.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: after.id,\n organizationId: null,\n tenantId: after.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n }\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: role,\n identifiers: {\n id: after.id,\n organizationId: null,\n tenantId: after.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n return role\n },\n}\n\nconst updateRoleCommand: CommandHandler<Record<string, unknown>, Role> = {\n id: 'auth.roles.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(updateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const existing = await findOneWithDecryption(em, Role, { id: parsed.id, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n if (!existing) throw new CrudHttpError(404, { error: 'Role not found' })\n assertRoleTenantInScope(resolveActorTenantScope(ctx), existing.tenantId)\n const acls = await loadRoleAclSnapshots(em, parsed.id)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: parsed.id,\n tenantId: existing.tenantId ? String(existing.tenantId) : null,\n })\n return { before: captureRoleSnapshots(existing, acls, custom) }\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(updateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const current = await findOneWithDecryption(em, Role, { id: parsed.id, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n if (!current) throw new CrudHttpError(404, { error: 'Role not found' })\n const actorTenantScope = resolveActorTenantScope(ctx)\n assertRoleTenantInScope(actorTenantScope, current.tenantId)\n if (parsed.name !== undefined) {\n const nextName = parsed.name\n if (nextName !== current.name) assertRoleNameAllowed(nextName)\n if (nextName !== current.name) {\n const assignments = await em.count(UserRole, { role: current, deletedAt: null })\n if (assignments > 0) {\n throw new CrudHttpError(400, { error: 'Role name cannot be changed while users are assigned' })\n }\n }\n }\n const wantsTenantChange = parsed.tenantId !== undefined && parsed.tenantId !== current.tenantId\n if (wantsTenantChange) {\n const assignments = await em.count(UserRole, { role: current, deletedAt: null })\n if (assignments > 0) {\n throw new CrudHttpError(400, { error: 'Role cannot be moved to another tenant while users are assigned' })\n }\n await em.nativeDelete(RoleAcl, { role: parsed.id as unknown as Role })\n }\n const updateWhere: Record<string, unknown> = { id: parsed.id, deletedAt: null }\n if (actorTenantScope) updateWhere.tenantId = actorTenantScope\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const role = await de.updateOrmEntity({\n entity: Role,\n where: updateWhere as FilterQuery<Role>,\n apply: (entity) => {\n if (parsed.name !== undefined) entity.name = parsed.name\n if (parsed.tenantId !== undefined) entity.tenantId = parsed.tenantId\n },\n })\n if (!role) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: String(role.id),\n organizationId: null,\n tenantId: role.tenantId ? String(role.tenantId) : null,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: role,\n identifiers: {\n id: String(role.id),\n organizationId: null,\n tenantId: role.tenantId ? String(role.tenantId) : null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return role\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n return serializeRole(result, custom)\n },\n buildLog: async ({ result, snapshots, ctx }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as RoleSnapshots | undefined\n const before = beforeSnapshots?.view\n const beforeUndo = beforeSnapshots?.undo ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const afterAcls = await loadRoleAclSnapshots(em, String(result.id))\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n const afterSnapshots = captureRoleSnapshots(result, afterAcls, custom)\n const after = afterSnapshots.view\n const changes = buildChanges(before ?? null, after as Record<string, unknown>, ['name', 'tenantId'])\n const customDiff = diffCustomFieldChanges(before?.custom, custom)\n for (const [key, diff] of Object.entries(customDiff)) {\n changes[`cf_${key}`] = diff\n }\n return {\n actionLabel: translate('auth.audit.roles.update', 'Update role'),\n resourceKind: 'auth.role',\n resourceId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n changes,\n snapshotBefore: before ?? null,\n snapshotAfter: after,\n payload: {\n undo: {\n before: beforeUndo,\n after: afterSnapshots.undo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<RoleUndoPayload>(logEntry)\n const before = undo?.before\n const after = undo?.after\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const updated = await de.updateOrmEntity({\n entity: Role,\n where: { id: before.id, deletedAt: null } as FilterQuery<Role>,\n apply: (entity) => {\n entity.name = before.name\n entity.tenantId = before.tenantId\n },\n })\n if (updated) {\n await restoreRoleAcls(em, before.id, before.acls)\n }\n const reset = buildCustomFieldResetMap(before.custom, after?.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: before.id,\n organizationId: null,\n tenantId: before.tenantId,\n values: reset,\n notify: false,\n })\n }\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: updated,\n identifiers: {\n id: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n },\n}\n\nconst deleteRoleCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, Role> = {\n id: 'auth.roles.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Role id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const existing = await findOneWithDecryption(em, Role, { id, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n if (!existing) return {}\n const actorTenantScope = resolveActorTenantScope(ctx)\n if (actorTenantScope) {\n const targetTenant = normalizeTenantId(existing.tenantId) ?? null\n if (!targetTenant || targetTenant !== actorTenantScope) return {}\n }\n const acls = await loadRoleAclSnapshots(em, id)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: id,\n tenantId: existing.tenantId ? String(existing.tenantId) : null,\n })\n return { before: captureRoleSnapshots(existing, acls, custom) }\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Role id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const role = await findOneWithDecryption(em, Role, { id, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n if (!role) throw new CrudHttpError(404, { error: 'Role not found' })\n assertRoleTenantInScope(resolveActorTenantScope(ctx), role.tenantId)\n const activeAssignments = await em.count(UserRole, { role, deletedAt: null })\n if (activeAssignments > 0) throw new CrudHttpError(400, { error: 'Role has assigned users' })\n\n await em.nativeDelete(RoleAcl, { role: id })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const deleted = await de.deleteOrmEntity({\n entity: Role,\n where: { id, deletedAt: null } as FilterQuery<Role>,\n soft: false,\n })\n if (!deleted) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: deleted,\n identifiers: {\n id,\n organizationId: null,\n tenantId: deleted.tenantId ? String(deleted.tenantId) : null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return deleted\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as RoleSnapshots | undefined\n const before = beforeSnapshots?.view\n const beforeUndo = beforeSnapshots?.undo ?? null\n const id = requireId(input, 'Role id required')\n return {\n actionLabel: translate('auth.audit.roles.delete', 'Delete role'),\n resourceKind: 'auth.role',\n resourceId: id,\n tenantId: before?.tenantId ?? null,\n snapshotBefore: before ?? null,\n payload: {\n undo: {\n before: beforeUndo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const before = extractUndoPayload<RoleUndoPayload>(logEntry)?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let role = await findOneWithDecryption(em, Role, { id: before.id }, {}, { tenantId: null, organizationId: null })\n if (role) {\n role.deletedAt = null\n role.name = before.name\n role.tenantId = before.tenantId\n await em.flush()\n } else {\n role = await de.createOrmEntity({\n entity: Role,\n data: {\n id: before.id,\n name: before.name,\n tenantId: before.tenantId,\n },\n })\n }\n await restoreRoleAcls(em, before.id, before.acls)\n const reset = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: role,\n identifiers: {\n id: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n },\n}\n\nregisterCommand(createRoleCommand)\nregisterCommand(updateRoleCommand)\nregisterCommand(deleteRoleCommand)\n\nfunction serializeRole(role: Role, custom?: Record<string, unknown> | null): SerializedRole {\n const payload: SerializedRole = {\n name: String(role.name ?? ''),\n tenantId: String(role.tenantId),\n }\n if (custom && Object.keys(custom).length) payload.custom = custom\n return payload\n}\n\nfunction captureRoleSnapshots(\n role: Role,\n acls: RoleAclSnapshot[] = [],\n custom?: Record<string, unknown> | null\n): RoleSnapshots {\n return {\n view: serializeRole(role, custom),\n undo: {\n id: String(role.id),\n name: String(role.name ?? ''),\n tenantId: String(role.tenantId),\n acls,\n ...(custom && Object.keys(custom).length ? { custom } : {}),\n },\n }\n}\n\nasync function loadRoleAclSnapshots(em: EntityManager, roleId: string): Promise<RoleAclSnapshot[]> {\n const entries = await findWithDecryption(em, RoleAcl, { role: roleId as unknown as Role }, {}, { tenantId: null, organizationId: null })\n return entries.map((entry) => ({\n id: entry.id ? String(entry.id) : null,\n tenantId: String(entry.tenantId),\n features: Array.isArray(entry.featuresJson) ? [...entry.featuresJson] : null,\n isSuperAdmin: Boolean(entry.isSuperAdmin),\n organizations: Array.isArray(entry.organizationsJson) ? [...entry.organizationsJson] : null,\n }))\n}\n\nasync function restoreRoleAcls(em: EntityManager, roleId: string, acls: RoleAclSnapshot[]) {\n await em.nativeDelete(RoleAcl, { role: roleId as unknown as Role })\n if (!acls.length) {\n await em.flush()\n return\n }\n const roleRef = em.getReference(Role, roleId)\n for (const acl of acls) {\n const entity = em.create(RoleAcl, {\n id: acl.id ?? undefined,\n role: roleRef,\n tenantId: acl.tenantId,\n featuresJson: acl.features ?? null,\n isSuperAdmin: acl.isSuperAdmin,\n organizationsJson: acl.organizations ?? null,\n createdAt: new Date(),\n })\n em.persist(entity)\n }\n await em.flush()\n}\n\ntype RoleUndoPayload = { before?: RoleUndoSnapshot | null; after?: RoleUndoSnapshot | null }\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n emitCrudUndoSideEffects,\n buildChanges,\n requireId,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport type { CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { Role, RoleAcl, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n loadCustomFieldSnapshot,\n buildCustomFieldResetMap,\n diffCustomFieldChanges,\n} from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { resolveIsSuperAdmin, normalizeTenantId } from '@open-mercato/core/modules/auth/lib/tenantAccess'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\n\ntype SerializedRole = {\n name: string\n tenantId: string\n custom?: Record<string, unknown>\n}\n\ntype RoleAclSnapshot = {\n id: string | null\n tenantId: string\n features: string[] | null\n isSuperAdmin: boolean\n organizations: string[] | null\n}\n\ntype RoleUndoSnapshot = {\n id: string\n name: string\n tenantId: string\n acls: RoleAclSnapshot[]\n custom?: Record<string, unknown>\n}\n\ntype RoleSnapshots = {\n view: SerializedRole\n undo: RoleUndoSnapshot\n}\n\nfunction resolveActorTenantScope(ctx: CommandRuntimeContext): string | null {\n if (ctx.systemActor === true) return null\n const auth = ctx.auth\n if (!auth) return null\n if ((auth as { isSuperAdmin?: boolean }).isSuperAdmin === true) return null\n return normalizeTenantId(auth.tenantId ?? null) ?? null\n}\n\nfunction assertRoleTenantInScope(actorTenantScope: string | null, targetTenantId: unknown): void {\n if (!actorTenantScope) return\n const targetTenant = normalizeTenantId(targetTenantId) ?? null\n if (!targetTenant || targetTenant !== actorTenantScope) {\n throw new CrudHttpError(404, { error: 'Role not found' })\n }\n}\n\nconst RESERVED_ROLE_NAMES = new Set(['superadmin', 'admin'])\n\nfunction isReservedRoleName(name: string | undefined | null): boolean {\n if (typeof name !== 'string') return false\n const normalized = name.trim().toLowerCase()\n return normalized.length > 0 && RESERVED_ROLE_NAMES.has(normalized)\n}\n\nfunction assertRoleNameAllowed(name: string | undefined | null) {\n if (isReservedRoleName(name)) {\n throw new CrudHttpError(400, { error: 'Role name is reserved' })\n }\n}\n\ntype ResolvedActorScope = { isSuperAdmin: boolean; actorTenantId: string | null }\n\nasync function resolveActorScope(ctx: { auth: { tenantId?: string | null; sub?: string | null; orgId?: string | null; isSuperAdmin?: boolean } | null; container: { resolve<T = unknown>(name: string): T } }): Promise<ResolvedActorScope> {\n const isSuperAdmin = await resolveIsSuperAdmin(ctx)\n const actorTenantId = normalizeTenantId(ctx.auth?.tenantId ?? null) ?? null\n return { isSuperAdmin, actorTenantId }\n}\n\nfunction buildScopedRoleFilter(roleId: string, scope: ResolvedActorScope): FilterQuery<Role> {\n const filter: FilterQuery<Role> = { id: roleId, deletedAt: null }\n if (!scope.isSuperAdmin) {\n ;(filter as Record<string, unknown>).tenantId = scope.actorTenantId\n }\n return filter\n}\n\nconst createSchema = z.object({\n name: z.string().min(2).max(100),\n tenantId: z.string().uuid().optional(),\n})\n\nconst updateSchema = z.object({\n id: z.string().uuid(),\n name: z.string().min(2).max(100).optional(),\n tenantId: z.string().uuid().optional(),\n})\n\nexport const roleCrudEvents: CrudEventsConfig = {\n module: 'auth',\n entity: 'role',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nexport const roleCrudIndexer: CrudIndexerConfig = {\n entityType: E.auth.role,\n buildUpsertPayload: (ctx) => ({\n entityType: E.auth.role,\n recordId: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n buildDeletePayload: (ctx) => ({\n entityType: E.auth.role,\n recordId: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nconst createRoleCommand: CommandHandler<Record<string, unknown>, Role> = {\n id: 'auth.roles.create',\n async execute(rawInput, ctx) {\n const rawBody = rawInput && typeof rawInput === 'object' ? rawInput as Record<string, unknown> : {}\n if ('tenantId' in rawBody && rawBody.tenantId === null) {\n throw new CrudHttpError(400, { error: 'tenantId cannot be null \u2014 global roles are not supported' })\n }\n const { parsed, custom } = parseWithCustomFields(createSchema, rawInput)\n assertRoleNameAllowed(parsed.name)\n const scope = await resolveActorScope(ctx)\n const requestedTenantId = parsed.tenantId ?? null\n if (!scope.isSuperAdmin && requestedTenantId && requestedTenantId !== scope.actorTenantId) {\n throw new CrudHttpError(403, { error: 'Not authorized to target this tenant.' })\n }\n const resolvedTenantId = scope.isSuperAdmin\n ? (requestedTenantId ?? scope.actorTenantId)\n : scope.actorTenantId\n if (!resolvedTenantId) {\n throw new CrudHttpError(400, { error: 'tenantId is required \u2014 global roles are not supported' })\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const role = await de.createOrmEntity({\n entity: Role,\n data: {\n name: parsed.name,\n tenantId: resolvedTenantId,\n },\n })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: String(role.id),\n organizationId: null,\n tenantId: resolvedTenantId,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: role,\n identifiers: {\n id: String(role.id),\n organizationId: null,\n tenantId: resolvedTenantId,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return role\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n return serializeRole(result, custom)\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n const snapshot = captureRoleSnapshots(result, [], custom)\n return {\n actionLabel: translate('auth.audit.roles.create', 'Create role'),\n resourceKind: 'auth.role',\n resourceId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n snapshotAfter: snapshot.view,\n payload: {\n undo: {\n after: snapshot.undo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<RoleUndoPayload>(logEntry)?.after\n if (!undo) return\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await em.nativeDelete(RoleAcl, { role: undo.id as unknown as Role })\n if (undo.custom && Object.keys(undo.custom).length) {\n const reset = buildCustomFieldResetMap(undefined, undo.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: undo.id,\n organizationId: null,\n tenantId: undo.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n }\n await de.deleteOrmEntity({\n entity: Role,\n where: { id: undo.id, deletedAt: null } as FilterQuery<Role>,\n soft: false,\n })\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<RoleUndoSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for role create' })\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let role = await findOneWithDecryption(em, Role, { id: after.id }, {}, { tenantId: null, organizationId: null })\n if (role) {\n role.deletedAt = null\n role.name = after.name\n role.tenantId = after.tenantId\n await em.flush()\n } else {\n role = await de.createOrmEntity({\n entity: Role,\n data: {\n id: after.id,\n name: after.name,\n tenantId: after.tenantId,\n },\n })\n }\n await restoreRoleAcls(em, after.id, after.acls)\n if (after.custom && Object.keys(after.custom).length) {\n const reset = buildCustomFieldResetMap(after.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: after.id,\n organizationId: null,\n tenantId: after.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n }\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: role,\n identifiers: {\n id: after.id,\n organizationId: null,\n tenantId: after.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n return role\n },\n}\n\nconst updateRoleCommand: CommandHandler<Record<string, unknown>, Role> = {\n id: 'auth.roles.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(updateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const scope = await resolveActorScope(ctx)\n const existing = await findOneWithDecryption(em, Role, buildScopedRoleFilter(parsed.id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null })\n if (!existing) throw new CrudHttpError(404, { error: 'Role not found' })\n assertRoleTenantInScope(resolveActorTenantScope(ctx), existing.tenantId)\n const acls = await loadRoleAclSnapshots(em, parsed.id)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: parsed.id,\n tenantId: existing.tenantId ? String(existing.tenantId) : null,\n })\n return { before: captureRoleSnapshots(existing, acls, custom) }\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(updateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const scope = await resolveActorScope(ctx)\n const current = await findOneWithDecryption(em, Role, buildScopedRoleFilter(parsed.id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null })\n if (!current) throw new CrudHttpError(404, { error: 'Role not found' })\n const actorTenantScope = resolveActorTenantScope(ctx)\n assertRoleTenantInScope(actorTenantScope, current.tenantId)\n if (parsed.name !== undefined) {\n const nextName = parsed.name\n if (nextName !== current.name) assertRoleNameAllowed(nextName)\n if (nextName !== current.name) {\n const assignments = await em.count(UserRole, { role: current, deletedAt: null })\n if (assignments > 0) {\n throw new CrudHttpError(400, { error: 'Role name cannot be changed while users are assigned' })\n }\n }\n }\n const wantsTenantChange = parsed.tenantId !== undefined && parsed.tenantId !== current.tenantId\n if (wantsTenantChange) {\n if (!scope.isSuperAdmin) {\n throw new CrudHttpError(403, { error: 'Not authorized to target this tenant.' })\n }\n const assignments = await em.count(UserRole, { role: current, deletedAt: null })\n if (assignments > 0) {\n throw new CrudHttpError(400, { error: 'Role cannot be moved to another tenant while users are assigned' })\n }\n await em.nativeDelete(RoleAcl, { role: parsed.id as unknown as Role })\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const role = await de.updateOrmEntity({\n entity: Role,\n where: buildScopedRoleFilter(parsed.id, scope),\n apply: (entity) => {\n if (parsed.name !== undefined) entity.name = parsed.name\n if (parsed.tenantId !== undefined && scope.isSuperAdmin) entity.tenantId = parsed.tenantId\n },\n })\n if (!role) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: String(role.id),\n organizationId: null,\n tenantId: role.tenantId ? String(role.tenantId) : null,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: role,\n identifiers: {\n id: String(role.id),\n organizationId: null,\n tenantId: role.tenantId ? String(role.tenantId) : null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return role\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n return serializeRole(result, custom)\n },\n buildLog: async ({ result, snapshots, ctx }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as RoleSnapshots | undefined\n const before = beforeSnapshots?.view\n const beforeUndo = beforeSnapshots?.undo ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const afterAcls = await loadRoleAclSnapshots(em, String(result.id))\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n const afterSnapshots = captureRoleSnapshots(result, afterAcls, custom)\n const after = afterSnapshots.view\n const changes = buildChanges(before ?? null, after as Record<string, unknown>, ['name', 'tenantId'])\n const customDiff = diffCustomFieldChanges(before?.custom, custom)\n for (const [key, diff] of Object.entries(customDiff)) {\n changes[`cf_${key}`] = diff\n }\n return {\n actionLabel: translate('auth.audit.roles.update', 'Update role'),\n resourceKind: 'auth.role',\n resourceId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n changes,\n snapshotBefore: before ?? null,\n snapshotAfter: after,\n payload: {\n undo: {\n before: beforeUndo,\n after: afterSnapshots.undo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<RoleUndoPayload>(logEntry)\n const before = undo?.before\n const after = undo?.after\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const updated = await de.updateOrmEntity({\n entity: Role,\n where: { id: before.id, deletedAt: null } as FilterQuery<Role>,\n apply: (entity) => {\n entity.name = before.name\n entity.tenantId = before.tenantId\n },\n })\n if (updated) {\n await restoreRoleAcls(em, before.id, before.acls)\n }\n const reset = buildCustomFieldResetMap(before.custom, after?.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: before.id,\n organizationId: null,\n tenantId: before.tenantId,\n values: reset,\n notify: false,\n })\n }\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: updated,\n identifiers: {\n id: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n },\n}\n\nconst deleteRoleCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, Role> = {\n id: 'auth.roles.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Role id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const scope = await resolveActorScope(ctx)\n const existing = await findOneWithDecryption(em, Role, buildScopedRoleFilter(id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null })\n if (!existing) return {}\n const actorTenantScope = resolveActorTenantScope(ctx)\n if (actorTenantScope) {\n const targetTenant = normalizeTenantId(existing.tenantId) ?? null\n if (!targetTenant || targetTenant !== actorTenantScope) return {}\n }\n const acls = await loadRoleAclSnapshots(em, id)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: id,\n tenantId: existing.tenantId ? String(existing.tenantId) : null,\n })\n return { before: captureRoleSnapshots(existing, acls, custom) }\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Role id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const scope = await resolveActorScope(ctx)\n const role = await findOneWithDecryption(em, Role, buildScopedRoleFilter(id, scope), {}, { tenantId: scope.actorTenantId, organizationId: null })\n if (!role) throw new CrudHttpError(404, { error: 'Role not found' })\n assertRoleTenantInScope(resolveActorTenantScope(ctx), role.tenantId)\n const activeAssignments = await em.count(UserRole, { role, deletedAt: null })\n if (activeAssignments > 0) throw new CrudHttpError(400, { error: 'Role has assigned users' })\n\n await em.nativeDelete(RoleAcl, { role: id })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const deleted = await de.deleteOrmEntity({\n entity: Role,\n where: buildScopedRoleFilter(id, scope),\n soft: false,\n })\n if (!deleted) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: deleted,\n identifiers: {\n id,\n organizationId: null,\n tenantId: deleted.tenantId ? String(deleted.tenantId) : null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return deleted\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as RoleSnapshots | undefined\n const before = beforeSnapshots?.view\n const beforeUndo = beforeSnapshots?.undo ?? null\n const id = requireId(input, 'Role id required')\n return {\n actionLabel: translate('auth.audit.roles.delete', 'Delete role'),\n resourceKind: 'auth.role',\n resourceId: id,\n tenantId: before?.tenantId ?? null,\n snapshotBefore: before ?? null,\n payload: {\n undo: {\n before: beforeUndo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const before = extractUndoPayload<RoleUndoPayload>(logEntry)?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let role = await findOneWithDecryption(em, Role, { id: before.id }, {}, { tenantId: null, organizationId: null })\n if (role) {\n role.deletedAt = null\n role.name = before.name\n role.tenantId = before.tenantId\n await em.flush()\n } else {\n role = await de.createOrmEntity({\n entity: Role,\n data: {\n id: before.id,\n name: before.name,\n tenantId: before.tenantId,\n },\n })\n }\n await restoreRoleAcls(em, before.id, before.acls)\n const reset = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: role,\n identifiers: {\n id: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n },\n}\n\nregisterCommand(createRoleCommand)\nregisterCommand(updateRoleCommand)\nregisterCommand(deleteRoleCommand)\n\nfunction serializeRole(role: Role, custom?: Record<string, unknown> | null): SerializedRole {\n const payload: SerializedRole = {\n name: String(role.name ?? ''),\n tenantId: String(role.tenantId),\n }\n if (custom && Object.keys(custom).length) payload.custom = custom\n return payload\n}\n\nfunction captureRoleSnapshots(\n role: Role,\n acls: RoleAclSnapshot[] = [],\n custom?: Record<string, unknown> | null\n): RoleSnapshots {\n return {\n view: serializeRole(role, custom),\n undo: {\n id: String(role.id),\n name: String(role.name ?? ''),\n tenantId: String(role.tenantId),\n acls,\n ...(custom && Object.keys(custom).length ? { custom } : {}),\n },\n }\n}\n\nasync function loadRoleAclSnapshots(em: EntityManager, roleId: string): Promise<RoleAclSnapshot[]> {\n const entries = await findWithDecryption(em, RoleAcl, { role: roleId as unknown as Role }, {}, { tenantId: null, organizationId: null })\n return entries.map((entry) => ({\n id: entry.id ? String(entry.id) : null,\n tenantId: String(entry.tenantId),\n features: Array.isArray(entry.featuresJson) ? [...entry.featuresJson] : null,\n isSuperAdmin: Boolean(entry.isSuperAdmin),\n organizations: Array.isArray(entry.organizationsJson) ? [...entry.organizationsJson] : null,\n }))\n}\n\nasync function restoreRoleAcls(em: EntityManager, roleId: string, acls: RoleAclSnapshot[]) {\n await em.nativeDelete(RoleAcl, { role: roleId as unknown as Role })\n if (!acls.length) {\n await em.flush()\n return\n }\n const roleRef = em.getReference(Role, roleId)\n for (const acl of acls) {\n const entity = em.create(RoleAcl, {\n id: acl.id ?? undefined,\n role: roleRef,\n tenantId: acl.tenantId,\n featuresJson: acl.features ?? null,\n isSuperAdmin: acl.isSuperAdmin,\n organizationsJson: acl.organizations ?? null,\n createdAt: new Date(),\n })\n em.persist(entity)\n }\n await em.flush()\n}\n\ntype RoleUndoPayload = { before?: RoleUndoSnapshot | null; after?: RoleUndoSnapshot | null }\n"],
5
+ "mappings": "AACA,SAAS,uBAAuB;AAChC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AAEpC,SAAS,uBAAuB,0BAA0B;AAE1D,SAAS,SAAS;AAClB,SAAS,MAAM,SAAS,gBAAgB;AACxC,SAAS,SAAS;AAClB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AACpC,SAAS,qBAAqB,yBAAyB;AA8BvD,SAAS,wBAAwB,KAA2C;AAC1E,MAAI,IAAI,gBAAgB,KAAM,QAAO;AACrC,QAAM,OAAO,IAAI;AACjB,MAAI,CAAC,KAAM,QAAO;AAClB,MAAK,KAAoC,iBAAiB,KAAM,QAAO;AACvE,SAAO,kBAAkB,KAAK,YAAY,IAAI,KAAK;AACrD;AAEA,SAAS,wBAAwB,kBAAiC,gBAA+B;AAC/F,MAAI,CAAC,iBAAkB;AACvB,QAAM,eAAe,kBAAkB,cAAc,KAAK;AAC1D,MAAI,CAAC,gBAAgB,iBAAiB,kBAAkB;AACtD,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,EAC1D;AACF;AAEA,MAAM,sBAAsB,oBAAI,IAAI,CAAC,cAAc,OAAO,CAAC;AAE3D,SAAS,mBAAmB,MAA0C;AACpE,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,QAAM,aAAa,KAAK,KAAK,EAAE,YAAY;AAC3C,SAAO,WAAW,SAAS,KAAK,oBAAoB,IAAI,UAAU;AACpE;AAEA,SAAS,sBAAsB,MAAiC;AAC9D,MAAI,mBAAmB,IAAI,GAAG;AAC5B,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,wBAAwB,CAAC;AAAA,EACjE;AACF;AAIA,eAAe,kBAAkB,KAA2M;AAC1O,QAAM,eAAe,MAAM,oBAAoB,GAAG;AAClD,QAAM,gBAAgB,kBAAkB,IAAI,MAAM,YAAY,IAAI,KAAK;AACvE,SAAO,EAAE,cAAc,cAAc;AACvC;AAEA,SAAS,sBAAsB,QAAgB,OAA8C;AAC3F,QAAM,SAA4B,EAAE,IAAI,QAAQ,WAAW,KAAK;AAChE,MAAI,CAAC,MAAM,cAAc;AACvB;AAAC,IAAC,OAAmC,WAAW,MAAM;AAAA,EACxD;AACA,SAAO;AACT;AAEA,MAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAC/B,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AACvC,CAAC;AAED,MAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EAC1C,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AACvC,CAAC;AAEM,MAAM,iBAAmC;AAAA,EAC9C,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,cAAc,CAAC,SAAS;AAAA,IACtB,IAAI,IAAI,YAAY;AAAA,IACpB,UAAU,IAAI,YAAY;AAAA,EAC5B;AACF;AAEO,MAAM,kBAAqC;AAAA,EAChD,YAAY,EAAE,KAAK;AAAA,EACnB,oBAAoB,CAAC,SAAS;AAAA,IAC5B,YAAY,EAAE,KAAK;AAAA,IACnB,UAAU,IAAI,YAAY;AAAA,IAC1B,UAAU,IAAI,YAAY;AAAA,EAC5B;AAAA,EACA,oBAAoB,CAAC,SAAS;AAAA,IAC5B,YAAY,EAAE,KAAK;AAAA,IACnB,UAAU,IAAI,YAAY;AAAA,IAC1B,UAAU,IAAI,YAAY;AAAA,EAC5B;AACF;AAEA,MAAM,oBAAmE;AAAA,EACvE,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,UAAU,YAAY,OAAO,aAAa,WAAW,WAAsC,CAAC;AAClG,QAAI,cAAc,WAAW,QAAQ,aAAa,MAAM;AACtD,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,gEAA2D,CAAC;AAAA,IACpG;AACA,UAAM,EAAE,QAAQ,OAAO,IAAI,sBAAsB,cAAc,QAAQ;AACvE,0BAAsB,OAAO,IAAI;AACjC,UAAM,QAAQ,MAAM,kBAAkB,GAAG;AACzC,UAAM,oBAAoB,OAAO,YAAY;AAC7C,QAAI,CAAC,MAAM,gBAAgB,qBAAqB,sBAAsB,MAAM,eAAe;AACzF,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,wCAAwC,CAAC;AAAA,IACjF;AACA,UAAM,mBAAmB,MAAM,eAC1B,qBAAqB,MAAM,gBAC5B,MAAM;AACV,QAAI,CAAC,kBAAkB;AACrB,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,6DAAwD,CAAC;AAAA,IACjG;AACA,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,OAAO,MAAM,GAAG,gBAAgB;AAAA,MACpC,QAAQ;AAAA,MACR,MAAM;AAAA,QACJ,MAAM,OAAO;AAAA,QACb,UAAU;AAAA,MACZ;AAAA,IACF,CAAC;AAED,UAAM,qBAAqB;AAAA,MACzB,YAAY;AAAA,MACZ,UAAU,EAAE,KAAK;AAAA,MACjB,UAAU,OAAO,KAAK,EAAE;AAAA,MACxB,gBAAgB;AAAA,MAChB,UAAU;AAAA,MACV,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO,KAAK,EAAE;AAAA,QAClB,gBAAgB;AAAA,QAChB,UAAU;AAAA,MACZ;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EACA,cAAc,OAAO,QAAQ,QAAQ,QAAQ;AAC3C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM,wBAAwB,IAAI;AAAA,MAC/C,UAAU,EAAE,KAAK;AAAA,MACjB,UAAU,OAAO,OAAO,EAAE;AAAA,MAC1B,UAAU,OAAO,WAAW,OAAO,OAAO,QAAQ,IAAI;AAAA,IACxD,CAAC;AACD,WAAO,cAAc,QAAQ,MAAM;AAAA,EACrC;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,IAAI,MAAM;AACnC,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM,wBAAwB,IAAI;AAAA,MAC/C,UAAU,EAAE,KAAK;AAAA,MACjB,UAAU,OAAO,OAAO,EAAE;AAAA,MAC1B,UAAU,OAAO,WAAW,OAAO,OAAO,QAAQ,IAAI;AAAA,IACxD,CAAC;AACD,UAAM,WAAW,qBAAqB,QAAQ,CAAC,GAAG,MAAM;AACxD,WAAO;AAAA,MACL,aAAa,UAAU,2BAA2B,aAAa;AAAA,MAC/D,cAAc;AAAA,MACd,YAAY,OAAO,OAAO,EAAE;AAAA,MAC5B,UAAU,OAAO,WAAW,OAAO,OAAO,QAAQ,IAAI;AAAA,MACtD,eAAe,SAAS;AAAA,MACxB,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,OAAO,SAAS;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,OAAO,mBAAoC,QAAQ,GAAG;AAC5D,QAAI,CAAC,KAAM;AACX,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,GAAG,aAAa,SAAS,EAAE,MAAM,KAAK,GAAsB,CAAC;AACnE,QAAI,KAAK,UAAU,OAAO,KAAK,KAAK,MAAM,EAAE,QAAQ;AAClD,YAAM,QAAQ,yBAAyB,QAAW,KAAK,MAAM;AAC7D,UAAI,OAAO,KAAK,KAAK,EAAE,QAAQ;AAC7B,cAAM,qBAAqB;AAAA,UACzB,YAAY;AAAA,UACZ,UAAU,EAAE,KAAK;AAAA,UACjB,UAAU,KAAK;AAAA,UACf,gBAAgB;AAAA,UAChB,UAAU,KAAK,YAAY;AAAA,UAC3B,QAAQ;AAAA,UACR,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAAA,IACF;AACA,UAAM,GAAG,gBAAgB;AAAA,MACvB,QAAQ;AAAA,MACR,OAAO,EAAE,IAAI,KAAK,IAAI,WAAW,KAAK;AAAA,MACtC,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,QAAQ,oBAAsC,QAAQ;AAC5D,QAAI,CAAC,MAAO,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,uDAAuD,CAAC;AAC1G,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,QAAI,OAAO,MAAM,sBAAsB,IAAI,MAAM,EAAE,IAAI,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,UAAU,MAAM,gBAAgB,KAAK,CAAC;AAC/G,QAAI,MAAM;AACR,WAAK,YAAY;AACjB,WAAK,OAAO,MAAM;AAClB,WAAK,WAAW,MAAM;AACtB,YAAM,GAAG,MAAM;AAAA,IACjB,OAAO;AACL,aAAO,MAAM,GAAG,gBAAgB;AAAA,QAC9B,QAAQ;AAAA,QACR,MAAM;AAAA,UACJ,IAAI,MAAM;AAAA,UACV,MAAM,MAAM;AAAA,UACZ,UAAU,MAAM;AAAA,QAClB;AAAA,MACF,CAAC;AAAA,IACH;AACA,UAAM,gBAAgB,IAAI,MAAM,IAAI,MAAM,IAAI;AAC9C,QAAI,MAAM,UAAU,OAAO,KAAK,MAAM,MAAM,EAAE,QAAQ;AACpD,YAAM,QAAQ,yBAAyB,MAAM,QAAQ,MAAS;AAC9D,UAAI,OAAO,KAAK,KAAK,EAAE,QAAQ;AAC7B,cAAM,qBAAqB;AAAA,UACzB,YAAY;AAAA,UACZ,UAAU,EAAE,KAAK;AAAA,UACjB,UAAU,MAAM;AAAA,UAChB,gBAAgB;AAAA,UAChB,UAAU,MAAM,YAAY;AAAA,UAC5B,QAAQ;AAAA,UACR,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAAA,IACF;AACA,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,MAAM;AAAA,QACV,gBAAgB;AAAA,QAChB,UAAU,MAAM,YAAY;AAAA,MAC9B;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AACD,WAAO;AAAA,EACT;AACF;AAEA,MAAM,oBAAmE;AAAA,EACvE,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,OAAO,IAAI,sBAAsB,cAAc,QAAQ;AAC/D,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,QAAQ,MAAM,kBAAkB,GAAG;AACzC,UAAM,WAAW,MAAM,sBAAsB,IAAI,MAAM,sBAAsB,OAAO,IAAI,KAAK,GAAG,CAAC,GAAG,EAAE,UAAU,MAAM,eAAe,gBAAgB,KAAK,CAAC;AAC3J,QAAI,CAAC,SAAU,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,iBAAiB,CAAC;AACvE,4BAAwB,wBAAwB,GAAG,GAAG,SAAS,QAAQ;AACvE,UAAM,OAAO,MAAM,qBAAqB,IAAI,OAAO,EAAE;AACrD,UAAM,SAAS,MAAM,wBAAwB,IAAI;AAAA,MAC/C,UAAU,EAAE,KAAK;AAAA,MACjB,UAAU,OAAO;AAAA,MACjB,UAAU,SAAS,WAAW,OAAO,SAAS,QAAQ,IAAI;AAAA,IAC5D,CAAC;AACD,WAAO,EAAE,QAAQ,qBAAqB,UAAU,MAAM,MAAM,EAAE;AAAA,EAChE;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,QAAQ,OAAO,IAAI,sBAAsB,cAAc,QAAQ;AACvE,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,QAAQ,MAAM,kBAAkB,GAAG;AACzC,UAAM,UAAU,MAAM,sBAAsB,IAAI,MAAM,sBAAsB,OAAO,IAAI,KAAK,GAAG,CAAC,GAAG,EAAE,UAAU,MAAM,eAAe,gBAAgB,KAAK,CAAC;AAC1J,QAAI,CAAC,QAAS,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,iBAAiB,CAAC;AACtE,UAAM,mBAAmB,wBAAwB,GAAG;AACpD,4BAAwB,kBAAkB,QAAQ,QAAQ;AAC1D,QAAI,OAAO,SAAS,QAAW;AAC7B,YAAM,WAAW,OAAO;AACxB,UAAI,aAAa,QAAQ,KAAM,uBAAsB,QAAQ;AAC7D,UAAI,aAAa,QAAQ,MAAM;AAC7B,cAAM,cAAc,MAAM,GAAG,MAAM,UAAU,EAAE,MAAM,SAAS,WAAW,KAAK,CAAC;AAC/E,YAAI,cAAc,GAAG;AACnB,gBAAM,IAAI,cAAc,KAAK,EAAE,OAAO,uDAAuD,CAAC;AAAA,QAChG;AAAA,MACF;AAAA,IACF;AACA,UAAM,oBAAoB,OAAO,aAAa,UAAa,OAAO,aAAa,QAAQ;AACvF,QAAI,mBAAmB;AACrB,UAAI,CAAC,MAAM,cAAc;AACvB,cAAM,IAAI,cAAc,KAAK,EAAE,OAAO,wCAAwC,CAAC;AAAA,MACjF;AACA,YAAM,cAAc,MAAM,GAAG,MAAM,UAAU,EAAE,MAAM,SAAS,WAAW,KAAK,CAAC;AAC/E,UAAI,cAAc,GAAG;AACnB,cAAM,IAAI,cAAc,KAAK,EAAE,OAAO,kEAAkE,CAAC;AAAA,MAC3G;AACA,YAAM,GAAG,aAAa,SAAS,EAAE,MAAM,OAAO,GAAsB,CAAC;AAAA,IACvE;AACA,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,OAAO,MAAM,GAAG,gBAAgB;AAAA,MACpC,QAAQ;AAAA,MACR,OAAO,sBAAsB,OAAO,IAAI,KAAK;AAAA,MAC7C,OAAO,CAAC,WAAW;AACjB,YAAI,OAAO,SAAS,OAAW,QAAO,OAAO,OAAO;AACpD,YAAI,OAAO,aAAa,UAAa,MAAM,aAAc,QAAO,WAAW,OAAO;AAAA,MACpF;AAAA,IACF,CAAC;AACD,QAAI,CAAC,KAAM,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAEnE,UAAM,qBAAqB;AAAA,MACzB,YAAY;AAAA,MACZ,UAAU,EAAE,KAAK;AAAA,MACjB,UAAU,OAAO,KAAK,EAAE;AAAA,MACxB,gBAAgB;AAAA,MAChB,UAAU,KAAK,WAAW,OAAO,KAAK,QAAQ,IAAI;AAAA,MAClD,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO,KAAK,EAAE;AAAA,QAClB,gBAAgB;AAAA,QAChB,UAAU,KAAK,WAAW,OAAO,KAAK,QAAQ,IAAI;AAAA,MACpD;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EACA,cAAc,OAAO,QAAQ,QAAQ,QAAQ;AAC3C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM,wBAAwB,IAAI;AAAA,MAC/C,UAAU,EAAE,KAAK;AAAA,MACjB,UAAU,OAAO,OAAO,EAAE;AAAA,MAC1B,UAAU,OAAO,WAAW,OAAO,OAAO,QAAQ,IAAI;AAAA,IACxD,CAAC;AACD,WAAO,cAAc,QAAQ,MAAM;AAAA,EACrC;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,WAAW,IAAI,MAAM;AAC9C,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,kBAAkB,UAAU;AAClC,UAAM,SAAS,iBAAiB;AAChC,UAAM,aAAa,iBAAiB,QAAQ;AAC5C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,YAAY,MAAM,qBAAqB,IAAI,OAAO,OAAO,EAAE,CAAC;AAClE,UAAM,SAAS,MAAM,wBAAwB,IAAI;AAAA,MAC/C,UAAU,EAAE,KAAK;AAAA,MACjB,UAAU,OAAO,OAAO,EAAE;AAAA,MAC1B,UAAU,OAAO,WAAW,OAAO,OAAO,QAAQ,IAAI;AAAA,IACxD,CAAC;AACD,UAAM,iBAAiB,qBAAqB,QAAQ,WAAW,MAAM;AACrE,UAAM,QAAQ,eAAe;AAC7B,UAAM,UAAU,aAAa,UAAU,MAAM,OAAkC,CAAC,QAAQ,UAAU,CAAC;AACnG,UAAM,aAAa,uBAAuB,QAAQ,QAAQ,MAAM;AAChE,eAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,UAAU,GAAG;AACpD,cAAQ,MAAM,GAAG,EAAE,IAAI;AAAA,IACzB;AACA,WAAO;AAAA,MACL,aAAa,UAAU,2BAA2B,aAAa;AAAA,MAC/D,cAAc;AAAA,MACd,YAAY,OAAO,OAAO,EAAE;AAAA,MAC5B,UAAU,OAAO,WAAW,OAAO,OAAO,QAAQ,IAAI;AAAA,MACtD;AAAA,MACA,gBAAgB,UAAU;AAAA,MAC1B,eAAe;AAAA,MACf,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,QAAQ;AAAA,UACR,OAAO,eAAe;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,OAAO,mBAAoC,QAAQ;AACzD,UAAM,SAAS,MAAM;AACrB,UAAM,QAAQ,MAAM;AACpB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,UAAU,MAAM,GAAG,gBAAgB;AAAA,MACvC,QAAQ;AAAA,MACR,OAAO,EAAE,IAAI,OAAO,IAAI,WAAW,KAAK;AAAA,MACxC,OAAO,CAAC,WAAW;AACjB,eAAO,OAAO,OAAO;AACrB,eAAO,WAAW,OAAO;AAAA,MAC3B;AAAA,IACF,CAAC;AACD,QAAI,SAAS;AACX,YAAM,gBAAgB,IAAI,OAAO,IAAI,OAAO,IAAI;AAAA,IAClD;AACA,UAAM,QAAQ,yBAAyB,OAAO,QAAQ,OAAO,MAAM;AACnE,QAAI,OAAO,KAAK,KAAK,EAAE,QAAQ;AAC7B,YAAM,qBAAqB;AAAA,QACzB,YAAY;AAAA,QACZ,UAAU,EAAE,KAAK;AAAA,QACjB,UAAU,OAAO;AAAA,QACjB,gBAAgB;AAAA,QAChB,UAAU,OAAO;AAAA,QACjB,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AACA,UAAM,wBAAwB;AAAA,MAC5B,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO;AAAA,QACX,gBAAgB;AAAA,QAChB,UAAU,OAAO,YAAY;AAAA,MAC/B;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAEA,MAAM,oBAA+G;AAAA,EACnH,IAAI;AAAA,EACJ,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,KAAK,UAAU,OAAO,kBAAkB;AAC9C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,QAAQ,MAAM,kBAAkB,GAAG;AACzC,UAAM,WAAW,MAAM,sBAAsB,IAAI,MAAM,sBAAsB,IAAI,KAAK,GAAG,CAAC,GAAG,EAAE,UAAU,MAAM,eAAe,gBAAgB,KAAK,CAAC;AACpJ,QAAI,CAAC,SAAU,QAAO,CAAC;AACvB,UAAM,mBAAmB,wBAAwB,GAAG;AACpD,QAAI,kBAAkB;AACpB,YAAM,eAAe,kBAAkB,SAAS,QAAQ,KAAK;AAC7D,UAAI,CAAC,gBAAgB,iBAAiB,iBAAkB,QAAO,CAAC;AAAA,IAClE;AACA,UAAM,OAAO,MAAM,qBAAqB,IAAI,EAAE;AAC9C,UAAM,SAAS,MAAM,wBAAwB,IAAI;AAAA,MAC/C,UAAU,EAAE,KAAK;AAAA,MACjB,UAAU;AAAA,MACV,UAAU,SAAS,WAAW,OAAO,SAAS,QAAQ,IAAI;AAAA,IAC5D,CAAC;AACD,WAAO,EAAE,QAAQ,qBAAqB,UAAU,MAAM,MAAM,EAAE;AAAA,EAChE;AAAA,EACA,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,KAAK,UAAU,OAAO,kBAAkB;AAC9C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,QAAQ,MAAM,kBAAkB,GAAG;AACzC,UAAM,OAAO,MAAM,sBAAsB,IAAI,MAAM,sBAAsB,IAAI,KAAK,GAAG,CAAC,GAAG,EAAE,UAAU,MAAM,eAAe,gBAAgB,KAAK,CAAC;AAChJ,QAAI,CAAC,KAAM,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,iBAAiB,CAAC;AACnE,4BAAwB,wBAAwB,GAAG,GAAG,KAAK,QAAQ;AACnE,UAAM,oBAAoB,MAAM,GAAG,MAAM,UAAU,EAAE,MAAM,WAAW,KAAK,CAAC;AAC5E,QAAI,oBAAoB,EAAG,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,0BAA0B,CAAC;AAE5F,UAAM,GAAG,aAAa,SAAS,EAAE,MAAM,GAAG,CAAC;AAE3C,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,UAAU,MAAM,GAAG,gBAAgB;AAAA,MACvC,QAAQ;AAAA,MACR,OAAO,sBAAsB,IAAI,KAAK;AAAA,MACtC,MAAM;AAAA,IACR,CAAC;AACD,QAAI,CAAC,QAAS,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAEtE,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX;AAAA,QACA,gBAAgB;AAAA,QAChB,UAAU,QAAQ,WAAW,OAAO,QAAQ,QAAQ,IAAI;AAAA,MAC1D;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EACA,UAAU,OAAO,EAAE,WAAW,MAAM,MAAM;AACxC,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,kBAAkB,UAAU;AAClC,UAAM,SAAS,iBAAiB;AAChC,UAAM,aAAa,iBAAiB,QAAQ;AAC5C,UAAM,KAAK,UAAU,OAAO,kBAAkB;AAC9C,WAAO;AAAA,MACL,aAAa,UAAU,2BAA2B,aAAa;AAAA,MAC/D,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,UAAU,QAAQ,YAAY;AAAA,MAC9B,gBAAgB,UAAU;AAAA,MAC1B,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,SAAS,mBAAoC,QAAQ,GAAG;AAC9D,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,QAAI,OAAO,MAAM,sBAAsB,IAAI,MAAM,EAAE,IAAI,OAAO,GAAG,GAAG,CAAC,GAAG,EAAE,UAAU,MAAM,gBAAgB,KAAK,CAAC;AAChH,QAAI,MAAM;AACR,WAAK,YAAY;AACjB,WAAK,OAAO,OAAO;AACnB,WAAK,WAAW,OAAO;AACvB,YAAM,GAAG,MAAM;AAAA,IACjB,OAAO;AACL,aAAO,MAAM,GAAG,gBAAgB;AAAA,QAC9B,QAAQ;AAAA,QACR,MAAM;AAAA,UACJ,IAAI,OAAO;AAAA,UACX,MAAM,OAAO;AAAA,UACb,UAAU,OAAO;AAAA,QACnB;AAAA,MACF,CAAC;AAAA,IACH;AACA,UAAM,gBAAgB,IAAI,OAAO,IAAI,OAAO,IAAI;AAChD,UAAM,QAAQ,yBAAyB,OAAO,QAAQ,MAAS;AAC/D,QAAI,OAAO,KAAK,KAAK,EAAE,QAAQ;AAC7B,YAAM,qBAAqB;AAAA,QACzB,YAAY;AAAA,QACZ,UAAU,EAAE,KAAK;AAAA,QACjB,UAAU,OAAO;AAAA,QACjB,gBAAgB;AAAA,QAChB,UAAU,OAAO,YAAY;AAAA,QAC7B,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AACA,UAAM,wBAAwB;AAAA,MAC5B,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO;AAAA,QACX,gBAAgB;AAAA,QAChB,UAAU,OAAO,YAAY;AAAA,MAC/B;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAEA,gBAAgB,iBAAiB;AACjC,gBAAgB,iBAAiB;AACjC,gBAAgB,iBAAiB;AAEjC,SAAS,cAAc,MAAY,QAAyD;AAC1F,QAAM,UAA0B;AAAA,IAC9B,MAAM,OAAO,KAAK,QAAQ,EAAE;AAAA,IAC5B,UAAU,OAAO,KAAK,QAAQ;AAAA,EAChC;AACA,MAAI,UAAU,OAAO,KAAK,MAAM,EAAE,OAAQ,SAAQ,SAAS;AAC3D,SAAO;AACT;AAEA,SAAS,qBACP,MACA,OAA0B,CAAC,GAC3B,QACe;AACf,SAAO;AAAA,IACL,MAAM,cAAc,MAAM,MAAM;AAAA,IAChC,MAAM;AAAA,MACJ,IAAI,OAAO,KAAK,EAAE;AAAA,MAClB,MAAM,OAAO,KAAK,QAAQ,EAAE;AAAA,MAC5B,UAAU,OAAO,KAAK,QAAQ;AAAA,MAC9B;AAAA,MACA,GAAI,UAAU,OAAO,KAAK,MAAM,EAAE,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,IAC3D;AAAA,EACF;AACF;AAEA,eAAe,qBAAqB,IAAmB,QAA4C;AACjG,QAAM,UAAU,MAAM,mBAAmB,IAAI,SAAS,EAAE,MAAM,OAA0B,GAAG,CAAC,GAAG,EAAE,UAAU,MAAM,gBAAgB,KAAK,CAAC;AACvI,SAAO,QAAQ,IAAI,CAAC,WAAW;AAAA,IAC7B,IAAI,MAAM,KAAK,OAAO,MAAM,EAAE,IAAI;AAAA,IAClC,UAAU,OAAO,MAAM,QAAQ;AAAA,IAC/B,UAAU,MAAM,QAAQ,MAAM,YAAY,IAAI,CAAC,GAAG,MAAM,YAAY,IAAI;AAAA,IACxE,cAAc,QAAQ,MAAM,YAAY;AAAA,IACxC,eAAe,MAAM,QAAQ,MAAM,iBAAiB,IAAI,CAAC,GAAG,MAAM,iBAAiB,IAAI;AAAA,EACzF,EAAE;AACJ;AAEA,eAAe,gBAAgB,IAAmB,QAAgB,MAAyB;AACzF,QAAM,GAAG,aAAa,SAAS,EAAE,MAAM,OAA0B,CAAC;AAClE,MAAI,CAAC,KAAK,QAAQ;AAChB,UAAM,GAAG,MAAM;AACf;AAAA,EACF;AACA,QAAM,UAAU,GAAG,aAAa,MAAM,MAAM;AAC5C,aAAW,OAAO,MAAM;AACtB,UAAM,SAAS,GAAG,OAAO,SAAS;AAAA,MAChC,IAAI,IAAI,MAAM;AAAA,MACd,MAAM;AAAA,MACN,UAAU,IAAI;AAAA,MACd,cAAc,IAAI,YAAY;AAAA,MAC9B,cAAc,IAAI;AAAA,MAClB,mBAAmB,IAAI,iBAAiB;AAAA,MACxC,WAAW,oBAAI,KAAK;AAAA,IACtB,CAAC;AACD,OAAG,QAAQ,MAAM;AAAA,EACnB;AACA,QAAM,GAAG,MAAM;AACjB;",
6
6
  "names": []
7
7
  }
@@ -1,28 +1,56 @@
1
- import { forbidden } from "@open-mercato/shared/lib/crud/errors";
1
+ import { CrudHttpError, forbidden } from "@open-mercato/shared/lib/crud/errors";
2
+ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
2
3
  import { Role } from "@open-mercato/core/modules/auth/data/entities";
3
4
  import { enforceTenantSelection, normalizeTenantId, resolveIsSuperAdmin } from "./tenantAccess.js";
4
- async function enforceRoleTenantAccess(mode, input, ctx) {
5
+ function roleNotFound() {
6
+ throw new CrudHttpError(404, { error: "Role not found" });
7
+ }
8
+ function extractRoleId(input) {
9
+ const payload = input;
10
+ const candidates = [payload.id, payload.body?.id, payload.query?.id];
11
+ for (const candidate of candidates) {
12
+ if (typeof candidate === "string" && candidate.length) return candidate;
13
+ }
14
+ return null;
15
+ }
16
+ async function assertActorOwnsRole(roleId, ctx) {
5
17
  const auth = ctx.auth;
6
18
  if (!auth) throw forbidden("Not authorized");
19
+ const em = ctx.container.resolve("em");
20
+ const existing = await findOneWithDecryption(
21
+ em,
22
+ Role,
23
+ { id: roleId, deletedAt: null },
24
+ {},
25
+ { tenantId: null, organizationId: null }
26
+ );
27
+ if (!existing) return { matched: false };
7
28
  const isSuperAdmin = await resolveIsSuperAdmin(ctx);
29
+ if (isSuperAdmin) return { matched: true };
30
+ const actorTenant = normalizeTenantId(auth.tenantId ?? null) ?? null;
31
+ const existingTenantId = normalizeTenantId(existing.tenantId ?? null) ?? null;
32
+ if (!actorTenant) roleNotFound();
33
+ if (existingTenantId !== actorTenant) roleNotFound();
34
+ return { matched: true };
35
+ }
36
+ async function enforceRoleTenantAccess(mode, input, ctx) {
37
+ const auth = ctx.auth;
38
+ if (!auth) throw forbidden("Not authorized");
8
39
  if (mode === "create") {
9
40
  const tenantId2 = await enforceTenantSelection(ctx, input.tenantId);
10
41
  return { ...input, tenantId: tenantId2 };
11
42
  }
43
+ if (mode === "delete") {
44
+ const roleId2 = extractRoleId(input);
45
+ if (!roleId2) return input;
46
+ await assertActorOwnsRole(roleId2, ctx);
47
+ return input;
48
+ }
12
49
  const roleIdCandidate = input.id;
13
50
  const roleId = typeof roleIdCandidate === "string" ? roleIdCandidate : null;
14
51
  if (!roleId) return input;
15
- const em = ctx.container.resolve("em");
16
- const existing = await em.findOne(Role, { id: roleId, deletedAt: null });
17
- if (!existing) return input;
18
- const actorTenant = normalizeTenantId(auth.tenantId ?? null) ?? null;
19
- const existingTenantId = normalizeTenantId(existing.tenantId ?? null) ?? null;
20
- if (!isSuperAdmin && !actorTenant) {
21
- throw forbidden("Not authorized");
22
- }
23
- if (!isSuperAdmin && existingTenantId !== actorTenant) {
24
- throw forbidden("Not authorized");
25
- }
52
+ const ownership = await assertActorOwnsRole(roleId, ctx);
53
+ if (!ownership.matched) return input;
26
54
  if (input.tenantId === void 0) {
27
55
  return input;
28
56
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/auth/lib/roleTenantGuard.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { forbidden } from '@open-mercato/shared/lib/crud/errors'\nimport { Role } from '@open-mercato/core/modules/auth/data/entities'\nimport { enforceTenantSelection, normalizeTenantId, resolveIsSuperAdmin } from './tenantAccess'\n\ntype RoleTenantAccessCtx = {\n auth: {\n tenantId?: string | null\n sub?: string | null\n orgId?: string | null\n roles?: string[]\n isSuperAdmin?: boolean\n } | null\n container: { resolve<T = unknown>(name: string): T }\n}\n\ntype RoleTenantPayload = {\n id?: unknown\n tenantId?: unknown\n}\n\nexport async function enforceRoleTenantAccess(\n mode: 'create' | 'update',\n input: Record<string, unknown>,\n ctx: RoleTenantAccessCtx,\n): Promise<Record<string, unknown>> {\n const auth = ctx.auth\n if (!auth) throw forbidden('Not authorized')\n const isSuperAdmin = await resolveIsSuperAdmin(ctx)\n\n if (mode === 'create') {\n const tenantId = await enforceTenantSelection(ctx, (input as RoleTenantPayload).tenantId)\n return { ...input, tenantId }\n }\n\n const roleIdCandidate = (input as RoleTenantPayload).id\n const roleId = typeof roleIdCandidate === 'string' ? roleIdCandidate : null\n if (!roleId) return input\n\n const em = (ctx.container.resolve('em') as EntityManager)\n const existing = await em.findOne(Role, { id: roleId, deletedAt: null })\n if (!existing) return input\n\n const actorTenant = normalizeTenantId(auth.tenantId ?? null) ?? null\n const existingTenantId = normalizeTenantId(existing.tenantId ?? null) ?? null\n\n if (!isSuperAdmin && !actorTenant) {\n throw forbidden('Not authorized')\n }\n\n if (!isSuperAdmin && existingTenantId !== actorTenant) {\n throw forbidden('Not authorized')\n }\n\n if ((input as RoleTenantPayload).tenantId === undefined) {\n return input\n }\n\n const tenantId = await enforceTenantSelection(ctx, (input as RoleTenantPayload).tenantId)\n return { ...input, tenantId }\n}\n"],
5
- "mappings": "AACA,SAAS,iBAAiB;AAC1B,SAAS,YAAY;AACrB,SAAS,wBAAwB,mBAAmB,2BAA2B;AAkB/E,eAAsB,wBACpB,MACA,OACA,KACkC;AAClC,QAAM,OAAO,IAAI;AACjB,MAAI,CAAC,KAAM,OAAM,UAAU,gBAAgB;AAC3C,QAAM,eAAe,MAAM,oBAAoB,GAAG;AAElD,MAAI,SAAS,UAAU;AACrB,UAAMA,YAAW,MAAM,uBAAuB,KAAM,MAA4B,QAAQ;AACxF,WAAO,EAAE,GAAG,OAAO,UAAAA,UAAS;AAAA,EAC9B;AAEA,QAAM,kBAAmB,MAA4B;AACrD,QAAM,SAAS,OAAO,oBAAoB,WAAW,kBAAkB;AACvE,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,QAAM,WAAW,MAAM,GAAG,QAAQ,MAAM,EAAE,IAAI,QAAQ,WAAW,KAAK,CAAC;AACvE,MAAI,CAAC,SAAU,QAAO;AAEtB,QAAM,cAAc,kBAAkB,KAAK,YAAY,IAAI,KAAK;AAChE,QAAM,mBAAmB,kBAAkB,SAAS,YAAY,IAAI,KAAK;AAEzE,MAAI,CAAC,gBAAgB,CAAC,aAAa;AACjC,UAAM,UAAU,gBAAgB;AAAA,EAClC;AAEA,MAAI,CAAC,gBAAgB,qBAAqB,aAAa;AACrD,UAAM,UAAU,gBAAgB;AAAA,EAClC;AAEA,MAAK,MAA4B,aAAa,QAAW;AACvD,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,MAAM,uBAAuB,KAAM,MAA4B,QAAQ;AACxF,SAAO,EAAE,GAAG,OAAO,SAAS;AAC9B;",
6
- "names": ["tenantId"]
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError, forbidden } from '@open-mercato/shared/lib/crud/errors'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { Role } from '@open-mercato/core/modules/auth/data/entities'\nimport { enforceTenantSelection, normalizeTenantId, resolveIsSuperAdmin } from './tenantAccess'\n\nfunction roleNotFound(): never {\n throw new CrudHttpError(404, { error: 'Role not found' })\n}\n\ntype RoleTenantAccessCtx = {\n auth: {\n tenantId?: string | null\n sub?: string | null\n orgId?: string | null\n roles?: string[]\n isSuperAdmin?: boolean\n } | null\n container: { resolve<T = unknown>(name: string): T }\n}\n\ntype RoleTenantPayload = {\n id?: unknown\n tenantId?: unknown\n body?: { id?: unknown } | null\n query?: { id?: unknown } | null\n}\n\nfunction extractRoleId(input: Record<string, unknown>): string | null {\n const payload = input as RoleTenantPayload\n const candidates: unknown[] = [payload.id, payload.body?.id, payload.query?.id]\n for (const candidate of candidates) {\n if (typeof candidate === 'string' && candidate.length) return candidate\n }\n return null\n}\n\nasync function assertActorOwnsRole(\n roleId: string,\n ctx: RoleTenantAccessCtx,\n): Promise<{ matched: boolean }> {\n const auth = ctx.auth\n if (!auth) throw forbidden('Not authorized')\n const em = (ctx.container.resolve('em') as EntityManager)\n const existing = await findOneWithDecryption(\n em,\n Role,\n { id: roleId, deletedAt: null },\n {},\n { tenantId: null, organizationId: null },\n )\n if (!existing) return { matched: false }\n\n const isSuperAdmin = await resolveIsSuperAdmin(ctx)\n if (isSuperAdmin) return { matched: true }\n\n const actorTenant = normalizeTenantId(auth.tenantId ?? null) ?? null\n const existingTenantId = normalizeTenantId(existing.tenantId ?? null) ?? null\n // Unified 404 for \"out of scope\" \u2014 matches grantChecks.assertActorCanAccessRoleTarget\n // and prevents existence enumeration of foreign-tenant or global roles.\n if (!actorTenant) roleNotFound()\n if (existingTenantId !== actorTenant) roleNotFound()\n return { matched: true }\n}\n\nexport async function enforceRoleTenantAccess(\n mode: 'create' | 'update' | 'delete',\n input: Record<string, unknown>,\n ctx: RoleTenantAccessCtx,\n): Promise<Record<string, unknown>> {\n const auth = ctx.auth\n if (!auth) throw forbidden('Not authorized')\n\n if (mode === 'create') {\n const tenantId = await enforceTenantSelection(ctx, (input as RoleTenantPayload).tenantId)\n return { ...input, tenantId }\n }\n\n if (mode === 'delete') {\n const roleId = extractRoleId(input)\n if (!roleId) return input\n await assertActorOwnsRole(roleId, ctx)\n return input\n }\n\n // mode === 'update'\n const roleIdCandidate = (input as RoleTenantPayload).id\n const roleId = typeof roleIdCandidate === 'string' ? roleIdCandidate : null\n if (!roleId) return input\n\n const ownership = await assertActorOwnsRole(roleId, ctx)\n if (!ownership.matched) return input\n\n if ((input as RoleTenantPayload).tenantId === undefined) {\n return input\n }\n\n const tenantId = await enforceTenantSelection(ctx, (input as RoleTenantPayload).tenantId)\n return { ...input, tenantId }\n}\n"],
5
+ "mappings": "AACA,SAAS,eAAe,iBAAiB;AACzC,SAAS,6BAA6B;AACtC,SAAS,YAAY;AACrB,SAAS,wBAAwB,mBAAmB,2BAA2B;AAE/E,SAAS,eAAsB;AAC7B,QAAM,IAAI,cAAc,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAC1D;AAoBA,SAAS,cAAc,OAA+C;AACpE,QAAM,UAAU;AAChB,QAAM,aAAwB,CAAC,QAAQ,IAAI,QAAQ,MAAM,IAAI,QAAQ,OAAO,EAAE;AAC9E,aAAW,aAAa,YAAY;AAClC,QAAI,OAAO,cAAc,YAAY,UAAU,OAAQ,QAAO;AAAA,EAChE;AACA,SAAO;AACT;AAEA,eAAe,oBACb,QACA,KAC+B;AAC/B,QAAM,OAAO,IAAI;AACjB,MAAI,CAAC,KAAM,OAAM,UAAU,gBAAgB;AAC3C,QAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,QAAM,WAAW,MAAM;AAAA,IACrB;AAAA,IACA;AAAA,IACA,EAAE,IAAI,QAAQ,WAAW,KAAK;AAAA,IAC9B,CAAC;AAAA,IACD,EAAE,UAAU,MAAM,gBAAgB,KAAK;AAAA,EACzC;AACA,MAAI,CAAC,SAAU,QAAO,EAAE,SAAS,MAAM;AAEvC,QAAM,eAAe,MAAM,oBAAoB,GAAG;AAClD,MAAI,aAAc,QAAO,EAAE,SAAS,KAAK;AAEzC,QAAM,cAAc,kBAAkB,KAAK,YAAY,IAAI,KAAK;AAChE,QAAM,mBAAmB,kBAAkB,SAAS,YAAY,IAAI,KAAK;AAGzE,MAAI,CAAC,YAAa,cAAa;AAC/B,MAAI,qBAAqB,YAAa,cAAa;AACnD,SAAO,EAAE,SAAS,KAAK;AACzB;AAEA,eAAsB,wBACpB,MACA,OACA,KACkC;AAClC,QAAM,OAAO,IAAI;AACjB,MAAI,CAAC,KAAM,OAAM,UAAU,gBAAgB;AAE3C,MAAI,SAAS,UAAU;AACrB,UAAMA,YAAW,MAAM,uBAAuB,KAAM,MAA4B,QAAQ;AACxF,WAAO,EAAE,GAAG,OAAO,UAAAA,UAAS;AAAA,EAC9B;AAEA,MAAI,SAAS,UAAU;AACrB,UAAMC,UAAS,cAAc,KAAK;AAClC,QAAI,CAACA,QAAQ,QAAO;AACpB,UAAM,oBAAoBA,SAAQ,GAAG;AACrC,WAAO;AAAA,EACT;AAGA,QAAM,kBAAmB,MAA4B;AACrD,QAAM,SAAS,OAAO,oBAAoB,WAAW,kBAAkB;AACvE,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,YAAY,MAAM,oBAAoB,QAAQ,GAAG;AACvD,MAAI,CAAC,UAAU,QAAS,QAAO;AAE/B,MAAK,MAA4B,aAAa,QAAW;AACvD,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,MAAM,uBAAuB,KAAM,MAA4B,QAAQ;AACxF,SAAO,EAAE,GAAG,OAAO,SAAS;AAC9B;",
6
+ "names": ["tenantId", "roleId"]
7
7
  }
@@ -1,4 +1,5 @@
1
1
  import { hash } from "bcryptjs";
2
+ import { randomBytes } from "node:crypto";
2
3
  import { Role, RoleAcl, User, UserRole } from "@open-mercato/core/modules/auth/data/entities";
3
4
  import { Tenant, Organization } from "@open-mercato/core/modules/directory/data/entities";
4
5
  import { rebuildHierarchyForTenant } from "@open-mercato/core/modules/directory/lib/hierarchy";
@@ -45,6 +46,15 @@ const DERIVED_EMAIL_ENV = {
45
46
  admin: "OM_INIT_ADMIN_EMAIL",
46
47
  employee: "OM_INIT_EMPLOYEE_EMAIL"
47
48
  };
49
+ function resolveDemoUserEmails() {
50
+ const adminEmail = readEnvValue(DERIVED_EMAIL_ENV.admin) ?? `admin@${DEFAULT_DERIVED_EMAIL_DOMAIN}`;
51
+ const employeeEmail = readEnvValue(DERIVED_EMAIL_ENV.employee) ?? `employee@${DEFAULT_DERIVED_EMAIL_DOMAIN}`;
52
+ return [
53
+ { role: "superadmin", email: DEMO_SUPERADMIN_EMAIL },
54
+ { role: "admin", email: adminEmail },
55
+ { role: "employee", email: employeeEmail }
56
+ ];
57
+ }
48
58
  class OrgSlugExistsError extends Error {
49
59
  constructor(slug) {
50
60
  super(`ORG_SLUG_EXISTS: an organization with slug "${slug}" already exists`);
@@ -52,6 +62,15 @@ class OrgSlugExistsError extends Error {
52
62
  this.name = "OrgSlugExistsError";
53
63
  }
54
64
  }
65
+ class DerivedUserPasswordRequiredError extends Error {
66
+ constructor(missing) {
67
+ super(
68
+ `DERIVED_USER_PASSWORD_REQUIRED: missing ${missing.join(", ")} (set the env vars, pass allowDemoDerivedPasswords: true / --include-demo-users in non-production, or disable derived user seeding)`
69
+ );
70
+ this.missing = missing;
71
+ this.name = "DerivedUserPasswordRequiredError";
72
+ }
73
+ }
55
74
  async function setupInitialTenant(em, options) {
56
75
  const {
57
76
  primaryUser,
@@ -130,12 +149,36 @@ async function setupInitialTenant(em, options) {
130
149
  const employeeOverride = readEnvValue(DERIVED_EMAIL_ENV.employee);
131
150
  const adminEmail = adminOverride ?? `admin@${DEFAULT_DERIVED_EMAIL_DOMAIN}`;
132
151
  const employeeEmail = employeeOverride ?? `employee@${DEFAULT_DERIVED_EMAIL_DOMAIN}`;
133
- const adminPassword = readEnvValue("OM_INIT_ADMIN_PASSWORD") || "secret";
134
- const employeePassword = readEnvValue("OM_INIT_EMPLOYEE_PASSWORD") || "secret";
135
- const adminPasswordHash = adminPassword ? await resolvePasswordHash({ email: adminEmail, password: adminPassword }) : null;
136
- const employeePasswordHash = employeePassword ? await resolvePasswordHash({ email: employeeEmail, password: employeePassword }) : null;
137
- addUniqueBaseUser(baseUsers, { email: adminEmail, roles: ["admin"], passwordHash: adminPasswordHash });
138
- addUniqueBaseUser(baseUsers, { email: employeeEmail, roles: ["employee"], passwordHash: employeePasswordHash });
152
+ const envAdminPwd = readEnvValue("OM_INIT_ADMIN_PASSWORD");
153
+ const envEmployeePwd = readEnvValue("OM_INIT_EMPLOYEE_PASSWORD");
154
+ const isProduction = process.env.NODE_ENV === "production";
155
+ const allowDemo = options.allowDemoDerivedPasswords === true;
156
+ if (isProduction && !allowDemo) {
157
+ const missing = [];
158
+ if (!envAdminPwd) missing.push("OM_INIT_ADMIN_PASSWORD");
159
+ if (!envEmployeePwd) missing.push("OM_INIT_EMPLOYEE_PASSWORD");
160
+ if (missing.length) {
161
+ throw new DerivedUserPasswordRequiredError(missing);
162
+ }
163
+ }
164
+ const fallbackAdminPwd = isProduction ? generateDerivedPassword() : DEMO_DERIVED_PASSWORD;
165
+ const fallbackEmployeePwd = isProduction ? generateDerivedPassword() : DEMO_DERIVED_PASSWORD;
166
+ const adminPasswordPlain = envAdminPwd ?? fallbackAdminPwd;
167
+ const employeePasswordPlain = envEmployeePwd ?? fallbackEmployeePwd;
168
+ const adminPasswordHash = await hash(adminPasswordPlain, 10);
169
+ const employeePasswordHash = await hash(employeePasswordPlain, 10);
170
+ addUniqueBaseUser(baseUsers, {
171
+ email: adminEmail,
172
+ roles: ["admin"],
173
+ passwordHash: adminPasswordHash,
174
+ generatedPassword: envAdminPwd ? null : adminPasswordPlain
175
+ });
176
+ addUniqueBaseUser(baseUsers, {
177
+ email: employeeEmail,
178
+ roles: ["employee"],
179
+ passwordHash: employeePasswordHash,
180
+ generatedPassword: envEmployeePwd ? null : employeePasswordPlain
181
+ });
139
182
  }
140
183
  const passwordHash = await resolvePasswordHash(primaryUser);
141
184
  await em.transactional(async (tem) => {
@@ -233,7 +276,7 @@ async function setupInitialTenant(em, options) {
233
276
  if (base.name) user.name = base.name;
234
277
  if (confirm) user.isConfirmed = true;
235
278
  tem.persist(user);
236
- userSnapshots.push({ user, roles: base.roles, created: false });
279
+ userSnapshots.push({ user, roles: base.roles, created: false, generatedPassword: base.generatedPassword ?? null });
237
280
  } else {
238
281
  user = tem.create(User, {
239
282
  email: encryptedPayload.email ?? base.email,
@@ -246,7 +289,7 @@ async function setupInitialTenant(em, options) {
246
289
  createdAt: /* @__PURE__ */ new Date()
247
290
  });
248
291
  tem.persist(user);
249
- userSnapshots.push({ user, roles: base.roles, created: true });
292
+ userSnapshots.push({ user, roles: base.roles, created: true, generatedPassword: base.generatedPassword ?? null });
250
293
  }
251
294
  await tem.flush();
252
295
  for (const roleName of base.roles) {
@@ -265,7 +308,7 @@ async function setupInitialTenant(em, options) {
265
308
  await rebuildHierarchyForTenant(em, tenantId);
266
309
  }
267
310
  await ensureDefaultRoleAcls(em, tenantId, resolvedModules, { includeSuperadminRole });
268
- await deactivateDemoSuperAdminIfSelfOnboardingEnabled(em);
311
+ await deactivateDemoUsersIfSelfOnboardingEnabled(em);
269
312
  for (const mod of resolvedModules) {
270
313
  if (mod.setup?.onTenantCreated) {
271
314
  await mod.setup.onTenantCreated({ em, tenantId, organizationId });
@@ -296,6 +339,10 @@ function addUniqueBaseUser(baseUsers, entry) {
296
339
  if (baseUsers.some((user) => user.email.toLowerCase() === normalized)) return;
297
340
  baseUsers.push(entry);
298
341
  }
342
+ const DEMO_DERIVED_PASSWORD = "secret";
343
+ function generateDerivedPassword() {
344
+ return randomBytes(12).toString("base64url");
345
+ }
299
346
  function isDemoModeEnabled() {
300
347
  const parsed = parseBooleanToken(process.env.DEMO_MODE ?? "");
301
348
  return parsed === false ? false : true;
@@ -410,28 +457,40 @@ async function ensureRoleAclFor(em, role, tenantId, features, options = {}) {
410
457
  await em.persist(existing).flush();
411
458
  }
412
459
  }
413
- async function deactivateDemoSuperAdminIfSelfOnboardingEnabled(em) {
460
+ async function deactivateDemoUsersIfSelfOnboardingEnabled(em) {
414
461
  if (process.env.SELF_SERVICE_ONBOARDING_ENABLED !== "true") return;
415
462
  if (shouldKeepDemoSuperadminDuringInit()) return;
416
- try {
417
- const user = await findOneWithDecryption(em, User, { email: DEMO_SUPERADMIN_EMAIL }, {}, { tenantId: null, organizationId: null });
418
- if (!user) return;
419
- let dirty = false;
420
- if (user.passwordHash) {
421
- user.passwordHash = null;
422
- dirty = true;
423
- }
424
- if (user.isConfirmed !== false) {
425
- user.isConfirmed = false;
426
- dirty = true;
427
- }
428
- if (dirty) {
429
- await em.persist(user).flush();
463
+ for (const { role, email } of resolveDemoUserEmails()) {
464
+ try {
465
+ const user = await findOneWithDecryption(
466
+ em,
467
+ User,
468
+ { email },
469
+ {},
470
+ { tenantId: null, organizationId: null }
471
+ );
472
+ if (!user) continue;
473
+ let dirty = false;
474
+ if (user.passwordHash) {
475
+ user.passwordHash = null;
476
+ dirty = true;
477
+ }
478
+ if (user.isConfirmed !== false) {
479
+ user.isConfirmed = false;
480
+ dirty = true;
481
+ }
482
+ if (dirty) {
483
+ await em.persist(user).flush();
484
+ }
485
+ } catch (error) {
486
+ console.error(
487
+ `[auth.setup] failed to deactivate demo ${role} user (${email})`,
488
+ error
489
+ );
430
490
  }
431
- } catch (error) {
432
- console.error("[auth.setup] failed to deactivate demo superadmin user", error);
433
491
  }
434
492
  }
493
+ const deactivateDemoSuperAdminIfSelfOnboardingEnabled = deactivateDemoUsersIfSelfOnboardingEnabled;
435
494
  function tryGetModules() {
436
495
  try {
437
496
  const { getModules } = require("@open-mercato/shared/lib/modules/registry");
@@ -441,10 +500,13 @@ function tryGetModules() {
441
500
  }
442
501
  }
443
502
  export {
503
+ DerivedUserPasswordRequiredError,
444
504
  OrgSlugExistsError,
505
+ deactivateDemoUsersIfSelfOnboardingEnabled,
445
506
  ensureCustomRoleAcls,
446
507
  ensureDefaultRoleAcls,
447
508
  ensureRoles,
509
+ resolveDemoUserEmails,
448
510
  setupInitialTenant
449
511
  };
450
512
  //# sourceMappingURL=setup-app.js.map