File size: 2,039 Bytes
5cb63c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// Plugin route runtime scopes map authenticated HTTP callers to operator scopes exposed inside plugin handlers.
import type { IncomingMessage } from "node:http";
import { roleScopesAllow } from "../../shared/operator-scope-compat.js";
import {
  applyHttpOperatorRoleScopeCeiling,
  getHeader,
  resolveTrustedHttpOperatorScopes,
  type AuthorizedGatewayHttpRequest,
} from "../http-auth-utils.js";
import { CLI_DEFAULT_OPERATOR_SCOPES, WRITE_SCOPE } from "../method-scopes.js";

/**
 * Runtime operator-scope resolver for plugin HTTP route requests.
 */
export type PluginRouteRuntimeScopeSurface = "write-default" | "trusted-operator";

/** Resolves the scopes a plugin route receives after gateway HTTP authentication. */
export function resolvePluginRouteRuntimeOperatorScopes(
  req: IncomingMessage,
  requestAuth: AuthorizedGatewayHttpRequest,
  surface: PluginRouteRuntimeScopeSurface = "write-default",
): string[] {
  if (requestAuth.authMethod === "device-token") {
    const deviceScopes = applyHttpOperatorRoleScopeCeiling(
      requestAuth.deviceOperatorScopes ?? [],
      requestAuth,
    );
    return surface === "trusted-operator"
      ? deviceScopes
      : [WRITE_SCOPE].filter((scope) =>
          roleScopesAllow({
            role: "operator",
            requestedScopes: [scope],
            allowedScopes: deviceScopes,
          }),
        );
  }
  const useTrustedScopes =
    surface === "trusted-operator"
      ? requestAuth.trustDeclaredOperatorScopes
      : requestAuth.authMethod === "trusted-proxy" &&
        getHeader(req, "x-openclaw-scopes") !== undefined;
  if (useTrustedScopes) {
    return resolveTrustedHttpOperatorScopes(req, requestAuth);
  }
  // Ordinary plugin routes grant only write by default; a named role can narrow
  // that grant, never replace it with the role's scopes or the CLI defaults.
  const defaultScopes =
    surface === "trusted-operator" ? [...CLI_DEFAULT_OPERATOR_SCOPES] : [WRITE_SCOPE];
  return applyHttpOperatorRoleScopeCeiling(defaultScopes, requestAuth);
}