A Common Expression Language (CEL) é uma linguagem de expressão de uso geral projetada para ser rápida, portátil e segura de executar. Você pode usar a CEL sozinha ou incorporá-la a um produto maior. A CEL é ideal para uma ampla variedade de aplicativos, desde o roteamento de chamadas de procedimento remoto (RPCs) até a definição de políticas de segurança. A CEL é extensível, independente de plataforma e otimizada para fluxos de trabalho de compilação única/avaliação múltipla.
A CEL foi projetada especificamente para ser segura na execução de código do usuário. Embora seja
perigoso chamar eval()
sem pensar em um código Python do usuário, é possível
executar com segurança o código CEL de um usuário. E como a CEL impede comportamentos que diminuiriam a performance, ela faz avaliações seguras em nanossegundos ou microssegundos. A velocidade e a segurança da CEL a tornam ideal para aplicativos essenciais para o desempenho.
A CEL avalia expressões semelhantes a funções de linha única ou expressões lambda. Embora o CEL seja usado com frequência para decisões booleanas, também é possível usá-lo para construir objetos mais complexos, como mensagens JSON ou de buffer de protocolo.
Por que usar a CEL?
Muitos serviços e aplicativos avaliam configurações declarativas. Por exemplo, o controle de acesso baseado em função (RBAC) é uma configuração declarativa que produz uma decisão de acesso com base em uma função de usuário e um conjunto de usuários. Embora as configurações declarativas sejam suficientes para a maioria dos casos, às vezes você precisa de mais poder expressivo. É aí que entra a CEL.
Como exemplo de extensão de uma configuração declarativa com CEL, considere os recursos do Identity and Access Management (IAM) do Google Cloud. Embora o RBAC seja o caso comum, o IAM oferece expressões CEL para permitir que os usuários restrinjam ainda mais o escopo da concessão baseada em papéis de acordo com as propriedades da mensagem proto da solicitação ou dos recursos acessados. Descrever essas condições usando o modelo de dados resultaria em uma superfície de API complicada e difícil de trabalhar. Em vez disso, usar a CEL com o controle de acesso baseado em atributos (ABAC) é uma extensão expressiva e poderosa do RBAC.
Principais conceitos da CEL
Em CEL, uma expressão é compilada em um ambiente. A etapa de compilação produz uma árvore de sintaxe abstrata (AST) no formato buffer de protocolo. As expressões compiladas são armazenadas para uso futuro, mantendo a avaliação o mais rápida possível. Uma única expressão compilada pode ser avaliada com várias entradas diferentes.
Confira mais detalhes sobre alguns desses conceitos.
Expressões
As expressões são escritas pelos usuários. As expressões são semelhantes a corpos de função de linha única ou expressões lambda. A assinatura da função que declara a entrada é escrita fora da expressão CEL, e a biblioteca de funções disponível para a CEL é importada automaticamente.
Por exemplo, a expressão CEL a seguir usa um objeto de solicitação, e a
solicitação inclui um token claims
. A expressão retorna um valor booleano que
indica se o token claims
ainda é válido.
Exemplo de expressão CEL para autenticar um token de declarações
// 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
Enquanto os usuários definem a expressão CEL, os serviços e aplicativos definem o ambiente em que ela é executada.
Ambientes
Os ambientes são definidos por serviços. Serviços e aplicativos que incorporam CEL declaram o ambiente de expressão. O ambiente é o conjunto de variáveis e funções que podem ser usadas em expressões CEL.
Por exemplo, o código textproto
a seguir declara um ambiente que contém
as variáveis request
e now
usando a mensagem CompileRequest
de um serviço
CEL.
Exemplo de declaração de 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" }
}
}
As declarações baseadas em proto são usadas pelo verificador de tipos da CEL para garantir que todos os identificadores e referências de função em uma expressão sejam declarados e usados corretamente.
Fases do processamento de expressões
As expressões CEL são processadas em três fases:
- Analisar
- Cheque
- Avaliar
O padrão mais comum de uso da CEL é analisar e verificar expressões no momento da configuração, armazenar a AST e, em seguida, recuperar e avaliar a AST repetidamente no ambiente de execução.
Ilustração das fases de processamento da CEL
A CEL é analisada de uma expressão legível por humanos para uma AST usando uma gramática de analisador léxico e analisador ANTLR. A fase de análise emite uma AST baseada em proto em que cada nó Expr
na AST contém um ID inteiro usado para indexar metadados gerados durante a análise e a verificação. O arquivo syntax.proto
produzido durante a análise representa a representação abstrata do que foi digitado na forma de string da expressão.
Depois que uma expressão é analisada, ela é verificada em relação ao ambiente para garantir que todos os identificadores de variáveis e funções na expressão foram declarados e estão sendo usados corretamente. O verificador de tipos produz um arquivo checked.proto
que inclui metadados de resolução de tipo, variável e função, o que pode melhorar muito a eficiência da avaliação.
Por fim, depois que uma expressão é analisada e verificada, a AST armazenada é avaliada.
O avaliador de CEL precisa de três coisas:
- Vinculações de função para extensões personalizadas
- Vinculações de variáveis
- Uma AST a ser avaliada
As vinculações de função e variável precisam corresponder ao que foi usado para compilar a AST. Qualquer uma dessas entradas pode ser reutilizada em várias avaliações, como um AST sendo avaliado em muitos conjuntos de vinculações de variáveis, as mesmas variáveis usadas em muitos ASTs ou as vinculações de funções usadas durante todo o ciclo de vida de um processo (um caso comum).