Правила Firestore: get () выдает ошибку вместо возврата null, когда документ не существует (только в Prod и Emulator; работает с тестером правил) - PullRequest
0 голосов
/ 29 мая 2020

В моих правилах безопасности я делаю get() для userDoc.

Если do c не существует, get() должен вернуть null (cf документация ), и правило должно разрешать запрос (если do c существует, то правило должно разрешать, только если do c имеет правильное поле version)

Проблема в том, что правила отклоняют создание первого документа при работе (= когда userDo c не существует), но они работают, когда я тестирую их с помощью инструмента тестирования

Я воспроизводю ту же ошибку с эмулятором, поэтому я смог использовать средство проверки правил и заметил, что get() выдает ошибку вместо того, чтобы возвращать значение null (несмотря на то, что правильно сформированный аргумент). Это согласуется с тем фактом, что в продукте у меня нет denied request, а только error request в консоли.

Security Rules analyzer screenshot

Полная функция выглядит следующим образом:

        function resourceVersionIsCompatible(resourceVersion) {
          let userDoc = get(/databases/$(database)/documents/users/$(userId));
          let serverVersion = userDoc != null && 'version' in userDoc.data ? int(string(userDoc.data.version).split('\\.')[0]) : 0;
          return (serverVersion == 0) || (resourceVersion == serverVersion);          
        }

Если я заменю userDoc != null на false, тогда он будет работать хорошо (я могу создать первый документ, который инициализирует пользователя Do c благодаря облачная функция. А затем я могу восстановить обычные правила для успешного создания последующих документов с проверкой рабочей версии).

Полные правила приведены ниже:

rules_version = '2';

service cloud.firestore {

  match /databases/{database}/documents {

        // Everything is disallowed if not specifically allowed after
    match /{document=**} {
      allow read, write: if false;
    }

    match /users/{userId} {

      allow read: if request.auth.uid == userId;
      allow write: if false;

      match /{collection}/{documentId} {

        function resourceIsValid(resourceVersion) {
          return resourceVersion <= 2 && {
            'version-1': (collection == 'interactions' || collection == 'contacts'),
            'version-2': (collection == 'events' || collection == 'contacts')
          }['version-' + string(resourceVersion)];
        }

        function resourceVersionIsCompatible(resourceVersion) {
          let userDoc = get(/databases/$(database)/documents/users/$(userId));
          let serverVersion = userDoc != null && 'version' in userDoc.data ? int(string(userDoc.data.version).split('\\.')[0]) : 0;
          // We allow the request if the resourceVersion is the same major than the serverVersion
          // of if there is no serverVersion (then a cloud function will initiate it based on the doc version)
          return (serverVersion == 0) || (resourceVersion == serverVersion);          
        }

        function creationAllowed() {
            let resourceVersionMajor = 'v' in request.resource.data ? int(string(request.resource.data.v).split('\\.')[0]) : 1;
          return request.auth.uid == userId
            && resourceIsValid(resourceVersionMajor)
            && resourceVersionIsCompatible(resourceVersionMajor);
        }

        allow read: if request.auth.uid == userId;
        allow delete: if request.auth.uid == userId;
        allow create, update: if creationAllowed();
      }
    }
  }
}
...