ceo-api/src/auth/ability.factory.ts
valentinbvro 261bd0dba1 feat: add AI tool capability permissions and secret masking to CASL layer
Extends AbilityFactory with a risk-stratified capability model for Hermes
MCP tools (economic-data/capability-reasoning/legislation-search), ported
from Open.Jarvis's plugin permission system: non-owner/admin roles only
get low/medium risk tools by default, high/critical stay reserved. Adds
maskSensitiveValue as a reusable secret/PII redaction utility for future
outbox/audit logging, reinforcing the existing ai_requests hash-only
storage principle.
2026-07-23 23:36:20 +02:00

97 lines
3.3 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { AbilityBuilder, createMongoAbility, subject, type ForcedSubject, type MongoAbility } from '@casl/ability';
/**
* Nivel de risc pentru unelte AI/agenti (Hermes MCP), portat din modelul de
* capabilitati risk-stratified al Open.Jarvis (github.com/dmrr35/Open.Jarvis,
* open_jarvis/plugins/permissions.py) -- roluri non-admin primesc implicit
* doar unelte low/medium, high/critical raman rezervate owner/admin.
*/
export type AgentToolRiskLevel = 'low' | 'medium' | 'high' | 'critical';
export interface AgentToolDefinition {
name: string;
riskLevel: AgentToolRiskLevel;
description: string;
}
/** Forma exacta produsa de `subject('AgentTool', toolDefinition)` -- vezi canInvokeAgentTool. */
type AgentToolInstance = AgentToolDefinition & ForcedSubject<'AgentTool'>;
export type Action = 'manage' | 'create' | 'read' | 'update' | 'delete' | 'invoke';
export type Subject =
| 'Organization'
| 'Transaction'
| 'Document'
| 'Membership'
| 'AgentTool'
| AgentToolInstance
| 'all';
export type AppAbility = MongoAbility<[Action, Subject]>;
export type MembershipRole = 'owner' | 'admin' | 'member';
export interface MembershipContext {
tenantId: string;
role: MembershipRole;
}
/**
* Registrul uneltelor AI expuse prin Hermes (namespace MetaMCP 'ceo-os-endpoint').
* `name` trebuie sa corespunda exact numelui serverului MCP raportat de
* `hermes mcp test` -- vezi project_supersrv_new_server.md pentru lista live.
*/
export const AGENT_TOOL_REGISTRY: readonly AgentToolDefinition[] = [
{
name: 'economic-data',
riskLevel: 'low',
description: 'World Bank/ECB/Eurostat/FRED/OECD, stiri, IP/domeniu lookups',
},
{
name: 'capability-reasoning',
riskLevel: 'low',
description: 'Potrivire ocupatii/skill-uri ESCO pentru "ce poate/nu poate face agentul"',
},
{
name: 'legislation-search',
riskLevel: 'medium',
description: 'Cautare semantica in legislatia UK/DE/RO -- informativ, nu consultanta juridica',
},
];
const DEFAULT_ROLE_INVOKABLE_RISK_LEVELS: readonly AgentToolRiskLevel[] = ['low', 'medium'];
/**
* Traduce rolul de membership (blueprint 8.3: memberships.role) intr-un set de
* permisiuni CASL. Regulile sunt intentionat minimale -- se extind per bounded
* context pe masura ce apar entitati noi (11.1 "roles si ABAC policies").
*/
@Injectable()
export class AbilityFactory {
createForMembership(membership: MembershipContext): AppAbility {
const { can, build } = new AbilityBuilder<AppAbility>(createMongoAbility);
if (membership.role === 'owner' || membership.role === 'admin') {
can('manage', 'all');
} else {
can('read', 'all');
can(['create', 'update'], ['Transaction', 'Document']);
can('invoke', 'AgentTool', { riskLevel: { $in: [...DEFAULT_ROLE_INVOKABLE_RISK_LEVELS] } });
}
return build();
}
}
/**
* Verifica daca o abilitate poate invoca o unealta AI dupa nume. Nume necunoscute
* (absente din AGENT_TOOL_REGISTRY) sunt respinse implicit -- acelasi deny-by-default
* ca `require_plugin_permission()` din Open.Jarvis.
*/
export function canInvokeAgentTool(ability: AppAbility, toolName: string): boolean {
const toolDefinition = AGENT_TOOL_REGISTRY.find((tool) => tool.name === toolName);
if (!toolDefinition) {
return false;
}
return ability.can('invoke', subject('AgentTool', toolDefinition));
}