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 { we
ll_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:
- Analizza
- Assegno
- 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
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).