Show all companies' agents on instance heartbeats page

The /instance/scheduler-heartbeats endpoint was filtering agents by the
requesting user's company memberships, which meant non-member companies
(like donchitos) were hidden. Since this is an instance-level admin page,
it should show all agents across all companies.

- Added assertInstanceAdmin to authz.ts for reuse
- Replaced assertBoard + company filter with assertInstanceAdmin
- Removed the companyIds-based WHERE clause since instance admins see all

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
dotta 2026-03-23 15:57:57 -05:00
parent 1adfd30b3b
commit 3b2cb3a699
2 changed files with 10 additions and 13 deletions

View file

@ -43,7 +43,7 @@ import {
workspaceOperationService, workspaceOperationService,
} from "../services/index.js"; } from "../services/index.js";
import { conflict, forbidden, notFound, unprocessable } from "../errors.js"; import { conflict, forbidden, notFound, unprocessable } from "../errors.js";
import { assertBoard, assertCompanyAccess, getActorInfo } from "./authz.js"; import { assertBoard, assertCompanyAccess, assertInstanceAdmin, getActorInfo } from "./authz.js";
import { findServerAdapter, listAdapterModels } from "../adapters/index.js"; import { findServerAdapter, listAdapterModels } from "../adapters/index.js";
import { redactEventPayload } from "../redaction.js"; import { redactEventPayload } from "../redaction.js";
import { redactCurrentUserValue } from "../log-redaction.js"; import { redactCurrentUserValue } from "../log-redaction.js";
@ -855,17 +855,7 @@ export function agentRoutes(db: Db) {
}); });
router.get("/instance/scheduler-heartbeats", async (req, res) => { router.get("/instance/scheduler-heartbeats", async (req, res) => {
assertBoard(req); assertInstanceAdmin(req);
const accessConditions = [];
if (req.actor.source !== "local_implicit" && !req.actor.isInstanceAdmin) {
const allowedCompanyIds = req.actor.companyIds ?? [];
if (allowedCompanyIds.length === 0) {
res.json([]);
return;
}
accessConditions.push(inArray(agentsTable.companyId, allowedCompanyIds));
}
const rows = await db const rows = await db
.select({ .select({
@ -883,7 +873,6 @@ export function agentRoutes(db: Db) {
}) })
.from(agentsTable) .from(agentsTable)
.innerJoin(companies, eq(agentsTable.companyId, companies.id)) .innerJoin(companies, eq(agentsTable.companyId, companies.id))
.where(accessConditions.length > 0 ? and(...accessConditions) : undefined)
.orderBy(companies.name, agentsTable.name); .orderBy(companies.name, agentsTable.name);
const items: InstanceSchedulerHeartbeatAgent[] = rows const items: InstanceSchedulerHeartbeatAgent[] = rows

View file

@ -7,6 +7,14 @@ export function assertBoard(req: Request) {
} }
} }
export function assertInstanceAdmin(req: Request) {
assertBoard(req);
if (req.actor.source === "local_implicit" || req.actor.isInstanceAdmin) {
return;
}
throw forbidden("Instance admin access required");
}
export function assertCompanyAccess(req: Request, companyId: string) { export function assertCompanyAccess(req: Request, companyId: string) {
if (req.actor.type === "none") { if (req.actor.type === "none") {
throw unauthorized(); throw unauthorized();