juicy89
juicy89

Reputation: 458

Firestore rules - Querying the existence of a document - Insufficent permissions

I want to query a document reference to see whether or not a document currently exists. If it doesn't exist, then I want to write a new document to that reference.

Unfortunately this isn't working with my security rules. If the document did already exists, then I only want authorised persons to be able to view it. My rules look like this

Firestore rules

function getItemData(item){
   return get(/databases/$(database)/documents/equipment/$(item)).data;
}

function getUserData(){
   return get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
}

match /equipment/{item}{
   allow create: if request.auth != null;
   allow read: if !exists(/databases/$(database)/documents/equipment/$(item)) ||
               getUserData().clientId == getItemData(item).clientId;
}

Document structure

users > userId: {
  clientId: "idhere"
}

equipment > item: {
  clientId: "idhere",
  serial: "serialnumber"
}

My react native code where i perform the get request

const docRef = firestore.collection("equipment").where('clientId', '==', clientId).where("serial", "==", serial);
docRef.get().then(doc => {
  if(!doc.exists){
    //add new item to existing document reference
  }
}).catch(error => {
  //Error here reads "Missing or insufficient persmissions"
})

I can read items if I query them directly. However it seems that my where() functions are conflicting with the rules.

Does anyone have any idea why this would happen?

Edit: If I change my rule to this

match /equipment/{item}
   allow read: if true

I'm finding success, so I've definitely confirmed that it is this rule that's causing my problem. When using the simulator, I can access the document directly. It appears that the where statements are the issue

Upvotes: 1

Views: 327

Answers (2)

Doug Stevenson
Doug Stevenson

Reputation: 317392

It looks like you're expecting the rule to check, for each document in the result set, if the contents of other documents suggest that it should be readable. The query is failing because security rules are not filters. Please be sure to understand the documentation here. You can't write a rules that removes documents from the requested result set - a query must be all or nothing.

The rule works for document get() requests because the requested document is known ahead of time - {item} has a specific value. But in the case of a query, the rules aren't able to check each possible document individually. You'll need to find another way of expressing your intent.

Upvotes: 1

Frank van Puffelen
Frank van Puffelen

Reputation: 598740

If you don't want the user to be able to read the existing document, you shouldn't give them read permission. And that means that they also won't be able to detect the document's existence by trying to read it.

But you can likely still reach your use-case by only allow the user create permission if the document doesn't exist yet:

allow create: if request.auth != null && !exists(resource['__name__']) 

Now you can have the client simply try to create the document. If that fails, you know the document already existed (or another security rules wasn't met).

Upvotes: 0

Related Questions