Firebase Data Connect zapewnia niezawodne zabezpieczenia po stronie klienta:
- Autoryzacja klienta mobilnego i internetowego
- Kontrola dostępu na poziomie poszczególnych zapytań i mutacji
- Potwierdzenie aplikacji przez Firebase App Check.
Data Connect zapewnia dodatkowe zabezpieczenia:
- Autoryzacja po stronie serwera
- Bezpieczeństwo użytkownika projektu Firebase i Cloud SQL za pomocą usługi IAM.
Autoryzowanie zapytań i mutacji klienta
Data Connect jest w pełni zintegrowany z Firebase Authentication, dzięki czemu możesz używać w swoim projekcie rozbudowanych danych o użytkownikach, którzy uzyskują dostęp do Twoich danych (uwierzytelnianie), oraz o danych, do których mają oni dostęp (autoryzacja).
Data Connect udostępnia dyrektywę @auth
dla zapytań i mutacji, która umożliwia ustawienie poziomu uwierzytelniania wymaganego do autoryzacji operacji. Ten przewodnik prezentuje dyrektywę @auth
z przykładami.
Ponadto Data Connect obsługuje wykonywanie zapytań zawartych w mutacjach, dzięki czemu możesz pobierać dodatkowe kryteria autoryzacji zapisane w bazie danych i używać ich w instrukcjach @check
, aby określić, czy otoczone mutacje są autoryzowane. W tym przypadku autoryzacji dyrektywa @redact
pozwala Ci określić, czy wyniki zapytań mają być zwracane do klientów w ramach protokołu sieciowego, a wbudowane zapytanie ma być pomijane w wygenerowanych pakietach SDK. Znajdź wprowadzenie do tych dyrektyw z przykładami.
Informacje o dyrektywie @auth
Możesz skonfigurować dyrektywę @auth
, aby korzystała z jednego z kilku wstępnie zdefiniowanych poziomów dostępu, które obejmują wiele typowych scenariuszy dostępu. Te poziomy sięgają od PUBLIC
(co pozwala na żądania i mutacje ze wszystkich klientów bez uwierzytelniania) do NO_ACCESS
(co uniemożliwia żądania i mutacje poza uprzywilejowanymi środowiskami serwerów korzystającymi z pakietu Admin SDK Firebase). Każdy z tych poziomów jest powiązany z procesami uwierzytelniania udostępnianymi przez Firebase Authentication.
Poziom | Definicja |
---|---|
PUBLIC |
Operację może wykonać dowolna osoba z uwierzytelnieniem lub bez niego. |
PUBLIC |
Operację może wykonać dowolna osoba z uwierzytelnieniem lub bez niego. |
USER_ANON |
Każdy zidentyfikowany użytkownik, w tym użytkownicy, którzy zalogowali się anonimowo za pomocą Firebase Authentication, może wykonać zapytanie lub mutację. |
USER |
Każdy użytkownik zalogowany za pomocą Firebase Authentication może wykonać zapytanie lub mutację, z wyjątkiem użytkowników logujących się anonimowo. |
USER_EMAIL_VERIFIED |
Każdy użytkownik, który zalogował się za pomocą Firebase Authentication z weryfikowanym adresem e-mail, może wykonać zapytanie lub mutację. |
NO_ACCESS |
Tej operacji nie można wykonać poza kontekstem pakietu SDK Admin. |
Korzystając z tych wstępnie zdefiniowanych poziomów dostępu, możesz definiować złożone i solidne mechanizmy autoryzacji w instrukcji @auth
za pomocą filtrów where
i wyrażeń w języku CEL (Common Expression Language) ocenianych na serwerze.
Używanie dyrektywy @auth
do implementowania typowych scenariuszy autoryzacji
Wstępnie ustawione poziomy dostępu stanowią punkt wyjścia dla autoryzacji.
Poziom dostępu USER
to najczęściej używany poziom podstawowy.
Pełny dostęp zabezpieczony będzie opierać się na poziomie USER
oraz filtrach i wyrażeniach, które sprawdzają atrybuty użytkownika, atrybuty zasobu, role i inne. Poziomy USER_ANON
i USER_EMAIL_VERIFIED
to odmiany przypadku USER
.
Składnia wyrażeń umożliwia analizowanie danych za pomocą obiektu auth
, który reprezentuje dane uwierzytelniania przekazywane z operacjami, zarówno standardowe dane w tokenach uwierzytelniania, jak i dane niestandardowe w tokenach. Listę pól dostępnych w obiekcie auth
znajdziesz w sekcji z informacjami referencyjnymi.
Oczywiście są przypadki, w których PUBLIC
jest odpowiednim poziomem dostępu na początek. Ponownie przypominamy, że poziom dostępu to zawsze punkt wyjścia, a do zapewnienia solidnej ochrony potrzebne są dodatkowe filtry i wyrażenia.
Ten przewodnik zawiera teraz przykłady wykorzystania funkcji USER
i PUBLIC
.
Motywujący przykład
Poniższe przykłady sprawdzonych metod odnoszą się do schematu platformy blogowej, w której niektóre treści są dostępne tylko dla użytkowników z subskrypcją płatną.
Taka platforma prawdopodobnie modelowałaby Users
i Posts
.
type User @table(key: "uid") {
uid: String!
name: String
birthday: Date
createdAt: Timestamp! @default(expr: "request.time")
}
type Post @table {
author: User!
text: String!
# "one of 'draft', 'public', or 'pro'"
visibility: String! @default(value: "draft")
# "the time at which the post should be considered published. defaults to
# immediately"
publishedAt: Timestamp! @default(expr: "request.time")
createdAt: Timestamp! @default(expr: "request.time")
updatedAt: Timestamp! @default(expr: "request.time")
}
Zasoby należące do użytkownika
Firebase zaleca pisanie filtrów i wyrażeń, które sprawdzają, czy użytkownik jest właścicielem zasobu. W tych przypadkach jest to Posts
.
W podanych niżej przykładach dane z tokenów autoryzacji są odczytywane i porównywane za pomocą wyrażeń. Typowy schemat to użycie wyrażenia takiego jak where: {authorUid:
{eq_expr: "auth.uid"}}
do porównania zapisanego authorUid
z auth.uid
(identyfikatorem użytkownika) przekazanym w tokenie uwierzytelniania.
Utwórz
Ta metoda autoryzacji zaczyna się od dodania auth.uid
z tokena autoryzacji do każdego nowego Post
jako pola authorUid
, aby umożliwić porównanie w testach autoryzacji podrzędnych.
# Create a new post as the current user
mutation CreatePost($text: String!, $visibility: String) @auth(level: USER) {
post_insert(data: {
# set the author's uid to the current user uid
authorUid_expr: "auth.uid"
text: $text
visibility: $visibility
})
}
Zaktualizuj
Gdy klient próbuje zaktualizować Post
, możesz przetestować przekazaną wartość auth.uid
w porównaniu z zapisaną wartością authorUid
.
# Update one of the current user's posts
mutation UpdatePost($id: UUID!, $text: String, $visibility: String) @auth(level:USER) {
post_update(
# only update posts whose author is the current user
first: { where: {
id: {eq: $id}
authorUid: {eq_expr: "auth.uid"}
}}
data: {
text: $text
visibility: $visibility
# insert the current server time for updatedAt
updatedAt_expr: "request.time"
}
)
}
Usuń
Ta sama metoda jest używana do autoryzacji operacji usuwania.
# Delete one of the current user's posts
mutation DeletePost($id: UUID!) @auth(level: USER) {
post_delete(
# only delete posts whose author is the current user
first: { where: {
id: {eq: $id}
authorUid: {eq_expr: "auth.uid"}
}}
)
}
# Common display information for a post
fragment DisplayPost on Post {
id, text, createdAt, updatedAt
author { uid, name }
}
Lista
# List all posts belonging to the current user
query ListMyPosts @auth(level: USER) {
posts(where: {
userUid: {eq_expr: "auth.uid"}
}) {
# See the fragment above
...DisplayPost
# also show visibility since it is user-controlled
visibility
}
}
Get
# Get a post only if it belongs to the current user
query GetMyPost($id: UUID!) @auth(level: USER) {
post(key: {id: $id},
first: {where: {
id: {eq: $id}
authorUid: {eq_expr: "auth.uid"}}
}}, {
# See the fragment above
...DisplayPost
# also show visibility since it is user-controlled
visibility
}
}
Filtrowanie danych
System autoryzacji Data Connect umożliwia tworzenie zaawansowanych filtrów w połączeniu z wstępnie ustawionymi poziomami dostępu, takimi jak PUBLIC
, a także za pomocą danych z tokenów autoryzacji.
System autoryzacji umożliwia też używanie wyrażeń bez poziomu dostępu do bazy danych, jak pokazano w niektórych z następujących przykładów.
Filtrowanie według atrybutów zasobu
W tym przypadku autoryzacja nie jest oparta na tokenach uwierzytelniania, ponieważ poziom bezpieczeństwa podstawowy ma wartość PUBLIC
. Możemy jednak wyraźnie ustawić w naszej bazie danych rekordy jako odpowiednie do publicznego dostępu. Załóżmy, że mamy w naszej bazie danych Post
rekordów z visibility
ustawionym jako „publiczny”.
# List all posts marked as 'public' visibility
query ListPublicPosts @auth(level: PUBLIC) {
posts(where: {
# Test that visibility is "public"
visibility: {eq: "public"}
# Only display articles that are already published
publishedAt: {lt_expr: "request.time"}
}) {
# see the fragment above
...DisplayPost
}
}
Filtrowanie według roszczeń użytkownika
Załóżmy, że masz skonfigurowane niestandardowe oświadczenia użytkownika, które przekazują tokeny uwierzytelniające, aby zidentyfikować użytkowników w ramach abonamentu „Pro” w Twojej aplikacji. W polu auth.token.plan
w tokenie uwierzytelniającym jest oznaczony odpowiednim flagą. Wyrażenia mogą być testowane pod kątem tego pola.
# List all public or pro posts, only permitted if user has "pro" plan claim
query ProListPosts @auth(expr: "auth.token.plan == 'pro'") {
posts(where: {
# display both public posts and "pro" posts
visibility: {in: ['public', 'pro']},
# only display articles that are already published
publishedAt: {lt_expr: "request.time"},
}) {
# see the fragment above
...DisplayPost
# show visibility so pro users can see which posts are pro\
visibility
}
}
Filtrowanie według kolejności i limitu
Możesz też ustawić w rekordach visibility
wartość visibility
, aby wskazać, że są one dostępne dla użytkowników „pro”, ale w celu wyświetlenia podglądu lub listy danych z teaserem możesz ograniczyć liczbę zwracanych rekordów.Post
# Show 2 oldest Pro post as a preview
query ProTeaser @auth(level: USER) {
posts(
where: {
# show only pro posts
visibility: {eq: "pro"}
# that have already been published more than 30 days ago
publishedAt: {lt_time: {now: true, sub: {days: 30}}}
},
# order by publish time
orderBy: [{publishedAt: DESC}],
# only return two posts
limit: 2
) {
# See the fragment above
...DisplayPost
}
}
Filtrowanie według roli
Jeśli roszczenie niestandardowe definiuje rolę admin
, możesz testować i autoryzować operacje.
# List all posts unconditionally iff the current user has an admin claim
query AdminListPosts @auth(expr: "auth.token.admin == true") {
posts { ...DisplayPost }
}
Zapoznanie się z dyrektywami @check
i @redact
Dyrektywa @check
sprawdza, czy określone pola występują w wynikach zapytania. Do testowania wartości pól służy wyrażenie w języku Common Expression Language (CEL). Domyślne działanie tej dyrektywy polega na sprawdzaniu i odrzucaniu węzłów o wartości null
.
Dyrektywa @redact
zastępuje część odpowiedzi od klienta. Pole zanonimizowane jest nadal oceniane pod kątem efektów ubocznych (w tym zmian danych i @check
), a jego wyniki są nadal dostępne dla kolejnych kroków w wyrażeniach CEL.
W Data Connect dyrektywy @check
i @redact
są najczęściej używane w kontekście kontroli autoryzacji. Zapoznaj się z omówieniem wyszukiwania danych autoryzacji.
Dodaj dyrektywy @check
i @redact
, aby sprawdzić dane autoryzacji
Typowym przypadkiem użycia autoryzacji jest przechowywanie niestandardowych ról autoryzacji w bazie danych, na przykład w specjalnej tabeli uprawnień, i używanie tych ról do autoryzowania mutacji w celu tworzenia, aktualizowania lub usuwania danych.
Za pomocą wyszukiwania danych autoryzacyjnych możesz wysyłać zapytania o role na podstawie identyfikatora użytkownika i używać wyrażeń CEL, aby określić, czy mutacja jest autoryzowana. Możesz na przykład napisać funkcję UpdateMovieTitle
, która pozwala autoryzowanemu klientowi aktualizować tytuły filmów.
W dalszej części tej dyskusji przyjmijmy, że baza danych aplikacji do recenzowania filmów przechowuje rolę autoryzacji w tabeli MoviePermission
.
# MoviePermission
# Suppose a user has an authorization role with respect to records in the Movie table
type MoviePermission @table(key: ["doc", "userId"]) {
movie: Movie! # implies another field: movieId: UUID!
userId: String! # Can also be a reference to a User table, doesn't matter
role: String!
}
W tym przykładzie implementacji mutacja UpdateMovieTitle
zawiera pole query
, które służy do pobierania danych z tabeli MoviePermission
, oraz te dyrektywy, które zapewniają bezpieczeństwo i stabilność operacji:
- Dyrektywa
@transaction
, która zapewnia, że wszystkie zapytania i sprawdzenia autoryzacji są wykonywane lub nie są wykonywane w ramach jednej operacji. - Dyrektywa
@redact
służy do pomijania wyników zapytań w odpowiedzi, co oznacza, że weryfikacja autoryzacji jest wykonywana na serwerze Data Connect, ale dane wrażliwe nie są udostępniane klientowi. Para dyrektyw
@check
służących do oceny logiki autoryzacji na podstawie wyników zapytania, np. sprawdzania, czy dany identyfikator użytkownika ma odpowiednią rolę do wprowadzania modyfikacji.
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"}
# Step 1a: Use @check to test if the user has any role associated with the movie
# Here the `this` binding refers the lookup result, i.e. a MoviePermission object or null
# The `this != null` expression could be omitted since rejecting on null is default behavior
) @check(expr: "this != null", message: "You do not have access to this movie") {
# Step 1b: Check if the user has the editor role for the movie
# Next we execute another @check; now `this` refers to the contents of the `role` field
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
})
}
Nieprawidłowe rozwiązania dotyczące autoryzacji
W poprzedniej sekcji omówiliśmy wzorce, których należy używać podczas stosowania dyrektywy @auth
.
Warto też wiedzieć, jakich wzorców unikać.
Unikaj przekazywania identyfikatorów atrybutów użytkownika i parametrów tokena autoryzacji w argumentach zapytania i mutacji
Firebase Authentication to potężne narzędzie do prezentowania przepływów uwierzytelniania i bezpiecznego rejestrowania danych uwierzytelniania, takich jak zarejestrowane identyfikatory użytkowników i liczne pola przechowywane w tokenach uwierzytelniania.
Nie zalecamy przekazywania identyfikatorów użytkowników ani danych tokena autoryzacji w argumentach zapytań i mutacji.
# Antipattern!
# This incorrectly allows any user to view any other user's posts
query AllMyPosts($userId: String!) @auth(level: USER) {
posts(where: {authorUid: {eq: $userId}}) {
id, text, createdAt
}
}
Unikaj używania poziomu dostępu USER
bez żadnych filtrów
Jak już kilkakrotnie wspominaliśmy w tym przewodniku, podstawowe poziomy dostępu, takie jak USER
, USER_ANON
i USER_EMAIL_VERIFIED
, stanowią punkt wyjścia dla kontroli autoryzacji, które można wzbogacić za pomocą filtrów i wyrażeń. Używanie tych poziomów bez odpowiedniego filtra lub wyrażenia, które sprawdza, który użytkownik wykonuje żądanie, jest zasadniczo równoznaczne z używaniem poziomu PUBLIC
.
# Antipattern!
# This incorrectly allows any user to view all documents
query ListDocuments @auth(level: USER) {
documents {
id
title
text
}
}
Unikaj korzystania z poziomu dostępu PUBLIC
lub USER
na potrzeby prototypowania
Aby przyspieszyć rozwój, możesz być skłonny ustawić wszystkie operacje na poziomie dostępu PUBLIC
lub USER
bez dalszych ulepszeń, aby autoryzować wszystkie operacje i umożliwić szybkie testowanie kodu.
Gdy wykonasz w ten sposób wstępny prototyp, zacznij przechodzić z poziomu NO_ACCESS
do autoryzacji gotowej do wdrożenia z poziomami PUBLIC
i USER
.
Nie należy jednak stosować ich jako PUBLIC
ani USER
bez dodania dodatkowej logiki, jak w tym przewodniku.
# Antipattern!
# This incorrectly allows anyone to delete any post
mutation DeletePost($id: UUID!) @auth(level: PUBLIC) {
post: post_delete(
id: $id,
)
}
Używanie aplikacji Firebase App Check do weryfikacji aplikacji
Uwierzytelnianie i autoryzacja to kluczowe elementy bezpieczeństwaData Connect. Uwierzytelnianie i autoryzacja w połączeniu z potwierdzeniem aplikacji stanowią bardzo solidne rozwiązanie zabezpieczające.
W przypadku uwierzytelniania za pomocą Firebase App Check urządzenia z Twoją aplikacją będą używać dostawcy uwierzytelniania aplikacji lub urządzenia, który potwierdza, że operacje Data Connect pochodzą z autentycznej aplikacji, a żądania pochodzą z autentycznego, nienaruszonego urządzenia. To zaświadczenie jest dołączane do każdego żądania wysyłanego przez Twoją aplikację do Data Connect.
Aby dowiedzieć się, jak włączyć App Check w przypadku Data Connect i umieścić w aplikacji pakiet klienta SDK App Check, przeczytaj omówienie App Check.
Poziomy uwierzytelniania w przypadku dyrektywy @auth(level)
Tabela poniżej zawiera wszystkie standardowe poziomy dostępu i ich odpowiedniki w CEL. Poziomy uwierzytelniania są wyświetlane od najszerszego do najwęższych – każdy poziom obejmuje wszystkich użytkowników, którzy pasują do podanych poziomów.
Poziom | Definicja |
---|---|
PUBLIC |
Operację może wykonać dowolna osoba z uwierzytelnieniem lub bez niego.
Uwagi: dane mogą być odczytywane i modyfikowane przez dowolnego użytkownika. Firebase zaleca ten poziom autoryzacji w przypadku danych publicznie dostępnych, takich jak informacje o produktach lub mediach. Zapoznaj się z przykładami i alternatywami sprawdzonych metod. Jest to równoważne z poziomem dostępu @auth(expr: "true")
@auth Filtry i wyrażenia nie mogą być używane w połączeniu z tym poziomem dostępu. W przypadku takich wyrażeń zostanie zwrócony błąd 400 Nieprawidłowe żądanie.
|
USER_ANON |
Każdy zidentyfikowany użytkownik, w tym użytkownicy, którzy zalogowali się anonimowo za pomocą Firebase Authentication, może wykonać zapytanie lub mutację.
Uwaga: USER_ANON jest superzbiorem zbioru USER .
Uwagi: musisz dokładnie zaprojektować zapytania i mutacje pod kątem tego poziomu autoryzacji. Ten poziom umożliwia użytkownikowi zalogowanie się anonimnie (automatyczne logowanie powiązane tylko z urządzeniem użytkownika) za pomocą Authentication. Nie wykonuje on samodzielnie żadnych innych kontroli, na przykład czy dane należą do użytkownika. Zapoznaj się z przykładami sprawdzonych metod i alternatywami. Ponieważ anonimowe procesy logowania Authentication generują odpowiedź uid , poziom USER_ANON jest równoważny poziomowi @auth(expr: "auth.uid != nil")
|
USER |
Każdy użytkownik zalogowany za pomocą Firebase Authentication może wykonać zapytanie lub mutację, z wyjątkiem użytkowników logujących się anonimowo.
Uwagi: musisz dokładnie zaprojektować zapytania i mutacje pod kątem tego poziomu autoryzacji. Na tym poziomie sprawdzane jest tylko to, czy użytkownik jest zalogowany za pomocą Authentication, a nie wykonuje samodzielnie innych kontroli, na przykład czy dane należą do tego użytkownika. Zapoznaj się ze sprawdzonymi metodami i alternatywami. Odpowiednik: @auth(expr: "auth.uid != nil &&
auth.token.firebase.sign_in_provider != 'anonymous'")"
|
USER_EMAIL_VERIFIED |
Każdy użytkownik, który zalogował się za pomocą Firebase Authentication z weryfikowanym adresem e-mail, może wykonać zapytanie lub mutację.
Uwagi: weryfikacja e-maila jest wykonywana za pomocą Authentication, co oznacza, że opiera się na bardziej niezawodnej metodzie Authentication, a więc zapewnia dodatkową ochronę w porównaniu z USER lub USER_ANON . Ten poziom sprawdza tylko, czy użytkownik jest zalogowany za pomocą Authentication z weryfikowanego adresu e-mail, ale nie wykonuje samodzielnie innych kontroli, na przykład czy dane należą do użytkownika. Zapoznaj się z przykładami i alternatywami sprawdzonych metod.
Odpowiednik: @auth(expr: "auth.uid != nil &&
auth.token.email_verified")" |
NO_ACCESS |
Tej operacji nie można wykonać poza kontekstem pakietu SDK Admin.
Odpowiednik: @auth(expr: "false") |
Przykłady wyrażeń CEL w przypadku funkcji @auth(expr)
i @check(expr)
Jak widać w przykładach w tym przewodniku, możesz i powinieneś używać wyrażeń zdefiniowanych w języku Common Expression Language (CEL) do kontrolowania autoryzacji Data Connect za pomocą dyrektyw @auth(expr:)
i @check
.
W tej sekcji omawiamy składnię CEL, która jest przydatna do tworzenia wyrażeń do tych dyrektyw.
Pełne informacje referencyjne dotyczące CEL znajdziesz w specyfikacji CEL.
Testowanie zmiennych przekazywanych w zapytaniach i mutacjach
Składnia @auth(expr)
umożliwia dostęp do zmiennych z zapytań i mutacji oraz ich testowanie.
Możesz na przykład użyć zmiennej operacji, takiej jak $status
, za pomocą parametru vars.status
.
mutation Update($id: UUID!, $status: Any) @auth(expr: "has(vars.status)")
Dane dostępne dla wyrażeń
Zarówno wyrażenia CEL @auth(expr:)
, jak i @check(expr:)
mogą oceniać te elementy:
request.operationName
vars
(alias dorequest.variables
)auth
(alias dorequest.auth
)
Dodatkowo wyrażenia @check(expr:)
mogą oceniać:
this
(wartość bieżącego pola)
Obiekt request.operationName
Obiekt request.operarationName
przechowuje typ operacji, czyli zapytanie lub mutację.
Obiekt vars
Obiekt vars
umożliwia wyrażeniom dostęp do wszystkich zmiennych przekazanych w zapytaniu lub mutacji.
Możesz użyć w wyrażeniu atrybutu vars.<variablename>
jako aliasu pełnego atrybutu 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'")
Obiekt auth
Authentication identyfikuje użytkowników, którzy proszą o dostęp do Twoich danych, i przekazuje te informacje jako obiekt, na podstawie którego możesz budować swoje wyrażenia.
W filtrach i wyrażeniach możesz używać wartości auth
jako aliasu wartości request.auth
.
Obiekt auth zawiera te informacje:
uid
: unikalny identyfikator użytkownika przypisany do użytkownika przesyłającego żądanie.token
: mapa wartości zebranych przez Authentication.
Więcej informacji o zawartości pola auth.token
znajdziesz w artykule Dane w tokenach uwierzytelniania.
Powiązanie this
Powiązanie this
zwraca pole, do którego jest przypisane pole dyrektywy @check
. W najprostszym przypadku możesz analizować wyniki zapytania o pojedynczej wartości.
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
})
}
Jeśli zwracane pole występuje kilka razy, ponieważ każdy z jego przodków jest listą, każde wystąpienie jest testowane z użyciem this
powiązanego z każdą wartością.
W przypadku dowolnej ścieżki, jeśli jeden z jej elementów jest elementem nadrzędnym null
lub []
, nie dojdzie do odwołania się do pola i ocena CEL zostanie pominięta. Inaczej mówiąc,
sprawdzanie odbywa się tylko wtedy, gdy this
ma wartość null
lub nie-null
, ale nigdy undefined
.
Gdy samo pole jest listą lub obiektem, this
ma taką samą strukturę (w tym wszystkich potomków wybranych w przypadku obiektów), jak w tym przykładzie.
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
})
}
Składnia wyrażeń złożonych
Aby tworzyć bardziej złożone wyrażenia, możesz łączyć operatory &&
i ||
.
mutation UpsertUser($username: String!) @auth(expr: "(auth != null) && (vars.username == 'joe')")
W sekcji poniżej opisujemy wszystkie dostępne operatory.
Operatory i pierwotność operatorów
Poniższa tabela zawiera informacje o operatorach i odpowiednim dla nich priorytecie.
Załóżmy dowolne wyrażenia a
i b
, pole f
oraz indeks i
.
Operator | Opis | Związek |
---|---|---|
a[i] a() a.f |
Indeksowanie, wywoływanie i dostęp do pól | od lewej do prawej. |
!a -a |
Negacja unarna | od prawej do lewej |
a/b a%b a*b |
Operatory mnożenia | od lewej do prawej. |
a+b a-b |
Operatory addytywne | od lewej do prawej. |
a>b a>=b a<b a<=b |
Operatory relacji | od lewej do prawej. |
a in b |
występowanie na liście lub mapie; | od lewej do prawej. |
type(a) == t |
Porównanie typu, gdzie t może być wartością logiczną, liczbą całkowitą, liczbą zmiennoprzecinkową, liczbą, ciągiem znaków, listą, mapą, sygnaturą czasową lub czasem trwania. |
od lewej do prawej. |
a==b a!=b |
Operatory porównania | od lewej do prawej. |
a && b |
Warunkowe AND | od lewej do prawej. |
a || b |
Warunkowe LUB | od lewej do prawej. |
a ? true_value : false_value |
Wyrażenie warunkowe | od lewej do prawej. |
Dane w tokenach uwierzytelniania
Obiekt auth.token
może zawierać te wartości:
Pole | Opis |
---|---|
email |
adres e-mail powiązany z kontem (jeśli istnieje). |
email_verified |
true , jeśli użytkownik potwierdził, że ma dostęp do adresu email . Niektórzy dostawcy automatycznie weryfikują należące do siebie adresy e-mail. |
phone_number |
numer telefonu powiązany z kontem (jeśli istnieje); |
name |
Wyświetlana nazwa użytkownika (jeśli została ustawiona). |
sub |
Identyfikator UID użytkownika w Firebase. Musi być niepowtarzalny w ramach projektu. |
firebase.identities |
Słownik wszystkich tożsamości powiązanych z kontem tego użytkownika. Klucze słownika mogą być dowolnymi z tych elementów: email , phone , google.com , facebook.com , github.com , twitter.com . Wartości w słowniku to tablice unikalnych identyfikatorów dla każdego dostawcy tożsamości powiązanego z kontem. Na przykład auth.token.firebase.identities["google.com"][0] zawiera pierwsze identyfikator użytkownika Google powiązany z kontem. |
firebase.sign_in_provider |
Dostawca logowania użyty do uzyskania tego tokena. Może być dowolnym z tych ciągów: custom , password , phone , anonymous , google.com , facebook.com , github.com , twitter.com . |
firebase.tenant |
Identyfikator tenantId powiązany z kontem (jeśli występuje). na przykład tenant2-m6tyz . |
Dodatkowe pola w tokenach identyfikacyjnych JWT
Możesz też uzyskać dostęp do tych pól auth.token
:
Deklaracje tokenów niestandardowych | ||
---|---|---|
alg |
Algorytm | "RS256" |
iss |
Wystawca | Adres e-mail konta usługi projektu |
sub |
Temat | Adres e-mail konta usługi projektu |
aud |
Odbiorcy | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat |
Issued-at time | Bieżący czas w sekundach od początku epoki UNIX |
exp |
Okres ważności |
Czas w sekundach od początku epoki UNIX, w którym token traci ważność. Może ona nastąpić maksymalnie 3600 sekund później niż iat .
Uwaga: to ustawienie określa tylko czas wygaśnięcia tokenu niestandardowego. Jednak po zalogowaniu użytkownika za pomocą opcji signInWithCustomToken() będzie on zalogowany na urządzeniu do czasu unieważnienia sesji lub wylogowania się.
|
<claims> (opcjonalnie) |
Opcjonalne oświadczenia niestandardowe do uwzględnienia w tokenie, do których można uzyskać dostęp za pomocą funkcji auth.token (lub request.auth.token ) w wyrażeniach. Jeśli na przykład utworzysz roszczenie niestandardowe
adminClaim , możesz uzyskać do niego dostęp za pomocą
auth.token.adminClaim .
|
Co dalej?
- Firebase Data Connect udostępnia pakiet Admin SDK, który umożliwia wykonywanie zapytań i mutacji z otoczeń z podwyższonymi uprawnieniami.
- Więcej informacji o zabezpieczeniach uprawnień znajdziesz w przewodniku po zarządzaniu usługami i bazami danych.