Firebase Data Connect zapewnia niezawodne zabezpieczenia po stronie klienta:
- Autoryzacja klienta mobilnego i internetowego
- Kontrola autoryzacji 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 bogatych danych o użytkownikach, którzy uzyskują dostęp do Twoich danych (uwierzytelnianie), do określenia, do jakich danych 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.
Dodatkowo 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 różnią się od poziomu PUBLIC
(który zezwala na zapytania i mutacje ze wszystkich klientów bez uwierzytelniania) do poziomu NO_ACCESS
(który nie zezwala na zapytania 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 dane standardowe w tokenach uwierzytelniania, jak i dane niestandardowe w tokenach. Listę pól dostępnych w obiekcie auth
znajdziesz w sekcji z informacjami.
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 dla platformy blogowej, w której niektóre treści są dostępne tylko w ramach planu płatnego.
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 chodzi o własność Posts
.
W podanych niżej przykładach dane z tokenów autoryzacji są odczytywane i porównywane za pomocą wyrażeń. Typowym wzorcem jest użycie wyrażenia takiego jak where: {authorUid:
{eq_expr: "auth.uid"}}
, aby porównać zapisany identyfikator authorUid
z identyfikatorem auth.uid
(identyfikator 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 „publiczne”.
# 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 znacznikiem. 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 w ramach teasera 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 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 autoryzacji 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 zakładamy, ż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 uwzględnić w aplikacji pakiet SDK klienta 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 niżej 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 media. Zapoznaj się z przykładami i alternatywami sprawdzonych metod. Jest to równoznaczne 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. Nie są wykonywane żadne inne weryfikacje, na przykład czy dane należą do 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 pokazano w przykładach w tym przewodniku, możesz i powinieneś używać wyrażeń zdefiniowanych w języku Common Expression Language (CEL), aby kontrolować autoryzację Data Connect za pomocą dyrektyw @auth(expr:)
i @check
.
W tej sekcji omawiamy składnię CEL, która jest przydatna do tworzenia wyrażeń dla 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 wartości:
request.operationName
vars
(alias dorequest.variables
)auth
(alias dlarequest.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 występuje). |
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 Firebase użytkownika. 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 identyfikatorach 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 | Aktualny 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() pozostanie on zalogowany na urządzeniu do czasu unieważnienia sesji lub wylogowania.
|
<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 w ramach uprawnień znajdziesz w przewodniku po zarządzaniu usługami i bazami danych.