fix: lists decide every org through the permission system; no system-org shortcut

This commit is contained in:
2026-11-02 10:43:29 +01:00
parent 12964793cf
commit 0f7db7e99e
12 changed files with 98 additions and 31 deletions
+26
View File
@@ -75,6 +75,32 @@ export function orgScopeExpr(orgIds: string[], column: string, paramOffset = 0):
return { expr: `${column} IN (${parts.join(' UNION ')})`, params: orgIds };
}
/**
* The filter a list applies to `column`: the orgs it answers for are those under
* the orgs of `scope` (every org when the request named none), kept only where
* the caller holds (activity, target) once the permission hooks have run. A row
* with no org shows only on an unpinned list, which `permittedScope` let through
* on the no-org perimeter.
*/
export async function listScope(
db: Pool,
req: FastifyRequest,
scope: string[],
activity: string,
target: string,
column: string,
paramOffset = 0,
): Promise<{ expr: string; params: [string[]] }> {
const user = getUser(req);
const { rows } = scope.length
? await db.query<{ id: string }>(
"SELECT DISTINCT s.org_id AS id FROM unnest($1::UUID[]) o, morbac.get_org_scope(o, 'subtree') s", [scope])
: await db.query<{ id: string }>('SELECT id FROM morbac.orgs');
const orgs = user ? await permittedOrgs(db, user.id, rows.map((r) => r.id), activity, target, req) : [];
const own = `${column} = ANY($${paramOffset + 1}::UUID[])`;
return { expr: scope.length ? own : `(${own} OR ${column} IS NULL)`, params: [orgs] };
}
export function viewForType(type: 'user' | 'service'): string {
return type === 'service' ? 'service_users' : 'users';
}