Reputation: 1451
Firestore has a DocumentReference type, which is a "pointer" to another firestore document. Using the firebase JavaScript client, you can access properties (e.g. document "id"), directly on the reference.
For example, if there is a document with a docRef
property that is a firestore DocumentReference:
const retrievedDoc = await getFirestoreDocument();
console.log(retrievedDoc.docRef.id); // "jRmSeMYDMKiOPGsmkdaZ"
I am trying to accomplish the same thing within firestore rules. There is a custom function named isOwner
. It uses the firestore rules get
call on a document path, and then attempts to access the docRef.id
just as if it were the JavaScript client above.
get(/databases/$(database)/documents/path/to/$(id)).data.docRef.id
The value of the document's id is compared against the current user's. But when I test this using the simulator and in real code, it fails. I feel like this should work, but it doesn't.
What does work is to store and use the id value directly as a string (e.g. get(/path/id).docId
) instead of a DocumentReference.
Should I be able to access the id
value of a DocumentReference within the firestore rules? Am I doing something wrong?
I want to avoid doing a second document get
within the rule as described in this SO answer. That's a second "read" for each trigger of this rule. And I don't think the document id (which is what I need) will be available on the get
call anyway.
Upvotes: 25
Views: 5441
Reputation: 304
Update
Actually, a reference is a Path
object in Firestore rules as documented here. So you access the id by the index of the part of the path you need.
In this example I use the incoming document's data which has a reference object to lookup a property on another document from a get()
match /databases{database}/documents {
match /contacts/{contact} {
allow create: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.relatedRules[request.resource.data.relation.path[4]].canBeRelated
// the [4] assumes the path to be `databases/$(database)/documents/contacts/contactId`
// your exact index would vary for your data structure
}
}
First Answer
This only works in the Firestore dashboard rules simulator, it is not a working example for either the local emulation or production Firestore.
This is a year old but I encountered this same puzzling issue, but not on the data from a get()
, just on the data of the request.resource.data
. I'm not sure what ought to be available (not even __name__
is available) in the rules but if you're accessing a resource reference on the data and you have a predictable id size (say, 20 characters) you could simply get the range of the path
on the resource to check against
match /databases{database}/documents {
match /contacts/{contact} {
allow create: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.relatedRules[request.resource.data.relation.path[9:29]].canBeRelated
// the [9:29] assumes the path to be `/contacts/20characterLongIdStr`
// your exact range would vary for your data structure
}
}
Feels like a resource reference object should have at least the id since the path is there. It appears Firestore won't support this for whatever reason.
Upvotes: 1
Reputation: 253
Based on documentation:
get()
method is supposed to returns a Resource object which is supposed to contains a .id
property (as well as .data
).
For example, to restrict write
access to an authenticated user which is the author of a book document (authors documents are identified with the user uid), you would do:
service cloud.firestore {
match /databases/{database}/documents {
match /books/{document=**} {
allow write: if get(resource.data.authorReference).id == request.auth.uid;
}
}
}
Yet I'm always having the error property id is undefined on object
on trying.
.data
is accessible so I suppose there is an issue in the api.
Upvotes: 12