mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 05:01:15 +00:00
gateway: clear unbound scopes for trusted-proxy auth (#57692)
* gateway: clear unbound scopes for trusted-proxy auth * gateway: isolate trusted-proxy scope test branch
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
||||
evaluateMissingDeviceIdentity,
|
||||
isTrustedProxyControlUiOperatorAuth,
|
||||
resolveControlUiAuthPolicy,
|
||||
shouldClearUnboundScopesForMissingDeviceIdentity,
|
||||
shouldSkipControlUiPairing,
|
||||
} from "./connect-policy.js";
|
||||
|
||||
@@ -300,4 +301,72 @@ describe("ws connect policy", () => {
|
||||
).toBe(tc.expected);
|
||||
}
|
||||
});
|
||||
|
||||
test("clears unbound scopes for device-less shared auth outside explicit preservation cases", () => {
|
||||
const nonControlUi = resolveControlUiAuthPolicy({
|
||||
isControlUi: false,
|
||||
controlUiConfig: undefined,
|
||||
deviceRaw: null,
|
||||
});
|
||||
const controlUi = resolveControlUiAuthPolicy({
|
||||
isControlUi: true,
|
||||
controlUiConfig: { allowInsecureAuth: true },
|
||||
deviceRaw: null,
|
||||
});
|
||||
|
||||
expect(
|
||||
shouldClearUnboundScopesForMissingDeviceIdentity({
|
||||
decision: { kind: "allow" },
|
||||
controlUiAuthPolicy: nonControlUi,
|
||||
preserveInsecureLocalControlUiScopes: false,
|
||||
authMethod: "token",
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
shouldClearUnboundScopesForMissingDeviceIdentity({
|
||||
decision: { kind: "allow" },
|
||||
controlUiAuthPolicy: nonControlUi,
|
||||
preserveInsecureLocalControlUiScopes: false,
|
||||
authMethod: "password",
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
shouldClearUnboundScopesForMissingDeviceIdentity({
|
||||
decision: { kind: "allow" },
|
||||
controlUiAuthPolicy: nonControlUi,
|
||||
preserveInsecureLocalControlUiScopes: false,
|
||||
authMethod: "trusted-proxy",
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
shouldClearUnboundScopesForMissingDeviceIdentity({
|
||||
decision: { kind: "allow" },
|
||||
controlUiAuthPolicy: nonControlUi,
|
||||
preserveInsecureLocalControlUiScopes: false,
|
||||
authMethod: undefined,
|
||||
trustedProxyAuthOk: true,
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
shouldClearUnboundScopesForMissingDeviceIdentity({
|
||||
decision: { kind: "allow" },
|
||||
controlUiAuthPolicy: controlUi,
|
||||
preserveInsecureLocalControlUiScopes: true,
|
||||
authMethod: "token",
|
||||
}),
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
shouldClearUnboundScopesForMissingDeviceIdentity({
|
||||
decision: { kind: "reject-device-required" },
|
||||
controlUiAuthPolicy: nonControlUi,
|
||||
preserveInsecureLocalControlUiScopes: false,
|
||||
authMethod: undefined,
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -81,6 +81,26 @@ export type MissingDeviceIdentityDecision =
|
||||
| { kind: "reject-unauthorized" }
|
||||
| { kind: "reject-device-required" };
|
||||
|
||||
export function shouldClearUnboundScopesForMissingDeviceIdentity(params: {
|
||||
decision: MissingDeviceIdentityDecision;
|
||||
controlUiAuthPolicy: ControlUiAuthPolicy;
|
||||
preserveInsecureLocalControlUiScopes: boolean;
|
||||
authMethod: string | undefined;
|
||||
trustedProxyAuthOk?: boolean;
|
||||
}): boolean {
|
||||
return (
|
||||
params.decision.kind !== "allow" ||
|
||||
(!params.controlUiAuthPolicy.allowBypass &&
|
||||
!params.preserveInsecureLocalControlUiScopes &&
|
||||
// trusted-proxy auth can bypass pairing for some clients, but those
|
||||
// self-declared scopes are still unbound without device identity.
|
||||
(params.authMethod === "token" ||
|
||||
params.authMethod === "password" ||
|
||||
params.authMethod === "trusted-proxy" ||
|
||||
params.trustedProxyAuthOk === true))
|
||||
);
|
||||
}
|
||||
|
||||
export function evaluateMissingDeviceIdentity(params: {
|
||||
hasDeviceIdentity: boolean;
|
||||
role: GatewayRole;
|
||||
|
||||
@@ -90,6 +90,7 @@ import {
|
||||
evaluateMissingDeviceIdentity,
|
||||
isTrustedProxyControlUiOperatorAuth,
|
||||
resolveControlUiAuthPolicy,
|
||||
shouldClearUnboundScopesForMissingDeviceIdentity,
|
||||
shouldSkipControlUiPairing,
|
||||
} from "./connect-policy.js";
|
||||
import {
|
||||
@@ -548,10 +549,13 @@ export function attachGatewayWsMessageHandler(params: {
|
||||
// allow path, including trusted token-authenticated backend operators.
|
||||
if (
|
||||
!device &&
|
||||
(decision.kind !== "allow" ||
|
||||
(!controlUiAuthPolicy.allowBypass &&
|
||||
!preserveInsecureLocalControlUiScopes &&
|
||||
(authMethod === "token" || authMethod === "password" || trustedProxyAuthOk)))
|
||||
shouldClearUnboundScopesForMissingDeviceIdentity({
|
||||
decision,
|
||||
controlUiAuthPolicy,
|
||||
preserveInsecureLocalControlUiScopes,
|
||||
authMethod,
|
||||
trustedProxyAuthOk,
|
||||
})
|
||||
) {
|
||||
clearUnboundScopes();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user