В этом справочном руководстве рассматривается синтаксис языка выражений Common Expression Language (CEL), относящийся к созданию выражений для директив @auth(expr:)
и @check(expr:)
.
Полная справочная информация по CEL приведена в спецификации CEL .
Тестовые переменные, передаваемые в запросах и мутациях
Синтаксис @auth(expr)
позволяет получать доступ к переменным из запросов и мутаций и проверять их.
Например, вы можете включить переменную операции, такую как $status
, используя vars.status
.
mutation Update($id: UUID!, $status: Any) @auth(expr: "has(vars.status)")
Данные, доступные для выражений: запрос, ответ, это
Вы используете данные для:
- Оценка с помощью выражений CEL в директивах
@auth(expr:)
и@check(expr:)
- Назначение с использованием выражений сервера,
<field>_expr
.
Оба выражения CEL @auth(expr:)
и @check(expr:)
могут вычислять следующее:
-
request.operationName
-
vars
(псевдоним дляrequest.variables
) -
auth
(псевдоним дляrequest.auth
)
В мутациях вы можете получить доступ и назначить содержимое:
-
response
(для проверки частичных результатов в многошаговой логике)
Кроме того, выражения @check(expr:)
могут оценивать:
-
this
(значение текущего поля) -
response
(для проверки частичных результатов в многошаговой логике)
Привязка request.operationName
Привязка request.operarationName
хранит тип операции: запрос или мутация.
Связывание vars
(request.vars)
Привязка vars
позволяет вашим выражениям получать доступ ко всем переменным, переданным в вашем запросе или мутации.
Вы можете использовать vars.<variablename>
в выражении как псевдоним для полностью квалифицированного request.variables.<variablename>
:
# The following are equivalent
mutation StringType($v: String!) @auth(expr: "vars.v == 'hello'")
mutation StringType($v: String!) @auth(expr: "request.variables.v == 'hello'")
Привязка auth
(request.auth)
Authentication идентифицирует пользователей, запрашивающих доступ к вашим данным, и предоставляет эту информацию в качестве привязки, которую вы можете использовать в своих выражениях.
В своих фильтрах и выражениях вы можете использовать auth
как псевдоним для request.auth
.
Привязка аутентификации содержит следующую информацию:
-
uid
: уникальный идентификатор пользователя, назначенный запрашивающему пользователю. -
token
: карта значений, собранных при Authentication .
Более подробную информацию о содержимом auth.token
см. в разделе Данные в токенах аутентификации.
response
привязка
Привязка response
содержит данные, собираемые сервером в ответ на запрос или мутацию по мере сборки этих данных .
По мере выполнения операции, когда каждый шаг завершается успешно, response
содержит данные ответа с успешно завершенных шагов.
Привязка response
структурируется в соответствии с формой связанной с ней операции, включая (несколько) вложенных полей и (если применимо) встроенные запросы.
Обратите внимание, что при доступе к встроенным данным ответа на запрос поля могут содержать любой тип данных в зависимости от данных, запрошенных во встроенном запросе; при доступе к данным, возвращаемым полями мутации, такими как _insert
и _delete
, они могут содержать ключи UUID, количество удалений, значения NULL (см. справочник по мутациям ).
Например:
- В мутации, содержащей встроенный запрос, привязка
response
содержит данные поиска вresponse.query.<fieldName>.<fieldName>....
, в данном случаеresponse.query.todoList
иresponse.query.todoList.priority
.
mutation CheckTodoPriority(
$uniqueListName: String!
) {
# This query is identified as `response.query`
query @check(expr: "response.query.todoList.priority == 'high'", message: "This list is not for high priority items!") {
# This field is identified as `response.query.todoList`
todoList(where: { name: $uniqueListName }) {
# This field is identified as `response.query.todoList.priority`
priority
}
}
}
- В многошаговой мутации, например с несколькими полями
_insert
, привязкаresponse
содержит частичные данные вresponse.<fieldName>.<fieldName>....
, в данном случаеresponse.todoList_insert.id
.
mutation CreateTodoListWithFirstItem(
$listName: String!,
$itemContent: String!
) @transaction {
# Step 1
todoList_insert(data: {
id_expr: "uuidV4()",
name: $listName,
})
# Step 2:
todo_insert(data: {
listId_expr: "response.todoList_insert.id" # <-- Grab the newly generated ID from the partial response so far.
content: $itemContent,
})
}
this
привязка
Связывание this
оценивается по полю, к которому прикреплена директива @check
. В базовом случае вы можете оценить результаты запроса с одним значением.
mutation UpdateMovieTitle (
$movieId: UUID!,
$newTitle: String!)
@auth(level: USER)
@transaction {
# Step 1: Query and check
query @redact {
moviePermission( # Look up a join table called MoviePermission with a compound key.
key: {movieId: $movieId, userId_expr: "auth.uid"}
) {
# Check if the user has the editor role for the movie. `this` is the string value of `role`.
# If the parent moviePermission is null, the @check will also fail automatically.
role @check(expr: "this == 'editor'", message: "You must be an editor of this movie to update title")
}
}
# Step 2: Act
movie_update(id: $movieId, data: {
title: $newTitle
})
}
Если возвращаемое поле встречается несколько раз, поскольку любой предок является списком, каждое вхождение проверяется с this
к каждому значению.
Для любого заданного пути, если предок равен null
или []
, поле не будет достигнуто, и оценка CEL будет пропущена для этого пути. Другими словами, оценка происходит только тогда, когда this
равен null
или не null
, но никогда undefined
.
Если поле само по себе является списком или объектом, this
следует той же структуре (включая всех выбранных потомков в случае объектов), как показано в следующем примере.
mutation UpdateMovieTitle2($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
# Step 1: Query and check
query {
moviePermissions( # Now we query for a list of all matching MoviePermissions.
where: {movieId: {eq: $movieId}, userId: {eq_expr: "auth.uid"}}
# This time we execute the @check on the list, so `this` is the list of objects.
# We can use the `.exists` macro to check if there is at least one matching entry.
) @check(expr: "this.exists(p, p.role == 'editor')", message: "You must be an editor of this movie to update title") {
role
}
}
# Step 2: Act
movie_update(id: $movieId, data: {
title: $newTitle
})
}
Синтаксис сложного выражения
Вы можете писать более сложные выражения, комбинируя их с операторами &&
и ||
.
mutation UpsertUser($username: String!) @auth(expr: "(auth != null) && (vars.username == 'joe')")
В следующем разделе описаны все доступные операторы.
Операторы и приоритет операторов
Используйте следующую таблицу в качестве справочной информации по операторам и их приоритету.
Даны произвольные выражения a
и b
, поле f
и индекс i
.
Оператор | Описание | Ассоциативность |
---|---|---|
a[i] a() af | Индекс, вызов, доступ к полю | слева направо |
!a -a | Унарное отрицание | справа налево |
a/ba%ba*b | Мультипликативные операторы | слева направо |
a+b ab | Аддитивные операторы | слева направо |
a>b a>=b a<b a<=b | Реляционные операторы | слева направо |
a in b | Наличие в списке или на карте | слева направо |
type(a) == t | Сравнение типов, где t может быть bool, int, float, number, string, list, map, timestamp или duration | слева направо |
a==ba!=b | Операторы сравнения | слева направо |
a && b | Условное И | слева направо |
a || b | Условное ИЛИ | слева направо |
a ? true_value : false_value | Троичное выражение | слева направо |
Данные в токенах аутентификации
Объект auth.token
может содержать следующие значения:
Поле | Описание |
---|---|
email | Адрес электронной почты, связанный с учетной записью, если он есть. |
email_verified | true если пользователь подтвердил, что у него есть доступ к адресу email . Некоторые провайдеры автоматически проверяют адреса электронной почты, которыми они владеют. |
phone_number | Номер телефона, связанный с учетной записью, если он имеется. |
name | Отображаемое имя пользователя, если установлено. |
sub | Firebase UID пользователя. Он уникален в рамках проекта. |
firebase.identities | Словарь всех идентификаторов, связанных с учетной записью этого пользователя. Ключи словаря могут быть любыми из следующих: email , phone , google.com , facebook.com , github.com , twitter.com . Значения словаря представляют собой массивы уникальных идентификаторов для каждого поставщика идентификаторов, связанного с учетной записью. Например, auth.token.firebase.identities["google.com"][0] содержит первый идентификатор пользователя Google, связанный с учетной записью. |
firebase.sign_in_provider | Поставщик входа, используемый для получения этого токена. Может быть одной из следующих строк: custom , password , phone , anonymous , google.com , facebook.com , github.com , twitter.com . |
firebase.tenant | tenantId, связанный с учетной записью, если присутствует. Например, tenant2-m6tyz |
Дополнительные поля в токенах JWT ID
Вы также можете получить доступ к следующим полям auth.token
:
Требования к пользовательским токенам | ||
---|---|---|
alg | Алгоритм | "RS256" |
iss | Эмитент | Адрес электронной почты учетной записи службы вашего проекта |
sub | Предмет | Адрес электронной почты учетной записи службы вашего проекта |
aud | Аудитория | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat | Выпущено в момент | Текущее время в секундах с начала эпохи UNIX |
exp | Срок годности | Время в секундах с эпохи UNIX, когда истекает срок действия токена. Может быть максимум на 3600 секунд позже, чем iat .Примечание: это контролирует только время истечения срока действия самого пользовательского токена . Но как только вы введете пользователя с помощью signInWithCustomToken() , он останется в системе устройства до тех пор, пока его сеанс не будет аннулирован или пользователь не выйдет из системы. |
<claims> (необязательно) | Необязательные пользовательские утверждения для включения в токен, к которым можно получить доступ через auth.token (или request.auth.token ) в выражениях. Например, если вы создаете пользовательское утверждение adminClaim , вы можете получить к нему доступ с помощью auth.token.adminClaim . |