const OPERATOR_ROLE = "operator"; const OPERATOR_ADMIN_SCOPE = "operator.admin"; const OPERATOR_READ_SCOPE = "operator.read"; const OPERATOR_WRITE_SCOPE = "operator.write"; const OPERATOR_SCOPE_PREFIX = "operator."; function normalizeScopeList(scopes: readonly string[]): string[] { const out = new Set(); for (const scope of scopes) { const trimmed = scope.trim(); if (trimmed) { out.add(trimmed); } } return [...out]; } function operatorScopeSatisfied(requestedScope: string, granted: Set): boolean { if (granted.has(OPERATOR_ADMIN_SCOPE) && requestedScope.startsWith(OPERATOR_SCOPE_PREFIX)) { return true; } if (requestedScope === OPERATOR_READ_SCOPE) { return granted.has(OPERATOR_READ_SCOPE) || granted.has(OPERATOR_WRITE_SCOPE); } if (requestedScope === OPERATOR_WRITE_SCOPE) { return granted.has(OPERATOR_WRITE_SCOPE); } return granted.has(requestedScope); } export function roleScopesAllow(params: { role: string; requestedScopes: readonly string[]; allowedScopes: readonly string[]; }): boolean { const requested = normalizeScopeList(params.requestedScopes); if (requested.length === 0) { return true; } const allowed = normalizeScopeList(params.allowedScopes); if (allowed.length === 0) { return false; } const allowedSet = new Set(allowed); if (params.role.trim() !== OPERATOR_ROLE) { return requested.every((scope) => allowedSet.has(scope)); } return requested.every((scope) => operatorScopeSatisfied(scope, allowedSet)); }