Search code examples
firebasegoogle-cloud-firestorefirebase-security

How do I access the document ID in firestore rules


I am seemingly unable to access the resource.id value when trying queries using these rules. when I manually enter the schools id (the commented out line) the data returns fine. I only have 1 school and the doc ID definitely matches the string. but when I ask to match to the resource.id value, my rules return an 'insufficient permissions' error.

    rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
            //functions
        function signedIn() {
        return request.auth.uid != null;
            }
        
        function returnUID(){
        return request.auth.uid;
        }
  
  function getUserData() {
    return get(/databases/$(database)/documents/All%20Users/$(request.auth.uid)).data;
}
  
  
                match /All%20Users/{userID} {
          allow read,write: if 
          signedIn() && returnUID() == userID;
          }
  
          match /All%20Schools/{schoolID}{
            allow read, write: if
         //  signedIn() && getUserData().school == "f7asMxUvTs3uFhE08AJr"
             signedIn() && getUserData().school == resource.id
          }
          
          
    
  }
}

Image of Data Hierarchy

my structure is like this

All Schools / school (document) / Classrooms (subcollection) All Users / User (document) (each user doc has a classroomID associated to it)

as a point of reference this is a query that is successful

var docRef = db.collection("All Users").doc(uid).get()

and the one that is failing

db.collection("All Schools/" + properties.schoolid + "/Classrooms").onSnapshot()

[update] the working set of rules!

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
            //functions
        function signedIn() {
        return request.auth.uid != null;
            }
        
        function returnUID(){
        return request.auth.uid;
        }
  
  function getUserData() {
    return get(/databases/$(database)/documents/All%20Users/$(request.auth.uid)).data;
}
  
  
                match /All%20Users/{userID} {
          allow read,write: if 
          signedIn() && returnUID() == userID;
          }
  
          match /All%20Schools/{schoolID}{
            allow read, write: if   schoolID == 'f7asMxUvTs3uFhE08AJr'
           
          }
          match /All%20Schools/{schoolID}/Classrooms/{classId} {
  allow read, write: if getUserData().school == schoolID;
}
          match /All%20Schools/{schoolID}/Student%20List/{student} {
  allow read, write: if getUserData().school == schoolID;
}
    match /All%20Schools/{schoolID}/Staff/{staff} {
  allow read, write: if getUserData().school == schoolID;
}
  }
}

Solution

  • The following rules will be effective on documents of 'All Schools' collection only and not documents of 'Classrooms' sub-collection:

    match /All%20Schools/{schoolID} {
      // ...
    }
    

    That's why db.collection("All Users").doc(uid).get() works and fetching 'Classrooms' collection fail since you do not have any rules specified for it. Although you had a recursive wildcard earlier (before editing the question), resource object contains data of those documents being matched in 'Classrooms' sub-collection and hence getUserData().school == resource.id failed too.

    That being said, try specifying rules for 'Classrooms' sub-collection as well:

    match /All%20Schools/{schoolID}/Classrooms/{classId} {
      allow read, write: if getUserData().school == schoolID;
    }