Warunki zapisu reguł zabezpieczeń Cloud Firestore

Ten przewodnik jest rozwinięciem przewodnika dotyczącego strukturyzowania reguł zabezpieczeń . Pokazuje, jak dodawać warunki do Cloud Firestore Security Rules. Jeśli nie znasz podstaw Cloud Firestore Security Rules, zapoznaj się z przewodnikiem dla początkujących.

Podstawowym elementem składowym Cloud Firestore Security Rules jest warunek. Warunek to wyrażenie logiczne, które określa, czy dana operacja powinna zostać dozwolona czy zabroniona. Za pomocą reguł zabezpieczeń możesz pisać warunki, które sprawdzają uwierzytelnianie użytkownika, weryfikują dane przychodzące, a nawet uzyskują dostęp do innych części bazy danych.

Uwierzytelnianie

Jednym z najczęstszych wzorców reguł zabezpieczeń jest kontrolowanie dostępu na podstawie stanu uwierzytelnienia użytkownika. Na przykład aplikacja może zezwalać na zapisywanie danych tylko zalogowanym użytkownikom:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to access documents in the "cities" collection
    // only if they are authenticated.
    match /cities/{city} {
      allow read, write: if request.auth != null;
    }
  }
}

Innym częstym wzorcem jest upewnienie się, że użytkownicy mogą tylko odczytywać i zapisywać własne dane:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure the uid of the requesting user matches name of the user
    // document. The wildcard expression {userId} makes the userId variable
    // available in rules.
    match /users/{userId} {
      allow read, update, delete: if request.auth != null && request.auth.uid == userId;
      allow create: if request.auth != null;
    }
  }
}

Jeśli aplikacja korzysta z uwierzytelniania Firebase lub Google Cloud Identity Platform, zmienna request.auth zawiera informacje o uwierzytelnieniu klienta żądającego danych. Więcej informacji o zmiennej request.auth znajdziesz w dokumentacji referencyjnej.

Walidacja danych

Wiele aplikacji przechowuje informacje o kontroli dostępu jako pola w dokumentach w bazie danych. Cloud Firestore Security Rules mogą dynamicznie zezwalać na dostęp lub go odmawiać na podstawie danych dokumentu:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

Zmienna resource odnosi się do żądanego dokumentu, a resource.data to mapa wszystkich pól i wartości przechowywanych w dokumencie. Więcej informacji o zmiennej resource znajdziesz w dokumentacji referencyjnej.

Podczas zapisywania danych możesz chcieć porównać dane przychodzące z danymi istniejącymi. W takim przypadku, jeśli zestaw reguł zezwala na oczekujący zapis, zmienna request.resource zawiera przyszły stan dokumentu. W przypadku operacji update, które modyfikują tylko podzbiór pól dokumentu, zmienna request.resource będzie zawierać stan dokumentu po operacji. Możesz sprawdzić wartości pól w request.resource, aby zapobiec niechcianym lub niespójnym aktualizacjom danych:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure all cities have a positive population and
    // the name is not changed
    match /cities/{city} {
      allow update: if request.resource.data.population > 0
                    && request.resource.data.name == resource.data.name;
    }
  }
}

Dostęp do innych dokumentów

Za pomocą funkcji get() i exists() reguły zabezpieczeń mogą oceniać żądania przychodzące na podstawie innych dokumentów w bazie danych. Funkcje get() i exists() wymagają w pełni określonych ścieżek dokumentów. Gdy używasz zmiennych do tworzenia ścieżek dla funkcji get() i exists(), musisz jawnie je wyłączyć za pomocą składni $(variable).

W przykładzie poniżej zmienna database jest przechwytywana przez instrukcję match match /databases/{database}/documents i używana do utworzenia ścieżki:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      // Make sure a 'users' document exists for the requesting user before
      // allowing any writes to the 'cities' collection
      allow create: if request.auth != null && exists(/databases/$(database)/documents/users/$(request.auth.uid));

      // Allow the user to delete cities if their user document has the
      // 'admin' field set to 'true'
      allow delete: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
    }
  }
}

W przypadku zapisów możesz użyć funkcji getAfter(), aby uzyskać dostęp do stanu dokumentu po zakończeniu transakcji lub partii zapisów, ale przed zatwierdzeniem transakcji lub partii. Podobnie jak get(), funkcja getAfter() przyjmuje w pełni określoną ścieżkę dokumentu. Za pomocą funkcji getAfter() możesz definiować zestawy zapisów, które muszą być wykonywane razem jako transakcja lub partia.

Limity wywołań dostępu

