feat: unify device auth + pairing
This commit is contained in:
@@ -6,6 +6,7 @@ import { chatHandlers } from "./server-methods/chat.js";
|
||||
import { configHandlers } from "./server-methods/config.js";
|
||||
import { connectHandlers } from "./server-methods/connect.js";
|
||||
import { cronHandlers } from "./server-methods/cron.js";
|
||||
import { deviceHandlers } from "./server-methods/devices.js";
|
||||
import { execApprovalsHandlers } from "./server-methods/exec-approvals.js";
|
||||
import { healthHandlers } from "./server-methods/health.js";
|
||||
import { logsHandlers } from "./server-methods/logs.js";
|
||||
@@ -23,6 +24,43 @@ import { voicewakeHandlers } from "./server-methods/voicewake.js";
|
||||
import { webHandlers } from "./server-methods/web.js";
|
||||
import { wizardHandlers } from "./server-methods/wizard.js";
|
||||
|
||||
const ADMIN_SCOPE = "operator.admin";
|
||||
const APPROVALS_SCOPE = "operator.approvals";
|
||||
const PAIRING_SCOPE = "operator.pairing";
|
||||
|
||||
const APPROVAL_METHODS = new Set(["exec.approval.request", "exec.approval.resolve"]);
|
||||
const PAIRING_METHODS = new Set([
|
||||
"node.pair.request",
|
||||
"node.pair.list",
|
||||
"node.pair.approve",
|
||||
"node.pair.reject",
|
||||
"node.pair.verify",
|
||||
"device.pair.list",
|
||||
"device.pair.approve",
|
||||
"device.pair.reject",
|
||||
]);
|
||||
const ADMIN_METHOD_PREFIXES = ["exec.approvals."];
|
||||
|
||||
function authorizeGatewayMethod(method: string, client: GatewayRequestOptions["client"]) {
|
||||
if (!client?.connect) return null;
|
||||
const role = client.connect.role ?? "operator";
|
||||
const scopes = client.connect.scopes ?? [];
|
||||
if (role !== "operator") {
|
||||
return errorShape(ErrorCodes.INVALID_REQUEST, `unauthorized role: ${role}`);
|
||||
}
|
||||
if (scopes.includes(ADMIN_SCOPE)) return null;
|
||||
if (APPROVAL_METHODS.has(method) && !scopes.includes(APPROVALS_SCOPE)) {
|
||||
return errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.approvals");
|
||||
}
|
||||
if (PAIRING_METHODS.has(method) && !scopes.includes(PAIRING_SCOPE)) {
|
||||
return errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.pairing");
|
||||
}
|
||||
if (ADMIN_METHOD_PREFIXES.some((prefix) => method.startsWith(prefix))) {
|
||||
return errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.admin");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export const coreGatewayHandlers: GatewayRequestHandlers = {
|
||||
...connectHandlers,
|
||||
...logsHandlers,
|
||||
@@ -31,6 +69,7 @@ export const coreGatewayHandlers: GatewayRequestHandlers = {
|
||||
...channelsHandlers,
|
||||
...chatHandlers,
|
||||
...cronHandlers,
|
||||
...deviceHandlers,
|
||||
...execApprovalsHandlers,
|
||||
...webHandlers,
|
||||
...modelsHandlers,
|
||||
@@ -52,6 +91,11 @@ export async function handleGatewayRequest(
|
||||
opts: GatewayRequestOptions & { extraHandlers?: GatewayRequestHandlers },
|
||||
): Promise<void> {
|
||||
const { req, respond, client, isWebchatConnect, context } = opts;
|
||||
const authError = authorizeGatewayMethod(req.method, client);
|
||||
if (authError) {
|
||||
respond(false, undefined, authError);
|
||||
return;
|
||||
}
|
||||
const handler = opts.extraHandlers?.[req.method] ?? coreGatewayHandlers[req.method];
|
||||
if (!handler) {
|
||||
respond(
|
||||
|
||||
Reference in New Issue
Block a user