Na tej stronie rozwijamy zagadnienia omówione w artykułach Tworzenie reguł zabezpieczeń i Tworzenie warunków reguł zabezpieczeń, aby wyjaśnić, jak Cloud Firestore Security Rules współdziała z zapytaniami. W tym artykule omawiamy szczegółowo, jak reguły zabezpieczeń wpływają na zapytania, które możesz tworzyć, oraz wyjaśniamy, jak zapewnić, aby zapytania stosowały te same ograniczenia co reguły zabezpieczeń. Na tej stronie znajdziesz też opis sposobu tworzenia reguł zabezpieczeń, które zezwalają na zapytania lub je blokują na podstawie właściwości zapytań, takich jak limit
i orderBy
.
Reguły nie są filtrami
Podczas tworzenia zapytań służących do pobierania dokumentów pamiętaj, że reguły bezpieczeństwa to nie filtry – zapytania działają na zasadzie wszystko albo nic. Aby zaoszczędzić czas i zasoby, Cloud Firestore ocenia zapytanie na podstawie potencjalnego zbioru wyników zamiast rzeczywistych wartości pól wszystkich dokumentów. Jeśli zapytanie może zwrócić dokumenty, do których klient nie ma uprawnień do odczytu, cała prośba kończy się niepowodzeniem.
Zapytania i reguły zabezpieczeń
Jak widać w poniższych przykładach, musisz tworzyć zapytania tak, aby spełniały ograniczenia reguł zabezpieczeń.
Zabezpieczanie dokumentów i wykonywanie na nich zapytań na podstawie zasad auth.uid
Ten przykład pokazuje, jak napisać zapytanie, aby pobrać dokumenty chronione przez regułę zabezpieczeń. Weź pod uwagę bazę danych zawierającą kolekcję dokumentów story
:
/stories/{storyid}
{
title: "A Great Story",
content: "Once upon a time...",
author: "some_auth_id",
published: false
}
Oprócz pól title
i content
każdy dokument zawiera pola author
i published
, które służą do kontroli dostępu. W tych przykładach zakładamy, że aplikacja korzysta z uwierzytelniania Firebase do ustawienia w polu author
identyfikatora UID użytkownika, który utworzył dokument. Firebase Authentication wypełnia też zmienną request.auth
w regułach zabezpieczeń.
Poniższa reguła zabezpieczeń używa zmiennych request.auth
i resource.data
, aby ograniczyć dostęp do odczytu i zapisu dla każdego elementu story
dla jego autora:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Only the authenticated user who authored the document can read or write
allow read, write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
Załóżmy, że Twoja aplikacja zawiera stronę, na której użytkownik widzi listę story
dokumentów, których jest autorem. Możesz oczekiwać, że do wypełnienia tej strony możesz użyć tego zapytania. To zapytanie się nie powiedzie, ponieważ nie zawiera tych samych ograniczeń co reguły zabezpieczeń:
Nieprawidłowe: ograniczenia zapytań nie pasują do ograniczeń reguł bezpieczeństwa
// This query will fail
db.collection("stories").get()
Zapytanie kończy się niepowodzeniem nawet wtedy, gdy bieżący użytkownik jest autorem wszystkich dokumentów story
. Dzieje się tak, ponieważ Cloud Firestore stosuje reguły zabezpieczeń, oceniając zapytanie na podstawie potencjalnego zbioru wyników, a nie rzeczywistych właściwości dokumentów w bazie danych. Jeśli zapytanie może zawierać dokumenty, które naruszają Twoje reguły zabezpieczeń, nie zostanie ono zrealizowane.
Natomiast to zapytanie zostanie wykonane, ponieważ zawiera ono takie samo ograniczenie pola author
jak reguły zabezpieczeń:
Prawidłowy: ograniczenia zapytania pasują do ograniczeń reguł bezpieczeństwa
var user = firebase.auth().currentUser;
db.collection("stories").where("author", "==", user.uid).get()
Bezpieczne przechowywanie dokumentów i wysyłanie zapytań na podstawie pola
Aby jeszcze bardziej zilustrować interakcję zapytań i reguł, reguły zabezpieczeń poniżej rozszerzają uprawnienia do odczytu kolekcji stories
, aby każdy użytkownik mógł odczytywać dokumenty story
, w których polu published
ustawiono wartość true
.
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Anyone can read a published story; only story authors can read unpublished stories
allow read: if resource.data.published == true || (request.auth != null && request.auth.uid == resource.data.author);
// Only story authors can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
Zapytanie o opublikowane strony musi zawierać te same ograniczenia co reguły zabezpieczeń:
db.collection("stories").where("published", "==", true).get()
Ograniczenie zapytania .where("published", "==", true)
gwarantuje, że resource.data.published
będzie równe true
w przypadku każdego wyniku. Dlatego to zapytanie jest zgodne z regułami zabezpieczeń i może odczytywać dane.
Zapytania: OR
Podczas sprawdzania poprawności zapytania logicznego OR
(or
, in
lub array-contains-any
) w porównaniu z regułami Cloud Firestore sprawdza każdą wartość porównawczą osobno. Każda wartość porównawcza musi spełniać ograniczenia reguły bezpieczeństwa. Na przykład w przypadku tej reguły:
match /mydocuments/{doc} {
allow read: if resource.data.x > 5;
}
Nieprawidłowe: zapytanie nie gwarantuje, że x > 5
dla wszystkich możliwych dokumentów
// These queries will fail
query(db.collection("mydocuments"),
or(where("x", "==", 1),
where("x", "==", 6)
)
)
query(db.collection("mydocuments"),
where("x", "in", [1, 3, 6, 42, 99])
)
Prawidłowy: zapytanie gwarantuje, że x > 5
w przypadku wszystkich potencjalnych dokumentów
query(db.collection("mydocuments"),
or(where("x", "==", 6),
where("x", "==", 42)
)
)
query(db.collection("mydocuments"),
where("x", "in", [6, 42, 99, 105, 200])
)
Ocenianie ograniczeń zapytań
Twoje reguły dotyczące zabezpieczeń mogą też akceptować lub odrzucać zapytania na podstawie ich ograniczeń.
Zmienna request.query
zawiera właściwości zapytania limit
, offset
i orderBy
. Na przykład reguły zabezpieczeń mogą odrzucać żądania, które nie ograniczają maksymalnej liczby dokumentów do określonego zakresu:
allow list: if request.query.limit <= 10;
Poniższy zestaw reguł pokazuje, jak pisać reguły zabezpieczeń, które sprawdzają ograniczenia nałożone na zapytania. W tym przykładzie rozszerzamy poprzedni zbiór reguł stories
o te zmiany:
- Zestaw reguł dzieli regułę dotyczącą odczytu na reguły dotyczące
get
ilist
. - Reguła
get
ogranicza pobieranie pojedynczych dokumentów do dokumentów publicznych lub dokumentów utworzonych przez użytkownika. - Reguła
list
stosuje te same ograniczenia co regułaget
, ale w przypadku zapytań. Sprawdza też limit zapytań, a następnie odrzuca każde zapytanie bez limitu lub z limitem większym niż 10. - Zestaw reguł definiuje funkcję
authorOrPublished()
, która pozwala uniknąć duplikowania kodu.
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Returns `true` if the requested story is 'published'
// or the user authored the story
function authorOrPublished() {
return resource.data.published == true || request.auth.uid == resource.data.author;
}
// Deny any query not limited to 10 or fewer documents
// Anyone can query published stories
// Authors can query their unpublished stories
allow list: if request.query.limit <= 10 &&
authorOrPublished();
// Anyone can retrieve a published story
// Only a story's author can retrieve an unpublished story
allow get: if authorOrPublished();
// Only a story's author can write to a story
allow write: if request.auth.uid == resource.data.author;
}
}
}
Zapytania dotyczące grupy zbiorów i reguły zabezpieczeń
Domyślnie zapytania są ograniczone do 1 kolekcji i pobierają wyniki tylko z tej kolekcji. Za pomocą zapytań dotyczących grupy kolekcji możesz pobierać wyniki z grupy kolekcji zawierającej wszystkie kolekcje o tym samym identyfikatorze. W tej sekcji dowiesz się, jak zabezpieczyć zapytania dotyczące grupy kolekcji za pomocą reguł zabezpieczeń.
Zabezpieczanie dokumentów i wykonywanie na nich zapytań na podstawie grup kolekcji
W regułach zabezpieczeń musisz wyraźnie zezwolić na zapytania dotyczące grupy zbiorów danych, tworząc regułę dla tej grupy:
- Upewnij się, że
rules_version = '2';
jest pierwszym wierszem zestawu reguł. Zapytania dotyczące grup kolekcji wymagają nowego rekurencyjnego zachowania symbolu wieloznacznego{name=**}
reguł zabezpieczeń w wersji 2. - Napisz regułę dla swojej grupy kolekcji za pomocą
match /{path=**}/[COLLECTION_ID]/{doc}
.
Weźmy na przykład forum podzielone na dokumenty forum
zawierające posts
podkolekcji:
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
}
W tej aplikacji wpisy mogą być edytowane przez ich właścicieli i czytane przez uwierzytelnionych użytkowników:
service cloud.firestore {
match /databases/{database}/documents {
match /forums/{forumid}/posts/{post} {
// Only authenticated users can read
allow read: if request.auth != null;
// Only the post author can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
Każdy uwierzytelniony użytkownik może pobrać posty z dowolnego forum:
db.collection("forums/technology/posts").get()
Co jednak w sytuacji, gdy chcesz pokazywać bieżącemu użytkownikowi jego posty na wszystkich forach?
Za pomocą zapytania dotyczącego grupy kolekcji możesz pobrać wyniki ze wszystkich kolekcji posts
:
var user = firebase.auth().currentUser;
db.collectionGroup("posts").where("author", "==", user.uid).get()
W swoich regułach zabezpieczeń musisz zezwolić na to zapytanie, tworząc regułę odczytu lub regułę listy dla grupy kolekcji posts
:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Authenticated users can query the posts collection group
// Applies to collection queries, collection group queries, and
// single document retrievals
match /{path=**}/posts/{post} {
allow read: if request.auth != null;
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
Pamiętaj jednak, że te reguły będą stosowane do wszystkich kolekcji o identyfikatorze posts
, niezależnie od hierarchii. Na przykład te reguły dotyczą wszystkich tych kolekcji posts
:
/posts/{postid}
/forums/{forumid}/posts/{postid}
/forums/{forumid}/subforum/{subforumid}/posts/{postid}
Bezpieczne zapytania dotyczące zbioru grup na podstawie pola
Podobnie jak zapytania dotyczące pojedynczej kolekcji, zapytania dotyczące grupy kolekcji muszą też spełniać ograniczenia określone przez Twoje reguły zabezpieczeń. Możemy na przykład dodać pole published
do każdego wpisu na forum, tak jak w przypadku pola stories
w przykładzie powyżej:
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
published: false
}
Następnie możemy napisać reguły dla grupy kolekcji posts
na podstawie stanu published
i postu author
:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Returns `true` if the requested post is 'published'
// or the user authored the post
function authorOrPublished() {
return resource.data.published == true || request.auth.uid == resource.data.author;
}
match /{path=**}/posts/{post} {
// Anyone can query published posts
// Authors can query their unpublished posts
allow list: if authorOrPublished();
// Anyone can retrieve a published post
// Authors can retrieve an unpublished post
allow get: if authorOrPublished();
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth.uid == resource.data.author;
}
}
}
Dzięki tym regułom klienci internetowi, klienci Apple i klienci Androida mogą wykonywać te zapytania:
Każdy może pobrać opublikowane posty na forum:
db.collection("forums/technology/posts").where('published', '==', true).get()
Każdy może pobrać opublikowane przez autora posty na wszystkich forach:
db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
Autorzy mogą pobierać wszystkie opublikowane i nieopublikowane posty ze wszystkich forów:
var user = firebase.auth().currentUser; db.collectionGroup("posts").where("author", "==", user.uid).get()
Bezpieczne przechowywanie dokumentów i wysyłanie zapytań do nich na podstawie grupy kolekcji i ścieżki dokumentu
W niektórych przypadkach możesz ograniczyć zapytania do grupy kolekcji na podstawie ścieżki dokumentu. Aby utworzyć te ograniczenia, możesz użyć tych samych technik zabezpieczania i wyszukiwania dokumentów na podstawie pola.
Weź pod uwagę aplikację, która śledzi transakcje każdego użytkownika na kilku giełdach papierów wartościowych i giełdach kryptowalut:
/users/{userid}/exchange/{exchangeid}/transactions/{transaction}
{
amount: 100,
exchange: 'some_exchange_name',
timestamp: April 1, 2019 at 12:00:00 PM UTC-7,
user: "some_auth_id",
}
Zwróć uwagę na pole user
. Chociaż wiemy, który użytkownik jest właścicielem dokumentu transaction
na podstawie jego ścieżki, powielamy te informacje w każdym dokumencie transaction
, ponieważ pozwala nam to na:
Pisać zapytania dotyczące grupy kolekcji, które są ograniczone do dokumentów zawierających określoną ścieżkę dokumentu
/users/{userid}
. Przykład:var user = firebase.auth().currentUser; // Return current user's last five transactions across all exchanges db.collectionGroup("transactions").where("user", "==", user).orderBy('timestamp').limit(5)
Zastosuj to ograniczenie do wszystkich zapytań dotyczących grupy kolekcji
transactions
, aby jeden użytkownik nie mógł pobierać dokumentówtransactions
należących do innego użytkownika.
To ograniczenie egzekwujemy w naszych regułach zabezpieczeń i wprowadzamy weryfikację danych w polu user
:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{path=**}/transactions/{transaction} {
// Authenticated users can retrieve only their own transactions
allow read: if resource.data.user == request.auth.uid;
}
match /users/{userid}/exchange/{exchangeid}/transactions/{transaction} {
// Authenticated users can write to their own transactions subcollections
// Writes must populate the user field with the correct auth id
allow write: if userid == request.auth.uid && request.data.user == request.auth.uid
}
}
}
Dalsze kroki
- Bardziej szczegółowy przykład kontroli dostępu opartej na rolach znajdziesz w artykule na temat zabezpieczania dostępu do danych użytkowników i grup.
- Przeczytaj dokumentację reguł zabezpieczeń.