mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 19:00:55 +00:00
Merge branch 'dev' into freight/nati-2
This commit is contained in:
@@ -35,10 +35,57 @@ export class FreightMeService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Permissions granted to the position's TYPE (`iam.position_type_permissions`).
|
||||
* A position type is the platform's notion of a role, and admin-created
|
||||
* positions carry their grants there rather than on the position itself — but
|
||||
* the JWT only ever snapshots direct position permissions. Without this, staff
|
||||
* on such a position resolve to zero permissions and every permission-gated
|
||||
* route rejects them (this is what locked GL officers out of their clearance
|
||||
* detail pages). Resolved live from IAM, same as the position type above.
|
||||
*/
|
||||
private async lookupPositionTypePermissions(
|
||||
positionId: string | undefined,
|
||||
): Promise<string[]> {
|
||||
if (!positionId) return [];
|
||||
try {
|
||||
const rows: { key: string }[] = await this.dataSource.query(
|
||||
`SELECT DISTINCT perm.key
|
||||
FROM iam.positions p
|
||||
JOIN iam.position_type_permissions ptp
|
||||
ON ptp.position_type_id = p.position_type_id
|
||||
JOIN iam.permissions perm ON perm.id = ptp.permission_id
|
||||
WHERE p.id = $1`,
|
||||
[positionId],
|
||||
);
|
||||
return rows.map((r) => r.key).filter(Boolean);
|
||||
} catch {
|
||||
return []; // iam schema unreachable — degrade to position-only permissions
|
||||
}
|
||||
}
|
||||
|
||||
async getEnrichedProfile(user: TCurrentUser) {
|
||||
const positionType = await this.lookupPositionType(
|
||||
user.employee?.position?.id,
|
||||
const positionId = user.employee?.position?.id;
|
||||
const [positionType, positionTypePermissionKeys] = await Promise.all([
|
||||
this.lookupPositionType(positionId),
|
||||
this.lookupPositionTypePermissions(positionId),
|
||||
]);
|
||||
|
||||
// Merge the type-level grants into the position's own permission list so
|
||||
// BOTH consumers see them: `collectPermissionKeys` below, and the
|
||||
// backoffice's `getPermissionKeys`, which walks this same nested array.
|
||||
const positionPermissions = [
|
||||
...(user.employee?.position?.permissions ?? []),
|
||||
];
|
||||
const seenPermissionKeys = new Set(
|
||||
positionPermissions.map((p) => p?.key).filter(Boolean),
|
||||
);
|
||||
for (const key of positionTypePermissionKeys) {
|
||||
if (!seenPermissionKeys.has(key)) {
|
||||
seenPermissionKeys.add(key);
|
||||
positionPermissions.push({ key } as (typeof positionPermissions)[number]);
|
||||
}
|
||||
}
|
||||
|
||||
const employee = user.employee
|
||||
? [
|
||||
@@ -56,7 +103,7 @@ export class FreightMeService {
|
||||
name: user.employee.position.name,
|
||||
isDelegate: user.employee.position.isDelegate,
|
||||
parentPositionId: user.employee.position.parentPositionId,
|
||||
permissions: user.employee.position.permissions ?? [],
|
||||
permissions: positionPermissions,
|
||||
positionType,
|
||||
},
|
||||
]
|
||||
@@ -65,7 +112,15 @@ export class FreightMeService {
|
||||
]
|
||||
: [];
|
||||
|
||||
const permissionKeys = collectPermissionKeys(user);
|
||||
// `collectPermissionKeys` reads the raw token (position-level only), so
|
||||
// union the type-level grants in — the backoffice prefers this flat list
|
||||
// over the nested array and would otherwise still see none of them.
|
||||
const permissionKeys = [
|
||||
...new Set([
|
||||
...collectPermissionKeys(user),
|
||||
...positionTypePermissionKeys,
|
||||
]),
|
||||
];
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
|
||||
Reference in New Issue
Block a user