Получение отсутствующих или недостаточных разрешений с помощью правил get () и exists () для запроса

#android #firebase #web #google-cloud-firestore #firebase-security

#Android #firebase #веб #google-облако-firestore #firebase-безопасность

Вопрос:

Мы попытались перечислить все документы из коллекции «события». ( /event/{eventId} ) в документе каждого события существует свойство ‘users’ (массив) со всеми идентификаторами пользователей в событии. У нас есть другая коллекция ‘role’ с ролью каждого пользователя для каждого события. ( /role/{eventId}/userRole/{userId} )

У нас есть разрешение на получение одного события с

 db.collection('event').doc(eventId).get()
  

Но когда мы попытались (с Android и Web) выполнить запрос, подобный

 db.collection('event').where("users", "array-contains", user.uid).get()
  

мы получили FirebaseError: Missing or insufficient permissions.

Правила события :

     match /event/{eventId} {
      allow read:
        if isAuth() amp;amp; 
        inEvent(eventId, request.auth.uid);
      allow create: 
        if isAuth() amp;amp;
        checkEventName() amp;amp;
        isOwner(database) amp;amp;
        request.resource.data.users == [];
      allow update:
        if isAuth() amp;amp;
        resource.data.ownerId == request.auth.uid amp;amp;
        checkEventName() amp;amp;
        isOwner(database) amp;amp;
        request.resource.data.mediaCount == resource.data.mediaCount amp;amp;
        request.resource.data.users == resource.data.users;
      allow delete:
        if false;
    }
  

Правила роли :

     match /role/{roleId} {
      allow read: 
        if isAuth();
      allow write:
        if false;

      match /userRole/{userRoleId} {
        allow read:
          if isAuth() amp;amp;
          userRoleId == request.auth.uid;
        allow create:
          if isAuth() amp;amp;
          userRoleId == request.auth.uid amp;amp; 
          exists(/databases/$(database)/documents/event/$(roleId)) amp;amp;
          request.resource.data.actual is number amp;amp;
          request.resource.data.actual >= 0 amp;amp;
          request.resource.data.actual <= 10 amp;amp;
          request.resource.data.previous is number amp;amp;
          request.resource.data.actual == request.resource.data.previous;
        allow update: 
          if isAuth() amp;amp;
          userRoleId == request.auth.uid amp;amp;
          request.resource.data.actual == 0 amp;amp;
          resource.data.actual != 0;
        allow delete: 
          if false;
      }
    }
  

Идентификатор роли соответствует идентификатору события.

В правиле чтения события у нас есть inEvent(EventID, request.auth.uid). Когда мы пишем

 function inEvent(eventID, userID) {
      return true;
}
  

запрос работает, но если мы попробуем что-то вроде

 return exists(/databases/$(database)/documents/role/$(eventID)/userRole/$(userID));
  

или

 return get(/databases/$(database)/documents/role/$(eventID)/userRole/$(userID)).data.actual >= 10;
  

мы всегда получаем ошибку разрешения

Мы проверили все данные и правила, но мы не понимаем, почему мы можем получить отдельное событие, но не список. Что мы делаем не так? Можем ли мы использовать get () и exists () для разрешений списка?

Ответ №1:

Правила безопасности Firebase применяются при первом подключении прослушивателя. В этот момент сервер должен иметь возможность подтвердить, что операция чтения навсегда вернет только те документы, на получение которых у вас есть разрешение. В ваших текущих правилах это потребовало бы, чтобы сервер прочитал каждый events документ, а затем проверил роль пользователя для каждого документа. Это не масштабируемая операция, поэтому сервер не разрешает ее и отклоняет чтение.

Обычно это означает, что информация, которую вы хотите защитить, должна присутствовать в документе, к которому вы пытаетесь разрешить доступ, (или во вложенной коллекции), или в фиксированном расположении для текущего пользователя. Лучшее, что я могу придумать для вашего случая, — это иметь ACL под событием, которое считывается, хотя даже тогда я не совсем уверен, можете ли вы смоделировать его так, чтобы сервер мог статически оценивать.

Также смотрите:

Комментарии:

1. Спасибо за эти пояснения. Мы попытаемся переместить роли в коллекции событий и посмотрим, исправит ли это изменение нашу проблему

2. Мы перемещаем роли в role map в документах событий, таких как здесь : firebase.google.com/docs/firestore/solutions/role-based-access И теперь все работает нормально, проблема решена

3. Я все время забываю, что у нас есть страница документации для этого. Хорошая находка! И приятно слышать, что вы смогли перенести роли в сам документ. Это не только заставляет его работать, но и экономит количество прочитанных документов. 🙂