Obowiązuje limit wywołań dostępu do dokumentów na ocenę zestawu reguł:

  • 10 w przypadku żądań dotyczących pojedynczego dokumentu i zapytań.
  • 20 w przypadku odczytów z wielu dokumentów, transakcji, i zapisów zbiorczych. Poprzedni limit 10 dotyczy też każdej operacji.

    Załóżmy na przykład, że tworzysz zbiorcze żądanie zapisu z 3 operacjami zapisu a reguły zabezpieczeń używają 2 wywołań dostępu do dokumentów, aby zweryfikować każdy zapis. W takim przypadku każdy zapis używa 2 z 10 wywołań dostępu, a zbiorcze żądanie zapisu używa 6 z 20 wywołań dostępu.

Przekroczenie któregokolwiek z tych limitów spowoduje błąd odmowy dostępu. Niektóre wywołania dostępu do dokumentów mogą być buforowane i nie są wliczane do limitów.

Szczegółowe wyjaśnienie, jak te limity wpływają na transakcje i zapisy zbiorcze, znajdziesz w przewodniku dotyczącym zabezpieczania operacji niepodzielnych.

Wywołania dostępu i ceny

Użycie tych funkcji powoduje wykonanie operacji odczytu w bazie danych, co oznacza, że opłata zostanie naliczona za odczyt dokumentów, nawet jeśli reguły odrzucą żądanie. Więcej informacji o rozliczeniach znajdziesz w Cloud Firestore cenniku.

Funkcje niestandardowe

Gdy reguły zabezpieczeń stają się bardziej złożone, możesz chcieć umieścić zestawy warunków w funkcjach, których można używać w całym zestawie reguł. Reguły zabezpieczeń obsługują funkcje niestandardowe. Składnia funkcji niestandardowych jest podobna do JavaScriptu, ale funkcje reguł zabezpieczeń są pisane w języku specyficznym dla domeny, który ma pewne ważne ograniczenia:

  • Funkcje mogą zawierać tylko jedną instrukcję return. Nie mogą zawierać żadnej dodatkowej logiki. Na przykład nie mogą wykonywać pętli ani wywoływać usług zewnętrznych.
  • Funkcje mogą automatycznie uzyskiwać dostęp do funkcji i zmiennych z zakresu, w którym są zdefiniowane. Na przykład funkcja zdefiniowana w zakresie service cloud.firestore ma dostęp do zmiennej resource i wbudowanych funkcji, takich jak get() i exists().
  • Funkcje mogą wywoływać inne funkcje, ale nie mogą być rekurencyjne. Łączna głębokość stosu wywołań jest ograniczona do 10.
  • W regułach w wersji v2 funkcje mogą definiować zmienne za pomocą słowa kluczowego let. Funkcje mogą mieć maksymalnie 10 powiązań let, ale muszą kończyć się instrukcją return.

Funkcja jest definiowana za pomocą słowa kluczowego function i przyjmuje 0 lub więcej argumentów. Możesz na przykład połączyć 2 typy warunków użyte w powyższych przykładach w jedną funkcję:

service cloud.firestore {
  match /databases/{database}/documents {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }

    match /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

Używanie funkcji w regułach zabezpieczeń ułatwia ich utrzymanie, gdy stają się one bardziej złożone.

Reguły nie są filtrami

Gdy zabezpieczysz dane i zaczniesz pisać zapytania, pamiętaj, że reguły zabezpieczeń nie są filtrami. Nie możesz napisać zapytania o wszystkie dokumenty w kolekcji i oczekiwać, że Cloud Firestore zwróci tylko te dokumenty, do których bieżący klient ma uprawnienia dostępu.

Na przykład rozważ tę regułę zabezpieczeń:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

Odmowa: ta reguła odrzuca to zapytanie, ponieważ zestaw wyników może zawierać dokumenty, w których visibility nie jest ustawione na public:

Internet
db.collection("cities").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
    });
});

Dozwolone: ta reguła zezwala na to zapytanie, ponieważ klauzula where("visibility", "==", "public") gwarantuje, że zestaw wyników spełnia warunek reguły:

Internet
db.collection("cities").where("visibility", "==", "public").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
        });
    });

Cloud Firestore reguły zabezpieczeń oceniają każde zapytanie na podstawie jego potencjalnego wyniku i odrzucają żądanie, jeśli może ono zwrócić dokument, do którego klient nie ma uprawnień do odczytu. Zapytania muszą być zgodne z ograniczeniami określonymi w regułach zabezpieczeń. Więcej informacji o regułach zabezpieczeń i zapytaniach znajdziesz w artykule Bezpieczne wysyłanie zapytań o dane.

Dalsze kroki