Общий язык выражений (CEL)

Common Expression Language (CEL) — это язык выражений общего назначения, разработанный для обеспечения быстроты, переносимости и безопасности выполнения. Вы можете использовать CEL как самостоятельно, так и встраивать его в более крупный продукт. CEL отлично подходит для самых разных приложений: от маршрутизации удалённых вызовов процедур (RPC) до определения политик безопасности. CEL расширяем, независим от платформы и оптимизирован для рабочих процессов с однократной компиляцией и многократным вычислением.

CEL был разработан специально для безопасного выполнения пользовательского кода. Хотя слепой вызов eval() для пользовательского кода Python опасен, вы можете безопасно выполнить пользовательский CEL-код. Поскольку CEL предотвращает поведение, которое могло бы снизить производительность, он выполняет вычисления безопасно за наносекунды или микросекунды. Скорость и безопасность CEL делают его идеальным для приложений, критичных к производительности.

CEL вычисляет выражения, аналогичные однострочным функциям или лямбда-выражениям. Хотя CEL обычно используется для принятия решений с логическими значениями, его также можно использовать для создания более сложных объектов, таких как JSON или сообщения буфера протокола.

Почему CEL?

Многие сервисы и приложения оценивают декларативные конфигурации. Например, управление доступом на основе ролей (RBAC) — это декларативная конфигурация, которая принимает решение о доступе на основе роли пользователя и набора пользователей. Хотя декларативные конфигурации достаточны в большинстве случаев, иногда требуются более широкие возможности. Именно здесь на помощь приходит CEL.

В качестве примера расширения декларативной конфигурации с помощью CEL рассмотрим возможности Google Cloud Identity and Access Management (IAM) . Хотя RBAC является распространённым вариантом, IAM предлагает выражения CEL, позволяющие пользователям дополнительно ограничивать область действия ролевого гранта в соответствии со свойствами сообщения proto запроса или доступными ресурсами. Описание таких условий через модель данных привело бы к созданию сложной поверхности API, с которой трудно работать. Использование CEL с управлением доступом на основе атрибутов (ABAC) — это выразительное и мощное расширение RBAC.

Основные концепции CEL

В CEL выражение компилируется с учётом заданной среды. На этапе компиляции создаётся абстрактное синтаксическое дерево (AST) в формате буфера протокола . Скомпилированные выражения сохраняются для дальнейшего использования, что позволяет максимально ускорить вычисление. Одно скомпилированное выражение может быть вычислено с использованием множества различных входных данных.

Давайте подробнее рассмотрим некоторые из этих концепций.

Выражения

Выражения пишутся пользователями. Выражения аналогичны однострочным функциям или лямбда-выражениям. Сигнатура функции, объявляющая входные данные, записывается вне выражения CEL, а библиотека функций, доступная CEL, импортируется автоматически.

Например, следующее выражение CEL принимает объект запроса, а запрос включает токен claims . Выражение возвращает логическое значение, указывающее, действителен ли ещё токен claims .

Пример выражения CEL для аутентификации токена утверждений

// 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

В то время как пользователи определяют выражение CEL, службы и приложения определяют среду, в которой оно выполняется.

Окружающая среда

Окружения определяются службами . Службы и приложения, в которые встроен CEL, объявляют окружение выражений. Окружение — это набор переменных и функций, которые можно использовать в выражениях CEL.

Например, следующий код textproto объявляет среду, содержащую request и переменные now , используя сообщение CompileRequest от службы CEL.

Пример декларации среды 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" }
  }
}

Основанные на прототипах объявления используются средством проверки типов CEL для обеспечения того, чтобы все ссылки на идентификаторы и функции в выражении были объявлены и использовались правильно.

Фазы обработки выражения

Выражения CEL обрабатываются в три этапа:

  1. Анализировать
  2. Проверять
  3. Оценивать

Наиболее распространенный шаблон использования CEL — это разбор и проверка выражений во время конфигурации, сохранение AST, а затем повторное извлечение и оценка AST во время выполнения.

Иллюстрация фаз обработки CEL

Выражения анализируются и проверяются на путях конфигурации, сохраняются, а затем оцениваются по одному или нескольким контекстам на путях чтения.

CEL-код разбирается из человекочитаемого выражения в AST с помощью лексера ANTLR и грамматики парсера. На этапе разбора формируется AST на основе прото, где каждый узел Expr в AST содержит целочисленный идентификатор, используемый для индексации метаданных, сгенерированных в ходе разбора и проверки. Файл syntax.proto , создаваемый в процессе разбора, представляет собой абстрактное представление того, что было введено в строковой форме выражения.

После разбора выражения выполняется проверка его типов в соответствии с окружением, чтобы убедиться, что все идентификаторы переменных и функций в выражении объявлены и используются корректно. Проверка типов создаёт файл checked.proto , содержащий метаданные разрешения типов, переменных и функций, которые могут значительно повысить эффективность вычисления.

Наконец, после анализа и проверки выражения оценивается сохраненное AST.

Оценщику CEL нужны три вещи:

  • Привязки функций для любых пользовательских расширений
  • Переменные привязки
  • AST для оценки

Связывания функций и переменных должны соответствовать тем, которые использовались при компиляции AST. Любые из этих входных данных могут быть повторно использованы в нескольких вычислениях, например, AST, оцениваемый с использованием нескольких наборов связываний переменных, одни и те же переменные, используемые для нескольких AST, или связывания функций, используемые на протяжении всего жизненного цикла процесса (распространённый случай).