#javascript #google-cloud-firestore #firebase-security
#javascript #google-облако-firestore #firebase-безопасность
Вопрос:
В моем приложении для чата 1-1, использующем Firestore, 2 участника будут читать / записывать документы в msgs
вложенной коллекции, где каждый документ представляет собой одно сообщение.
Но это сбой с отказом в разрешении из-за правил безопасности.
Структура БД :
chatRooms/
${roomId}/
msgs/
${msgId}
Я хочу добавить правила безопасности, чтобы только соответствующие 2 участника могли читать / записывать во msgs
вложенную коллекцию своего roomId
документа
Я написал следующие правила безопасности.
Это позволяет участникам создавать новый документ (т. Е. Отправлять сообщения) в msgs
вложенной коллекции.
Но пользователи не могут прочитать эту msgs
вложенную коллекцию.
match /chatRooms/{roomId} {
allow read: if request.auth.uid == resource.data.userA.id ||
request.auth.uid == resource.data.userB.id;
allow create: if request.auth.uid == request.resource.data.userA.id ||
request.auth.uid == request.resource.data.userB.id;
match /msgs/{msgId} {
allow read: if request.auth.uid == resource.data.sender._id ||
request.auth.uid == resource.data.partner._id;
allow create: if request.auth.uid == request.resource.data.sender._id ||
request.auth.uid == request.resource.data.partner._id;
}
}
Код для чтения :
const roomId = `${userAId}_${userBId}`;
const query = db.collection('chatRooms').doc(roomId).collection('msgs')
.orderBy('createdAt', 'desc')
.startAfter(lastVisible)
.limit(10)
query.get()
Если я изменю правило чтения, как показано ниже, чтение и запись будут работать полностью нормально.
Таким образом, мы можем указать, что проблема заключается только в правиле чтения msgs
вложенной коллекции. (И не в его родительской коллекции / правилах doc)
match /msgs/{msgId} {
allow read: if request.auth != null;
allow create: if request.auth.uid == request.resource.data.sender._id ||
request.auth.uid == request.resource.data.partner._id;
}
Обновить :
Фрэнк предложил 3 решения, и я попробовал два из них (1 и 3), но оба не сработали для меня.
Для решения 1 я добавил ids
массив с идентификаторами участников.
Сообщение о записи (добавление нового документа) работало с тем же правилом безопасности.
Операция чтения выглядит следующим образом :
И это правило безопасности :
ОБНОВЛЕНИЕ 2
Это сработало после исправления правила безопасности, о чем Фрэнк упомянул в комментарии в своем ответе. (поскольку этот комментарий находится намного ниже, я добавляю его и здесь)
match /msgs/{msgId} {
allow read: if request.auth.uid in resource.data.ids;
allow create: if request.auth.uid in request.resource.data.ids;
}
ОБНОВЛЕНИЕ 3
Решение 1 требовало создания индекса (который я orderBy
также использую), что стоит дополнительно. Итак, я также попробовал решение Фрэнка 3, но оно возвращает ошибку «отказано в разрешении».
Вероятно, запрос на чтение неверен и требует некоторого фильтра, но я не могу добавить фильтр для $roomId
данных документа ( userA.id
/ userB.id
), как того требуют правила, при чтении из msgs
коллекции.
Правило :
match /chatRooms/{roomId=**} {
allow read: if request.auth.uid == resource.data.userA.id ||
request.auth.uid == resource.data.userB.id;
allow create: if request.auth.uid == request.resource.data.userA.id ||
request.auth.uid == request.resource.data.userB.id;
allow update: if request.auth.uid == resource.data.userA.id ||
request.auth.uid == resource.data.userB.id;
}
Запрос на чтение :
const query = dbFirestore.collection('chatRooms').doc(roomId).collection('msgs')
.orderBy('createdAt', 'desc')
.limit(10)
query.onSnapshot( ... )
Комментарии:
1. Пожалуйста, не публикуйте скриншоты вашего кода или другого текстового содержимого. Вместо этого опубликуйте фактический текст и используйте инструменты форматирования Stack Overflow для его разметки.
Ответ №1:
Правила безопасности не фильтруют данные. Вместо этого они просто проверяют, разрешена ли операция чтения.
Поэтому, если вы хотите разрешить пользователю читать только сообщения, частью которых они являются, вам придется выполнить запрос, который выбирает эти сообщения.
Например: вы можете выбрать все сообщения, отправителем которых является пользователь, с помощью:
db.collection('chatRooms').doc(roomId).collection('msgs')
where('sender._id', '==', firebase.auth().currentUser.uid)
И теперь правила безопасности проверят, разрешен ли этот запрос, и вернут соответствующие сообщения.
Затем проблема заключается в том, что вы не можете выполнить условие ИЛИ для полей sender._id
и partner._id
. Для этого есть несколько решений:
-
Сохраните пользовательские идентификаторы участников для каждого сообщения в поле массива (скажем
participants
), а затем используйтеarray-contains
для запроса:db.collection('chatRooms').doc(roomId).collection('msgs') where('participants', 'array-contains', firebase.auth().currentUser.uid)
А затем закрепите это в правилах с помощью чего-то вроде:
request.auth.uid in resource.data.participants
-
Смоделируйте безопасность для комнаты на
/chatRooms/$roomId
уровне с помощью поиска вmsgs
вложенной коллекции. Для этого требуется два дополнительных получения, но в остальном все намного прощеmatch /msgs/{msgId} { allow read: if request.auth != null amp;amp; (get(/databases/$(database)/documents/chatRooms/$(roomId)).data.userA.id == request.auth.uid || get(/databases/$(database)/documents/chatRooms/$(roomId)).data.userB.id == request.auth.uid);
-
Пусть доступ
/msgs
будет унаследован из комнаты:match /chatRooms/{roomId=**} { allow read: if request.auth.uid == resource.data.userA.id || request.auth.uid == resource.data.userB.id;
Комментарии:
1. Спасибо @Frank, ваше объяснение очень полезно. Я думаю, что я пойду с первым подходом.
2. Извините, я хотел сначала протестировать решения, а затем отметить их решаемыми. 3-е решение не сработало для меня, поэтому я попробовал первое. Но это тоже не сработало 🙁 ——— Прочитанный оп:
query = db.collection('chatRooms').doc(roomId).collection('msgs').where('ids','array-contains',firebase.auth().currentUser.uid)
———— правило:match /msgs/{msgId} { allow read: if request.auth.uid == resource.data.ids[0] || request.auth.uid == resource.data.ids[1]; }
——— хотя write работал с тем же правилом, и я подтвердилids
массив с идентификаторами участников в сгенерированном документе3. Пожалуйста, обновите свой вопрос новым запросом, правилами и скриншотом документа. Слишком сложно прочитать это в комментариях.
4. Эти сравнения массивов не будут работать, так как запрос выполняется
array-contains
, в то время как правила выполняют две==
проверки. Я бы ожидалrequest.auth.uid in resource.data.ids
.5. Хммм… Я действительно не уверен, работает ли это в конце концов. В основном: как правила будут проверять операцию чтения из передаваемой вами информации. Так что это может быть невозможно с подходом 3.