@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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/generated/entities/api_key/index.js +2 -0
- package/dist/generated/entities/api_key/index.js.map +2 -2
- package/dist/generated/entities/attachment_partition/index.js +4 -0
- package/dist/generated/entities/attachment_partition/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +3 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/api_keys/data/entities.js +9 -2
- package/dist/modules/api_keys/data/entities.js.map +2 -2
- package/dist/modules/api_keys/migrations/Migration20260523234901.js +17 -0
- package/dist/modules/api_keys/migrations/Migration20260523234901.js.map +7 -0
- package/dist/modules/api_keys/services/apiKeyService.js +26 -0
- package/dist/modules/api_keys/services/apiKeyService.js.map +2 -2
- package/dist/modules/attachments/api/partitions/route.js +54 -12
- package/dist/modules/attachments/api/partitions/route.js.map +2 -2
- package/dist/modules/attachments/data/entities.js +7 -0
- package/dist/modules/attachments/data/entities.js.map +2 -2
- package/dist/modules/attachments/migrations/Migration20260524000000.js +27 -0
- package/dist/modules/attachments/migrations/Migration20260524000000.js.map +7 -0
- package/dist/modules/auth/api/roles/route.js +6 -35
- package/dist/modules/auth/api/roles/route.js.map +2 -2
- package/dist/modules/auth/cli.js +26 -4
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/auth/commands/roles.js +34 -11
- package/dist/modules/auth/commands/roles.js.map +2 -2
- package/dist/modules/auth/lib/roleTenantGuard.js +41 -13
- package/dist/modules/auth/lib/roleTenantGuard.js.map +3 -3
- package/dist/modules/auth/lib/setup-app.js +88 -26
- package/dist/modules/auth/lib/setup-app.js.map +2 -2
- package/dist/modules/currencies/api/fetch-configs/route.js +4 -2
- package/dist/modules/currencies/api/fetch-configs/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/route.js +5 -1
- package/dist/modules/integrations/api/[id]/route.js.map +2 -2
- package/dist/modules/integrations/api/route.js +5 -1
- package/dist/modules/integrations/api/route.js.map +2 -2
- package/dist/modules/integrations/lib/credentials-service.js +6 -4
- package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
- package/generated/entities/api_key/index.ts +1 -0
- package/generated/entities/attachment_partition/index.ts +2 -0
- package/generated/entity-fields-registry.ts +3 -0
- package/package.json +7 -7
- package/src/modules/api_keys/data/entities.ts +15 -1
- package/src/modules/api_keys/migrations/.snapshot-open-mercato.json +18 -0
- package/src/modules/api_keys/migrations/Migration20260523234901.ts +17 -0
- package/src/modules/api_keys/services/apiKeyService.ts +63 -0
- package/src/modules/attachments/api/partitions/route.ts +58 -11
- package/src/modules/attachments/data/entities.ts +7 -0
- package/src/modules/attachments/migrations/.snapshot-open-mercato.json +28 -0
- package/src/modules/attachments/migrations/Migration20260524000000.ts +25 -0
- package/src/modules/auth/api/roles/route.ts +7 -38
- package/src/modules/auth/cli.ts +27 -4
- package/src/modules/auth/commands/roles.ts +39 -11
- package/src/modules/auth/lib/roleTenantGuard.ts +56 -17
- package/src/modules/auth/lib/setup-app.ts +160 -32
- package/src/modules/currencies/api/fetch-configs/route.ts +4 -2
- package/src/modules/integrations/api/[id]/route.ts +5 -2
- package/src/modules/integrations/api/route.ts +5 -2
- 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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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": "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",
|
|
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
|
-
|
|
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
|
|
16
|
-
|
|
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}
|
|
5
|
-
"mappings": "AACA,SAAS,iBAAiB;
|
|
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
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
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
|
|
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
|
|
460
|
+
async function deactivateDemoUsersIfSelfOnboardingEnabled(em) {
|
|
414
461
|
if (process.env.SELF_SERVICE_ONBOARDING_ENABLED !== "true") return;
|
|
415
462
|
if (shouldKeepDemoSuperadminDuringInit()) return;
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
user
|
|
426
|
-
dirty =
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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
|