openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
60 lines (59 loc) • 3.2 kB
JavaScript
//#region src/shared/operator-scope-compat.ts
const OPERATOR_ROLE = "operator";
const OPERATOR_ADMIN_SCOPE = "operator.admin";
const OPERATOR_READ_SCOPE = "operator.read";
const OPERATOR_TALK_SCOPE = "operator.talk";
const OPERATOR_WRITE_SCOPE = "operator.write";
const OPERATOR_SESSION_READ_SCOPE = "operator.sessions.read";
const OPERATOR_SESSION_WRITE_SCOPE = "operator.sessions.write";
const OPERATOR_SCOPE_PREFIX = "operator.";
function operatorScopeSatisfied(requestedScope, granted) {
if (!requestedScope.startsWith(OPERATOR_SCOPE_PREFIX)) return false;
return granted.includes(requestedScope) || granted.includes(OPERATOR_ADMIN_SCOPE) || (requestedScope === OPERATOR_READ_SCOPE || requestedScope === OPERATOR_TALK_SCOPE || requestedScope === OPERATOR_SESSION_READ_SCOPE || requestedScope === OPERATOR_SESSION_WRITE_SCOPE) && granted.includes(OPERATOR_WRITE_SCOPE) || requestedScope === OPERATOR_SESSION_READ_SCOPE && (granted.includes(OPERATOR_READ_SCOPE) || granted.includes(OPERATOR_SESSION_WRITE_SCOPE));
}
/** Returns true when a role grant satisfies requested scopes, including operator implications. */
function roleScopesAllow(params) {
return resolveMissingRequestedScope(params) === null;
}
/** Keeps only permissions shared by both ceilings, including implied operator scopes. */
function intersectOperatorScopes(scopes, ceiling) {
if (roleScopesAllow({
role: "operator",
requestedScopes: scopes,
allowedScopes: ceiling
})) return [...scopes];
const result = [.../* @__PURE__ */ new Set([...scopes, ...ceiling])].filter((scope) => roleScopesAllow({
role: "operator",
requestedScopes: [scope],
allowedScopes: scopes
}) && roleScopesAllow({
role: "operator",
requestedScopes: [scope],
allowedScopes: ceiling
}));
for (const scope of [OPERATOR_SESSION_WRITE_SCOPE, OPERATOR_SESSION_READ_SCOPE]) if (operatorScopeSatisfied(scope, scopes) && operatorScopeSatisfied(scope, ceiling) && !operatorScopeSatisfied(scope, result)) result.push(scope);
return result;
}
/** Returns the original first requested scope not covered by the role's allowed scopes. */
function resolveMissingRequestedScope(params) {
const role = params.role.trim();
const prefix = `${role}.`;
const allowedScopes = params.allowedScopes.map((scope) => scope.trim());
for (const scope of params.requestedScopes) {
const requestedScope = scope.trim();
if (!requestedScope) continue;
if (!(role === OPERATOR_ROLE ? operatorScopeSatisfied(requestedScope, allowedScopes) : requestedScope.startsWith(prefix) && allowedScopes.includes(requestedScope))) return scope;
}
return null;
}
/** Returns the first requested scope that does not belong to any requested role. */
function resolveScopeOutsideRequestedRoles(params) {
const prefixes = params.requestedRoles.map((role) => `${role.trim()}.`);
for (const scope of params.requestedScopes) {
const requestedScope = scope.trim();
if (!prefixes.some((prefix) => !requestedScope || requestedScope.startsWith(prefix))) return scope;
}
return null;
}
//#endregion
export { roleScopesAllow as a, resolveScopeOutsideRequestedRoles as i, operatorScopeSatisfied as n, resolveMissingRequestedScope as r, intersectOperatorScopes as t };