openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
171 lines (170 loc) • 9.16 kB
JavaScript
import { n as GATEWAY_CLIENT_IDS, r as GATEWAY_CLIENT_MODES } from "./client-info-B1bPgeKr.js";
import "./version-CwNT1gaY.js";
import { l as withPluginRuntimeGatewayRequestScope } from "./gateway-request-scope-BCMYlsDI.js";
import { n as findRegisteredPluginHttpRoute, o as isProtectedPluginRoutePathFromContext, r as isRegisteredPluginHttpRoutePath, s as resolvePluginRoutePathContext, t as findMatchingPluginHttpRoutes } from "./route-match-BkI3BCZw.js";
import { x as respondControlUiPluginAuthCookieProbe } from "./http-auth-utils-C3lb4QXY.js";
import { n as finishFailedGatewayHttpResponse } from "./http-common-BaZaosnr.js";
import { a as runWithGatewayUpgradeWorkAdmission, i as runWithGatewayHttpWorkAdmission, n as matchedPluginRoutesRequireGatewayAuth, r as shouldEnforceGatewayAuthForPluginPath, t as isPluginAuthenticatedRoutePath } from "./route-auth-Bp_S_Wob.js";
import { t as resolvePluginRouteRuntimeOperatorScopes } from "./plugin-route-runtime-scopes-DIDQKBJn.js";
//#region src/gateway/server/plugins-http.ts
function resolvePluginRoutePathContextForRequest(req, providedPathContext) {
if (providedPathContext) return providedPathContext;
const url = new URL(req.url ?? "/", "http://localhost");
return resolvePluginRoutePathContext(url.pathname);
}
function createPluginRouteRuntimeClient(scopes, clientIp, requestAuth) {
const authenticatedUserProfile = requestAuth?.authenticatedUserProfile;
const operatorRoleActor = requestAuth?.operatorRoleActor;
return {
connId: `plugin-http:${clientIp ?? "unknown"}`,
...clientIp ? { clientIp } : {},
...authenticatedUserProfile ? { authenticatedUserProfile } : {},
...operatorRoleActor ? { internal: { operatorRoleActor } } : {},
connect: {
minProtocol: 4,
maxProtocol: 4,
client: {
id: GATEWAY_CLIENT_IDS.GATEWAY_CLIENT,
version: "internal",
platform: "node",
mode: GATEWAY_CLIENT_MODES.BACKEND
},
role: "operator",
scopes: [...scopes]
}
};
}
function writeUpgradeUnauthorized(socket) {
socket.write("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
socket.destroy();
}
function getMissingPluginRouteRuntimeContext(route, context) {
if (route.auth !== "gateway") return;
if (route.gatewayRuntimeScopeSurface === "trusted-operator") return context.gatewayRequestAuth ? void 0 : "caller auth context";
return context.gatewayRequestOperatorScopes === void 0 ? "caller scope context" : void 0;
}
function canRunPluginHttpRouteWithoutAdmission(route) {
return route.auth === "gateway" && route.gatewayRuntimeScopeSurface === "trusted-operator" && route.gatewayMethodDispatchAllowed === true;
}
function createPluginRouteRuntimeScope(params) {
const runtimeClient = createPluginRouteRuntimeClient(params.route.auth !== "gateway" ? [] : params.gatewayRequestAuth?.controlUiPluginGrant ? params.gatewayRequestOperatorScopes : params.route.gatewayRuntimeScopeSurface === "trusted-operator" ? resolvePluginRouteRuntimeOperatorScopes(params.req, params.gatewayRequestAuth, "trusted-operator") : params.gatewayRequestOperatorScopes, params.gatewayRequestClientIp, params.route.auth === "gateway" ? params.gatewayRequestAuth : void 0);
return {
pluginRegistry: params.registry,
...params.gatewayRequestContext ? { context: params.gatewayRequestContext } : {},
client: runtimeClient,
isWebchatConnect: () => false,
...params.route.pluginId ? { pluginId: params.route.pluginId } : {},
...params.route.source ? { pluginSource: params.route.source } : {},
...params.route.gatewayMethodDispatchAllowed === true ? { gatewayMethodDispatchAllowed: true } : {}
};
}
function createGatewayPluginRequestHandler(params) {
const { log } = params;
return async (req, res, providedPathContext, dispatchContext) => {
const registry = params.getRouteRegistry?.() ?? params.registry;
const gatewayRequestContext = params.getGatewayRequestContext?.();
if ((registry.httpRoutes ?? []).length === 0) return false;
const pathContext = resolvePluginRoutePathContextForRequest(req, providedPathContext);
const matchedRoutes = findMatchingPluginHttpRoutes(registry, pathContext);
if (matchedRoutes.length === 0) return false;
if (matchedPluginRoutesRequireGatewayAuth(matchedRoutes) && dispatchContext?.gatewayAuthSatisfied !== true) {
log.warn(`plugin http route blocked without gateway auth (${pathContext.canonicalPath})`);
return false;
}
const firstGatewayRoute = matchedRoutes.find((route) => route.auth === "gateway");
const presentedGatewayRequestAuth = dispatchContext?.gatewayRequestAuth;
const presentedControlUiPluginGrants = presentedGatewayRequestAuth?.controlUiPluginGrants;
const controlUiPluginGrant = presentedControlUiPluginGrants?.find((grant) => grant.pluginId === firstGatewayRoute?.pluginId);
if (presentedControlUiPluginGrants && (!firstGatewayRoute || !controlUiPluginGrant)) {
log.warn(`plugin http route blocked for mismatched control ui grant (${pathContext.canonicalPath})`);
res.statusCode = 401;
res.setHeader("Content-Type", "text/plain; charset=utf-8");
res.end("Unauthorized");
return true;
}
const gatewayRequestAuth = controlUiPluginGrant ? {
...presentedGatewayRequestAuth,
controlUiPluginGrant
} : presentedGatewayRequestAuth;
const gatewayRequestOperatorScopes = controlUiPluginGrant ? controlUiPluginGrant.scopes : dispatchContext?.gatewayRequestOperatorScopes;
for (const route of matchedRoutes) {
if (controlUiPluginGrant && route.auth === "gateway" && route.pluginId !== controlUiPluginGrant.pluginId) continue;
const missingRuntimeContext = getMissingPluginRouteRuntimeContext(route, {
gatewayRequestAuth,
gatewayRequestOperatorScopes
});
if (missingRuntimeContext) {
log.warn(`plugin http route blocked without ${missingRuntimeContext} (${pathContext.canonicalPath})`);
return false;
}
}
if (controlUiPluginGrant && respondControlUiPluginAuthCookieProbe(req, res)) return true;
for (const route of matchedRoutes) {
if (controlUiPluginGrant && route.auth === "gateway" && route.pluginId !== controlUiPluginGrant.pluginId) continue;
try {
const runRoute = async () => await withPluginRuntimeGatewayRequestScope(createPluginRouteRuntimeScope({
registry,
route,
req,
gatewayRequestContext,
gatewayRequestAuth,
gatewayRequestOperatorScopes,
gatewayRequestClientIp: dispatchContext?.gatewayRequestClientIp
}), async () => route.handler(req, res)) !== false;
if (canRunPluginHttpRouteWithoutAdmission(route) ? await runRoute() : await runWithGatewayHttpWorkAdmission(res, runRoute)) return true;
} catch (err) {
log.warn(`plugin http route failed (${route.pluginId ?? "unknown"}): ${String(err)}`);
finishFailedGatewayHttpResponse(res);
return true;
}
}
return false;
};
}
function createGatewayPluginUpgradeHandler(params) {
const { log } = params;
return async (req, socket, head, providedPathContext, dispatchContext) => {
const registry = params.getRouteRegistry?.() ?? params.registry;
const gatewayRequestContext = params.getGatewayRequestContext?.();
if ((registry.httpRoutes ?? []).length === 0) return false;
const pathContext = resolvePluginRoutePathContextForRequest(req, providedPathContext);
const matchedRoutes = findMatchingPluginHttpRoutes(registry, pathContext).filter((route) => typeof route.handleUpgrade === "function");
if (matchedRoutes.length === 0) return false;
if (matchedPluginRoutesRequireGatewayAuth(matchedRoutes) && dispatchContext?.gatewayAuthSatisfied !== true) {
log.warn(`plugin http upgrade blocked without gateway auth (${pathContext.canonicalPath})`);
writeUpgradeUnauthorized(socket);
return true;
}
const gatewayRequestAuth = dispatchContext?.gatewayRequestAuth;
const gatewayRequestOperatorScopes = dispatchContext?.gatewayRequestOperatorScopes;
for (const route of matchedRoutes) {
const missingRuntimeContext = getMissingPluginRouteRuntimeContext(route, {
gatewayRequestAuth,
gatewayRequestOperatorScopes
});
if (missingRuntimeContext) {
log.warn(`plugin http upgrade blocked without ${missingRuntimeContext} (${pathContext.canonicalPath})`);
writeUpgradeUnauthorized(socket);
return true;
}
}
for (const route of matchedRoutes) try {
if (await runWithGatewayUpgradeWorkAdmission(socket, async () => await withPluginRuntimeGatewayRequestScope(createPluginRouteRuntimeScope({
registry,
route,
req,
gatewayRequestContext,
gatewayRequestAuth,
gatewayRequestOperatorScopes,
gatewayRequestClientIp: dispatchContext?.gatewayRequestClientIp
}), async () => route.handleUpgrade?.(req, socket, head)) !== false)) return true;
} catch (err) {
log.warn(`plugin http upgrade failed (${route.pluginId ?? "unknown"}): ${String(err)}`);
socket.destroy();
return true;
}
return false;
};
}
//#endregion
export { createGatewayPluginRequestHandler, createGatewayPluginUpgradeHandler, findRegisteredPluginHttpRoute, isPluginAuthenticatedRoutePath, isProtectedPluginRoutePathFromContext, isRegisteredPluginHttpRoutePath, resolvePluginRoutePathContext, shouldEnforceGatewayAuthForPluginPath };