CEL (Common Expression Language)

Common Expression Language (CEL) è un linguaggio di espressioni generico progettato per essere veloce, portatile e sicuro da eseguire. Puoi utilizzare CEL da solo o incorporarlo in un prodotto più grande. CEL è ideale per un'ampia gamma di applicazioni, dal routing delle chiamate di procedura remota (RPC) alla definizione di criteri di sicurezza. CEL è estensibile, indipendente dalla piattaforma e ottimizzato per workflow di compilazione una tantum/valutazione multipla.

CEL è stato progettato appositamente per l'esecuzione sicura del codice utente. Sebbene sia pericoloso chiamare ciecamente eval() nel codice Python di un utente, puoi eseguire in sicurezza il codice CEL di un utente. Inoltre, poiché CEL impedisce comportamenti che ne ridurrebbero le prestazioni, viene valutato in modo sicuro in nanosecondi o microsecondi. La velocità e la sicurezza di CEL lo rendono ideale per le applicazioni sensibili alle prestazioni.

CEL valuta espressioni simili a funzioni a una sola riga o espressioni lambda. Anche se CEL viene comunemente utilizzato per le decisioni booleane, puoi utilizzarlo anche per costruire oggetti più complessi come messaggi JSON o protocol buffer.

Perché CEL?

Molti servizi e applicazioni valutano le configurazioni dichiarative. Ad esempio, il controllo dell'accesso basato sui ruoli (RBAC) è una configurazione dichiarativa che produce una decisione di accesso in base a un ruolo utente e a un insieme di utenti. Sebbene le configurazioni dichiarative siano sufficienti nella maggior parte dei casi, a volte è necessaria una maggiore espressività. È qui che entra in gioco il CEL.

Come esempio di estensione di una configurazione dichiarativa con CEL, considera le funzionalità di Identity and Access Management (IAM) di Google Cloud. Sebbene RBAC sia il caso comune, IAM offre espressioni CEL per consentire agli utenti di limitare ulteriormente l'ambito della concessione basata sui ruoli in base alle proprietà del messaggio proto della richiesta o delle risorse a cui si accede. La descrizione di queste condizioni tramite il modello di dati comporterebbe una superficie API complessa e difficile da utilizzare. L'utilizzo di CEL con il controllo dell'accesso basato su attributi (ABAC) è un'estensione espressiva e potente di RBAC.

Concetti principali di CEL

In CEL, un'espressione viene compilata in base a un ambiente. Il passaggio di compilazione produce un albero sintattico astratto (AST) in formato protocol buffer. Le espressioni compilate vengono memorizzate per un uso futuro per mantenere la valutazione il più rapida possibile. Una singola espressione compilata può essere valutata con molti input diversi.

Ecco uno sguardo più da vicino ad alcuni di questi concetti.

Espressioni

Le espressioni sono scritte dagli utenti. Le espressioni sono simili ai corpi di funzioni a una sola riga o alle espressioni lambda. La firma della funzione che dichiara l'input viene scritta al di fuori dell'espressione CEL e la libreria di funzioni disponibile per CEL viene importata automaticamente.

Ad esempio, la seguente espressione CEL accetta un oggetto richiesta e la richiesta include un token claims. L'espressione restituisce un valore booleano che indica se il token claims è ancora valido.

Espressione CEL di esempio per autenticare un token delle rivendicazioni

// Check whether a JSON Web Token has expired by inspecting the 'exp' claim.
//
// Args:
//   claims - authentication claims.
//   now    - timestamp indicating the current system time.
// Returns: true if the token has expired.
//
timestamp(claims["exp"]) < now

Mentre gli utenti definiscono l'espressione CEL, i servizi e le applicazioni definiscono l'ambiente in cui viene eseguita.

Ambienti

Gli ambienti sono definiti dai servizi. I servizi e le applicazioni che incorporano CEL dichiarano l'ambiente dell'espressione. L'ambiente è l'insieme di variabili e funzioni che possono essere utilizzate nelle espressioni CEL.

Ad esempio, il seguente codice textproto dichiara un ambiente contenente le variabili request e now utilizzando il messaggio CompileRequest di un servizio CEL.

Esempio di dichiarazione dell'ambiente CEL

# Format: $SOURCE_PATH/service.proto#CompileRequest
declarations {
  name: "request"
  ident {
    type { message_type: "google.rpc.context.AttributeContext.Request" }
  }
}
declarations {
  name: "now"
  ident {
    type { well_known: "TIMESTAMP" }
  }
}

Le dichiarazioni basate su proto vengono utilizzate dal controllo dei tipi CEL per garantire che tutti i riferimenti a identificatori e funzioni all'interno di un'espressione siano dichiarati e utilizzati correttamente.

Fasi dell'elaborazione delle espressioni

Le espressioni CEL vengono elaborate in tre fasi:

  1. Analizza
  2. Assegno
  3. Valuta

Il pattern di utilizzo più comune di CEL consiste nell'analizzare e controllare le espressioni in fase di configurazione, archiviare l'AST e poi recuperare e valutare ripetutamente l'AST in fase di runtime.

Illustrazione delle fasi di elaborazione CEL

Le espressioni vengono analizzate e controllate nei percorsi di configurazione, archiviate e poi
valutate in base a uno o più contesti nei percorsi di lettura.

CEL viene analizzato da un'espressione leggibile in formato AST utilizzando una grammatica di analisi e lexer ANTLR. La fase di analisi genera un AST basato su proto in cui ogni nodo Expr dell'AST contiene un ID intero utilizzato per indicizzare i metadati generati durante l'analisi e il controllo. Il file syntax.proto prodotto durante l'analisi rappresenta la rappresentazione astratta di ciò che è stato digitato nella forma di stringa dell'espressione.

Dopo l'analisi di un'espressione, viene eseguito il controllo del tipo rispetto all'ambiente per assicurarsi che tutti gli identificatori di variabili e funzioni nell'espressione siano stati dichiarati e vengano utilizzati correttamente. Il controllo dei tipi produce un file checked.proto che include metadati di risoluzione di tipi, variabili e funzioni che possono migliorare drasticamente l'efficienza della valutazione.

Infine, dopo l'analisi e il controllo di un'espressione, viene valutato l'AST memorizzato.

Il valutatore CEL ha bisogno di tre cose:

  • Associazioni di funzioni per eventuali estensioni personalizzate
  • Associazioni di variabili
  • Un AST da valutare

I binding di funzioni e variabili devono corrispondere a quelli utilizzati per compilare l'AST. Uno qualsiasi di questi input può essere riutilizzato in più valutazioni, ad esempio un AST valutato in molti insiemi di associazioni di variabili, le stesse variabili utilizzate in molti AST o le associazioni di funzioni utilizzate per tutta la durata di un processo (un caso comune).