Na tej stronie omawiamy pojęcia z zakresu Tworzenie struktury reguł zabezpieczeń Pisanie warunków reguł zabezpieczeń wyjaśniających, jak możesz używać reguł zabezpieczeń Cloud Firestore do tworzenia reguł, które pozwolą klientom wykonywać tylko na niektórych polach w dokumencie.
Może się zdarzyć, że konieczne będzie kontrolowanie zmian w dokumencie, który nie jest w domenie na poziomie dokumentu, ale też pola.
Możesz na przykład zezwolić klientowi na tworzenie lub modyfikowanie dokumentu, ale uniemożliwić im edytowanie niektórych pól w tym dokumencie. Możesz też wymuszać, aby każdy dokument tworzony przez klienta zawsze zawierał określony zestaw . Z tego przewodnika dowiesz się, jak wykonać niektóre z tych zadań przy użyciu Reguły zabezpieczeń Cloud Firestore.
Zezwalanie na dostęp tylko do odczytu w przypadku określonych pól
Odczyty w Cloud Firestore są wykonywane na poziomie dokumentu. Ty albo możesz pobrać cały dokument lub niczego nie pobrać. Nie można odzyskać tylko części dokumentu. Użycie samych reguł zabezpieczeń do uniemożliwiać użytkownikom odczytywanie określonych pól w dokumencie.
Jeśli w dokumencie są pola, które nie mają być ukryte
dla niektórych użytkowników, najlepszym sposobem będzie umieszczenie ich w oddzielnym dokumencie. Dla:
możesz utworzyć dokument w kolekcji podrzędnej private
np.:
/pracownicy/{emp_id}
name: "Alice Hamilton",
department: 461,
start_date: <timestamp>
/Employees/{emp_id}/private/finances,
salary: 80000,
bonus_mult: 1.25,
perf_review: 4.2
Następnie możesz dodać reguły zabezpieczeń z różnymi poziomami dostępu
dwie kolekcje. W tym przykładzie używamy niestandardowych deklaracji uwierzytelniania
aby to zrobić, tylko użytkownicy z niestandardowym żądaniem uwierzytelniania role
równym Finance
mogą
wyświetlanie informacji finansowych pracownika.
service cloud.firestore {
match /databases/{database}/documents {
// Allow any logged in user to view the public employee data
match /employees/{emp_id} {
allow read: if request.resource.auth != null
// Allow only users with the custom auth claim of "Finance" to view
// the employee's financial data
match /private/finances {
allow read: if request.resource.auth &&
request.resource.auth.token.role == 'Finance'
}
}
}
}
Ograniczanie pól podczas tworzenia dokumentu
Cloud Firestore działa bez schematów, co oznacza, że nie ma ograniczeń na poziomie bazy danych pól, które zawiera dokument. Choć w tym roku elastyczność może ułatwić programowanie, ale i tak nadejdzie moment, dzięki któremu klienci mogą tworzyć tylko dokumenty zawierające określone pola, lub nie zawierają innych pól.
Aby utworzyć te reguły, zapoznaj się z metodą keys
funkcji
request.resource.data
obiektu. To jest lista wszystkich pól, które klient
Użytkownik próbuje pisać w tym nowym dokumencie. Łącząc ten zestaw pól
za pomocą funkcji takich jak hasOnly()
lub hasAny()
,
możesz dodać logikę ograniczającą typy dokumentów, do których użytkownik może dodawać treści.
i Cloud Firestore.
Wymaganie stosowania określonych pól w nowych dokumentach
Załóżmy, że chcesz się upewnić, że wszystkie dokumenty utworzone w restaurant
zawierało co najmniej pole name
, location
i city
. Możesz
Aby to zrobić, zadzwoń pod numer hasAll()
na liście kluczy w nowym dokumencie.
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to create a document only if that document contains a name
// location, and city field
match /restaurant/{restId} {
allow create: if request.resource.data.keys().hasAll(['name', 'location', 'city']);
}
}
}
Pozwala to również tworzyć restauracje z innymi polami, ale zapewnia aby wszystkie dokumenty utworzone przez klienta zawierały co najmniej te 3 pola.
Blokowanie określonych pól w nowych dokumentach
Można też uniemożliwić klientom tworzenie dokumentów zawierających
określonych pól za pomocą funkcji hasAny()
z listą zabronionych pól. Ta metoda zwraca wartość prawda, jeśli
dokument zawiera dowolne z tych pól, więc prawdopodobnie chcesz wykluczyć
w celu uniemożliwienia wyświetlania określonych pól.
W przykładzie poniżej klienty nie mogą tworzyć
dokument, który zawiera pole average_score
lub rating_count
, ponieważ te wartości
są dodawane w późniejszym czasie przez wywołanie serwera.
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to create a document only if that document does *not*
// contain an average_score or rating_count field.
match /restaurant/{restId} {
allow create: if (!request.resource.data.keys().hasAny(
['average_score', 'rating_count']));
}
}
}
Tworzenie listy dozwolonych pól dla nowych dokumentów
Zamiast blokować niektóre pola w nowych dokumentach, możesz utworzyć
listę tylko tych pól, które są wyraźnie dozwolone w nowych dokumentach. Potem
możesz użyć funkcji hasOnly()
, aby upewnić się, że każdy nowo utworzony dokument będzie zawierał tylko te pola,
(lub podzbiór tych pól), a nie żadne inne.
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to create a document only if that document doesn't contain
// any fields besides the ones listed below.
match /restaurant/{restId} {
allow create: if (request.resource.data.keys().hasOnly(
['name', 'location', 'city', 'address', 'hours', 'cuisine']));
}
}
}
Łączenie wymaganych i opcjonalnych pól
Możesz połączyć operacje hasAll
i hasOnly
ze względów bezpieczeństwa
wymaga tylko niektórych pól i zezwala na korzystanie z innych. Na przykład w tym przykładzie
wymaga, aby wszystkie nowe dokumenty zawierały name
, location
oraz city
i opcjonalnie zezwala na pola address
, hours
i cuisine
.
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to create a document only if that document has a name,
// location, and city field, and optionally address, hours, or cuisine field
match /restaurant/{restId} {
allow create: if (request.resource.data.keys().hasAll(['name', 'location', 'city'])) &&
(request.resource.data.keys().hasOnly(
['name', 'location', 'city', 'address', 'hours', 'cuisine']));
}
}
}
W rzeczywistości można przenieść tę logikę do funkcji pomocniczej aby uniknąć duplikowania kodu i łatwiej połączyć opcjonalne i na jedną listę wymaganych pól, na przykład:
service cloud.firestore {
match /databases/{database}/documents {
function verifyFields(required, optional) {
let allAllowedFields = required.concat(optional);
return request.resource.data.keys().hasAll(required) &&
request.resource.data.keys().hasOnly(allAllowedFields);
}
match /restaurant/{restId} {
allow create: if verifyFields(['name', 'location', 'city'],
['address', 'hours', 'cuisine']);
}
}
}
Ograniczam pola przy aktualizacji
Typową praktyką w zakresie bezpieczeństwa jest zezwalanie klientom na edytowanie tylko niektórych pól, a nie
reszta. Nie możesz tego osiągnąć, patrząc tylko na
request.resource.data.keys()
listy omówionej w poprzedniej sekcji, ponieważ
pokazuje cały dokument tak, jakby wyglądałby po aktualizacji, oraz
zawiera więc pola, których klient nie zmienił.
Jeśli jednak używasz strony diff()
możesz porównać request.resource.data
z
Obiekt resource.data
, który reprezentuje dokument w bazie danych przed
i zainstalować aktualizację. Spowoduje to utworzenie mapDiff
.
czyli obiekt zawierający wszystkie zmiany między dwoma różnymi
map.
Wywołując metodę affectedKeys()
na tym obiekcie mapDiff, możesz wymyślić zestaw pól, które zostały zmienione
w zmianie. Możesz potem użyć funkcji takich jak
hasOnly()
lub hasAny()
, by upewnić się, że zestaw zawiera określone elementy.
Uniemożliwienie zmiany niektórych pól
Za pomocą funkcji hasAny()
w zbiorze wygenerowanym przez: affectedKeys()
a następnie negując ten wynik, możesz odrzucić wszystkie żądania klienta, które próbują
zmienić pola, których nie chcesz zmieniać.
Możesz na przykład zezwolić klientom na aktualizowanie informacji o bez zmiany średniej oceny ani liczby opinii.
service cloud.firestore {
match /databases/{database}/documents {
match /restaurant/{restId} {
// Allow the client to update a document only if that document doesn't
// change the average_score or rating_count fields
allow update: if (!request.resource.data.diff(resource.data).affectedKeys()
.hasAny(['average_score', 'rating_count']));
}
}
}
Możliwość zmiany tylko niektórych pól
Zamiast określać pola, których nie chcesz zmieniać, możesz również użyć funkcji
hasOnly()
aby określić listę pól, które chcesz zmienić. Jest to zasadniczo
uważane za bezpieczniejsze, ponieważ zapisy w nowych polach dokumentu są
domyślnie niedozwolone, dopóki nie zezwolisz na nie w swoich regułach zabezpieczeń.
Na przykład zamiast blokować zasady average_score
i rating_count
możesz utworzyć reguły zabezpieczeń, które umożliwią klientom tylko zmianę
Pola name
, location
, city
, address
, hours
i cuisine
.
service cloud.firestore {
match /databases/{database}/documents {
match /restaurant/{restId} {
// Allow a client to update only these 6 fields in a document
allow update: if (request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['name', 'location', 'city', 'address', 'hours', 'cuisine']));
}
}
}
Oznacza to, że jeśli w przyszłej iteracji aplikacji dokumenty w restauracji
uwzględnić pole telephone
, próby jego edytowania zakończą się niepowodzeniem
dopóki nie wrócisz i nie dodasz tego pola do listy hasOnly()
w ustawieniach zabezpieczeń
reguł.
Wymuszanie typów pól
Innym efektem braku schematu w Cloud Firestore jest to, że nie ma
egzekwowania zasad na poziomie bazy danych w odniesieniu do typów danych, które mogą być przechowywane
określonych pól. Możesz to egzekwować w regułach zabezpieczeń,
za pomocą operatora is
.
Na przykład ta reguła zabezpieczeń wymusza, aby atrybut score
opinii
pole musi być liczbą całkowitą, a pola headline
, content
i author_name
są ciągami tekstowymi, a review_date
to sygnatura czasowa.
service cloud.firestore {
match /databases/{database}/documents {
match /restaurant/{restId} {
// Restaurant rules go here...
match /review/{reviewId} {
allow create: if (request.resource.data.score is int &&
request.resource.data.headline is string &&
request.resource.data.content is string &&
request.resource.data.author_name is string &&
request.resource.data.review_date is timestamp
);
}
}
}
}
Prawidłowe typy danych operatora is
to bool
, bytes
, float
, int
,
list
, latlng
, number
, path
, map
, string
i timestamp
. is
obsługuje również dane constraint
, duration
, set
i map_diff
ale ponieważ są one generowane przez język reguł zabezpieczeń,
nie są generowane przez klientów, dlatego rzadko
używają ich w najbardziej praktycznym kontekście.
aplikacji.
Typy danych list
i map
nie obsługują wyrażeń ogólnych ani argumentów typu.
Innymi słowy, możesz użyć reguł zabezpieczeń, aby wymusić stosowanie określonego pola
zawiera listę lub mapę, ale nie możesz wymusić, aby pole zawierało listę.
wszystkich liczb całkowitych lub wszystkich ciągów.
Podobnie możesz używać reguł zabezpieczeń do egzekwowania wartości typów w przypadku określonych wpisy na liście lub mapie (przy użyciu odpowiednio notacji Brakets lub nazw kluczy), ale nie ma skrótu, które pozwalałoby wymusić stosowanie typów danych przez wszystkich użytkowników na mapie. lub całą listę naraz.
Na przykład poniższe reguły powodują, że pole tags
w dokumencie
zawiera listę, a pierwszy wpis jest ciągiem. Zapewnia to też
pole product
zawiera mapę, która z kolei zawiera nazwę produktu, która
jest ciągiem i liczbą całkowitą.
service cloud.firestore {
match /databases/{database}/documents {
match /orders/{orderId} {
allow create: if request.resource.data.tags is list &&
request.resource.data.tags[0] is string &&
request.resource.data.product is map &&
request.resource.data.product.name is string &&
request.resource.data.product.quantity is int
}
}
}
}
Typy pól muszą być wymuszane podczas tworzenia i aktualizowania dokumentu. Dlatego warto rozważyć utworzenie funkcji pomocniczej, w sekcjach tworzenia i aktualizowania reguł zabezpieczeń.
service cloud.firestore {
match /databases/{database}/documents {
function reviewFieldsAreValidTypes(docData) {
return docData.score is int &&
docData.headline is string &&
docData.content is string &&
docData.author_name is string &&
docData.review_date is timestamp;
}
match /restaurant/{restId} {
// Restaurant rules go here...
match /review/{reviewId} {
allow create: if reviewFieldsAreValidTypes(request.resource.data) &&
// Other rules may go here
allow update: if reviewFieldsAreValidTypes(request.resource.data) &&
// Other rules may go here
}
}
}
}
Typy wymuszania w przypadku pól opcjonalnych
Pamiętaj, że dzwoniąc pod numer request.resource.data.foo
dokument, w którym nie ma elementu foo
, powoduje wystąpienie błędu, i w związku z tym
reguła bezpieczeństwa wykonująca to wywołanie spowoduje odrzucenie żądania. Dasz radę
przy użyciu funkcji get
request.resource.data
. Metoda get
umożliwia podanie parametru
domyślny argument dla pola, które pobierasz z mapy, jeśli to pole
nie istnieje.
Jeśli na przykład dokumenty do weryfikacji zawierają też opcjonalne pole photo_url
i opcjonalne pole tags
, które chcesz zweryfikować, są ciągami znaków i listami.
możesz to zrobić, przepisując kod
reviewFieldsAreValidTypes
na przykład do tego:
function reviewFieldsAreValidTypes(docData) {
return docData.score is int &&
docData.headline is string &&
docData.content is string &&
docData.author_name is string &&
docData.review_date is timestamp &&
docData.get('photo_url', '') is string &&
docData.get('tags', []) is list;
}
Spowoduje to odrzucenie dokumentów, w których przypadku tags
istnieje, ale nie jest listą,
zezwalanie na dokumenty, które nie zawierają pola tags
(lub photo_url
).
Częściowe zapisy nigdy nie są dozwolone
Ostatnia uwaga na temat reguł zabezpieczeń Cloud Firestore: to, że zezwalają na klient chce wprowadzić zmianę w dokumencie, albo odrzuca całą zmianę. Nie możesz tworzyć reguł zabezpieczeń, które akceptują zapisy w niektórych polach w dokument, jednocześnie odrzucając inne w tej samej operacji.