У меня есть две коллекции - аренда и пользователи.
В документе об аренде есть поле "landlordID" типа REFERENCE (не String).
Теперь в моих правилах безопасности Firestore я хочу разрешить обновление аренды, ТОЛЬКО ЕСЛИ поле LandlordID этой аренды совпадает с uid пользователя, делающего запрос, а именно request.auth.uid.
Прочитайте это как «разрешить обновление документа об аренде, если пользователь, создающий пользователя, аутентифицирован, поэтому request.auth.uid != null, а идентификатор поля landlordID должен быть равен идентификатору request.auth.uid.
Следовательно, код должен выглядеть примерно так:
service cloud.firestore {
match /databases/{database}/documents {
match /tenancies/{tenancyID}{
allow update: if request.auth.uid != null &&
request.auth.uid == get(resource.data.landlordID).id
}
}
Я тоже пробовал get(/databases/$(database)/documents/users/$(resource.data.landlordID)).data.id
Поддерживающий скриншот моей базы данных
Это должно быть очень просто, но get() просто не работает. Документы Firebase, прокрутите до «Доступ к другим документам». совсем не помог в моей ситуации, и я не знаю, как заставить его работать.
Было бы обидно, если бы ссылки нельзя было использовать таким образом, поскольку они аналогичны любому другому полю документа.
Это просто опечатка, в правилах безопасности это "ноль". И да, я показываю автономное правило, которое не выполняет запрос на симуляцию, который должен пройти. Прочитайте это как «разрешить обновление документа об аренде, если пользователь, создающий пользователя, аутентифицирован, следовательно, request.auth.uid != null, а идентификатор поля landlordID должен быть равен идентификатору request.auth.uid.
Ваш get() определенно неверен. Вам нужно передать ему полный спецификатор документа, как вы можете видеть в документации.
Смотрите второй ответ и мой ответ. Я пробовал различные версии get с полными путями и т. д., включая версию во втором ответе, и это не сработало. Второй ответ также указывает на то, что, возможно, нельзя использовать такие ссылки, что меня шокирует.

Я вижу здесь пару проблем. Первая проблема заключается в том, что функция get() ожидает полностью указанный путь к документу, например:
get(/databases/$(database)/documents/users/$(resource.data.landlordID)).data.id
Вторая проблема заключается в том, что вы пытаетесь использовать ссылочный тип в своих правилах, к сожалению, я не думаю, что это возможно.
Ссылочный тип в Firestore не очень полезен (пока), я думаю, что вы должны хранить идентификатор лендлорда в виде строки, тогда вы можете просто сделать что-то вроде:
service cloud.firestore {
match /databases/{database}/documents {
match /tenancies/{tenancyID}{
allow update: if request.auth.uid != resource.data.landlordID;
}
}
Я знаю это и пробовал несколько версий того, что я добавлял в get(), включая построение путей с помощью path() и т. д. Это позор, поскольку в этом нет никакого смысла, чтобы не быть полезным. Ссылка аналогична любому типу, и логика моего приложения построена вокруг того, что она не является строковым полем. Трудно понять, какой смысл иметь ссылку на тип, если вы не можете использовать ее в правилах безопасности.
Согласен, было бы неплохо, если бы ссылочный тип был полезнее. Кажется, я читал, что на данный момент единственным вариантом использования ссылочного типа является то, что вы можете щелкнуть по нему в консоли Firestore, чтобы перейти к соответствующему документу.
Я просто подумал, что размещение «строки», представляющей ссылку на другой документ/подколлекцию в другой коллекции, должно быть выполнено с типом ссылки, я считаю, что это логично, иначе в этом нет никакого смысла. В заключение, в этой ситуации нет обходного пути, кроме превращения landlordID в строку в моей «схеме»?
Да, я думаю, что это обходной путь, который вам придется использовать. Я много использую Firestore и всегда использую строки для создания отношений между документами.
У меня была та же проблема, на которую мне нужен был ответ. См. этот гугл-тред с ответом от кого-то из Google. Цитирую:
You can get an id out of a path using the "index" operator:
some_document_refshould look like/databases/(default)/documents/foo/barwhich has 5 segments:
["databases", "(default)", ...]
some_document_ref[4]should be "bar" allow create: if request.resource.data.some_document_ref[4] == "bar";You can also use the normal
getandexistsfunctions on them.A few difficult aspects of this that you may run into:
There's no way to retrieve the number of segments in a path at the moment (we're adding this soon), so you'll need to know some information about the reference ahead of time
There's not great support for writing references using the simulator in the Firebase Console. I used the Firestore emulator to test out this behavior (gist1, gist2)
Вот функция, которую я сделал, которая работает для меня. Я предполагаю, что у вас есть коллекция пользователей, в которой пользователи имеют такие же id, как и их auth.uid.
function isUserRef(field) {
return field in resource.data
&& resource.data[field] == /databases/$(database)/documents/users/$(request.auth.uid)
}
Приспосабливаясь к вашему варианту использования, вы бы назвали функцию так: isUserRef('landlordID') хотя идентификатор в конце немного вводит в заблуждение, поскольку это поле на самом деле является ссылкой.
Для других, которым необходимо получить идентификатор из поля типа ссылки, вам нужно сначала получить документ: get(/databases/$(database)/documents/$(request.resource.data['owner'].path)).id == request.auth.uid Здесь владелец — это ссылка пользователя.
может быть слишком поздно, но я смог собрать воедино (несмотря на отсутствие документов), что ссылка на документ — это просто путь, а полный путь можно создать с помощью
/databases/$(database)/documents/users/$(request.auth.uid)
Затем у меня есть массив/список в firestore ссылок, называемый reads, с помощью которого я могу получить:
get(/databases/$(database)/documents/users/$(userId)/userinfo/granted_users).data.reads
Оставив мне возможность создать логическое значение и правило с:
/databases/$(database)/documents/users/$(request.auth.uid) in get(/databases/$(database)/documents/users/$(userId)/userinfo/granted_users).data.reads
очевидно, ваша структура данных будет различаться, но здесь важно знать, что ссылка — это путь.
Мне пришлось немного поэкспериментировать, чтобы заставить это работать. Вот функция, которая сработала для меня
function isUserRef(database, userId) {
return 'user' in resource.data
&& resource.data.user == /databases/$(database)/documents/users/$(userId);
}
И я называю это так:
match /answers/{answer} {
allow read:
if isUserRef(database, request.auth.uid);
}
Как упоминалось в некоторых других ответах, ссылка имеет свойство path, которое представляет собой просто строку, которая будет выглядеть примерно так users/randomuserid123. Вы можете разделить это на массив и сопоставить его с пользователем, делающим запрос на обновление.
...
match /tenancies/{tenancyID}{
allow update: if request.auth.uid != null &&
resource.data.landlordID.path.split('/') == ['users', request.auth.uid]
}
...
Также возникли проблемы с обработкой этой проблемы, но в моем случае мне нужно было разрешить пользователю добавлять сообщение в чат, только если он является владельцем этого чата. Есть 2 "таблицы" - chats и chat_messages, причем chat_messages относятся к конкретному чату через поле chatId. chats объекты имеют ownerId поле.
Правило, которое я использовал, звучит так:
// Allow adding messages into a chat if the user is an owner of the chat room
match /chat_messages/{itemId} {
function isOwner() {
return get(/databases/$(database)/documents/chats/$(request.resource.data.chatId)).data.ownerId == request.auth.uid;
}
allow read: if true;
allow create: if isOwner();
}
Вы показываете настоящие правила, которые не работают так, как вы ожидаете? Если нет, отредактируйте вопрос, чтобы точно показать, что вы делаете. Как видно, я ожидаю, что они не будут компилироваться, потому что нет
nill. Вы имели в видуnull?