Common Expression Language (CEL) es un lenguaje de expresión de uso general diseñado para ser rápido, portátil y seguro de ejecución. Puedes usar CEL por sí solo o incorporarlo a un producto más grande. CEL es una excelente opción para una amplia variedad de aplicaciones, desde el enrutamiento de llamadas de procedimiento remoto (RPC) hasta la definición de políticas de seguridad. CEL es extensible, independiente de la plataforma y está optimizado para flujos de trabajo de compilación una vez y evaluación varios.
CEL se diseñó específicamente para ser seguro en la ejecución de código de usuario. Si bien es peligroso llamar a ciegas a eval()
en el código de Python de un usuario, puedes ejecutar de forma segura el código CEL de un usuario. Además, como el CEL evita un comportamiento que lo haría menos eficaz, se evalúa de forma segura en nanosegundos o microsegundos. La velocidad y seguridad de CEL lo hacen ideal para las aplicaciones críticas para el rendimiento.
CEL evalúa expresiones que son similares a funciones de una sola línea o expresiones lambda. Si bien el CEL se usa comúnmente para decisiones booleanas, también puedes usarlo a fin de construir objetos más complejos, como JSON o mensajes de búfer de protocolo.
¿Por qué usar CEL?
Muchos servicios y aplicaciones evalúan configuraciones declarativas. Por ejemplo, el control de acceso basado en funciones (RBAC) es una configuración declarativa que produce una decisión de acceso en función de una función del usuario y un conjunto de usuarios. Si bien los archivos de configuración declarativos son suficientes para la mayoría de los casos, a veces necesitas más potencia expresiva. Ahí es donde entra en juego CEL.
Como ejemplo de extender una configuración declarativa con CEL, considera las capacidades de Identity and Access Management (IAM) de Google Cloud. Si bien RBAC es el caso común, IAM ofrece expresiones CEL para permitir que los usuarios restrinjan aún más el alcance del otorgamiento basado en funciones de acuerdo con las propiedades del proto mensaje de la solicitud o los recursos a los que se accede. Si describes esas condiciones a través del modelo de datos, se generaría una plataforma de API complicada con la que sería difícil trabajar. En cambio, el uso de CEL con control de acceso basado en atributos (ABAC) es una extensión expresiva y potente de RBAC.
Conceptos básicos de CEL
En CEL, una expresión se compila en un entorno. El paso de compilación produce un árbol de sintaxis abstracto (AST) en formato de búfer de protocolo. Las expresiones compiladas se almacenan para su uso futuro a fin de mantener la evaluación lo más rápido posible. Una sola expresión compilada se puede evaluar con muchas entradas diferentes.
A continuación, veremos algunos de estos conceptos.
Expresiones
Los usuarios escriben las expresiones. Las expresiones son similares a los cuerpos de funciones de una sola línea o las expresiones lambda. La firma de la función que declara que la entrada se escribe fuera de la expresión de CEL, y la biblioteca de funciones disponibles para CEL se importa de forma automática.
Por ejemplo, la siguiente expresión de CEL toma un objeto de solicitud, y la solicitud incluye un token claims
. La expresión muestra un valor booleano que indica si el token claims
sigue siendo válido.
Expresión de CEL de ejemplo para autenticar un token de reclamaciones
// 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
Mientras los usuarios definen la expresión de CEL, los servicios y las aplicaciones definen el entorno en el que se ejecuta.
Entornos
Los entornos se definen mediante servicios. Los servicios y las aplicaciones que incorporan CEL declaran el entorno de expresión. El entorno es la colección de variables y funciones que se pueden usar en expresiones en CEL.
Por ejemplo, con el siguiente código textproto
, se declara un entorno que contiene las variables request
y now
mediante el mensaje CompileRequest
de un servicio de CEL.
Ejemplo de declaración de entorno 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" }
}
}
El verificador de tipos de CEL usa las declaraciones basadas en proto para garantizar que todos los identificadores y las referencias de funciones dentro de una expresión se declaren y usen de forma correcta.
Fases del procesamiento de expresión
Las expresiones en CEL se procesan en tres fases:
- Parse
- Verificación
- Evaluar
El patrón más común de uso de CEL es analizar y verificar expresiones en el momento de la configuración, almacenar el AST y, luego, recuperar y evaluar el AST varias veces en el tiempo de ejecución.
Ilustración de las fases de procesamiento de CEL
El CEL se analiza a partir de una expresión legible por un AST con un lexer ANTLR y la gramática del analizador. La fase de análisis emite un AST basado en proto, en el que cada nodo Expr
en el AST contiene un ID de número entero que se usa para indexar en los metadatos generados durante el análisis y la verificación. El archivo syntax.proto
que se genera durante el análisis representa la representación abstracta de lo que se escribió en el formato de string de la expresión.
Después de analizar una expresión, se verifica su tipo con el entorno para garantizar que todos los identificadores de variables y funciones de la expresión se hayan declarado y se usen de forma correcta. El verificador de tipo produce un archivo checked.proto
que incluye metadatos de resolución de tipo, variable y función que pueden mejorar drásticamente la eficiencia de la evaluación.
Por último, después de que se analiza y verifica una expresión, se evalúa el AST almacenado.
El evaluador de CEL necesita tres cosas:
- Vinculaciones de funciones para cualquier extensión personalizada
- Vinculaciones de variables
- Un AST para evaluar
Las vinculaciones de funciones y variables deben coincidir con lo que se usó para compilar el AT. Cualquiera de estas entradas se puede volver a usar en varias evaluaciones, por ejemplo, una AST que se evalúa en muchos conjuntos de vinculaciones de variables, las mismas variables que se usan en muchos AST o las vinculaciones de funciones utilizadas durante el ciclo de vida de un proceso (un caso común).