42 lines
1.2 KiB
TypeScript
42 lines
1.2 KiB
TypeScript
import type { Db } from "@paperclipai/db";
|
|
import { forbidden, unprocessable } from "../errors.js";
|
|
import { accessService } from "./access.js";
|
|
import { authorizationDeniedDetails, type AuthorizationActor } from "./authorization.js";
|
|
|
|
type ResolvableSecretProposal = {
|
|
kind: string;
|
|
targetId: string | null;
|
|
};
|
|
|
|
export async function assertCanResolveProposal(input: {
|
|
db: Db;
|
|
actor: AuthorizationActor;
|
|
companyId: string;
|
|
proposal: ResolvableSecretProposal;
|
|
assertSecretDefinitionAdmin?: () => void;
|
|
}) {
|
|
if (input.proposal.kind === "secret") {
|
|
if (!input.assertSecretDefinitionAdmin) {
|
|
throw forbidden("Company admin access required");
|
|
}
|
|
input.assertSecretDefinitionAdmin();
|
|
return;
|
|
}
|
|
if (input.proposal.kind !== "binding" || !input.proposal.targetId) {
|
|
throw unprocessable("Binding proposal target is missing");
|
|
}
|
|
const decision = await accessService(input.db).decide({
|
|
actor: input.actor,
|
|
action: "agent_config:update",
|
|
resource: {
|
|
type: "agent",
|
|
companyId: input.companyId,
|
|
agentId: input.proposal.targetId,
|
|
},
|
|
scope: { requiresChangeGrant: true },
|
|
});
|
|
if (!decision.allowed) {
|
|
throw forbidden(decision.explanation, authorizationDeniedDetails(decision));
|
|
}
|
|
}
|