wtk
wtk

Reputation: 1451

Firestore Access Rules that Rely on a Document Reference

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

Answers (2)

Charles Harris
Charles Harris

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

Akabab
Akabab

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

Related Questions