Jeremy W
Jeremy W

Reputation: 153

Firestore Match Rules for looking up data in a document that is in another collection

I am having an issue with Firestore rules when the permission is stored in another document in another collection. I haven't been able to find any examples of this, but I have read that it can be done.

I want to do it this way to avoid having to do a lot of writes when a student shares his homework list with many other students. Yes, I know this counts as another read.

I have 3 collections, users, permissions, and homework along with some sample data.

users

{
    id:  fK3ddutEpD2qQqRMXNW,
    name: "Steven Smith"
},

{
    id:  YI2Fx656kkkk25,
    name: "Becky Kinsley"
},

{
    id:  CAFDSDFR244bb,
    name: "Tonya Benz"
}

permissions

{
    id:  fK3ddutEpD2qQqRMXNW,
    followers: [YI2Fx656kkkk25,CAFDSDFR244bb]  
}

homework

{
    id:  adfsajkfsk4444,
    owner:  fK3ddutEpD2qQqRMXNW,
    name: "Math Homework",
    isDone: false
}

The start of my firestore rules:

 service cloud.firestore {

//lock down the entire firestore then open rules up.
  match /databases/{database}/documents {

    match /{document=**} {
      allow read, write: if false;
    }

match /homework/{      } {
      allow get: if isSignedIn()

    }

// helper functions
    function isSignedIn() {
      return request.auth != null;
    }

    function isUser(userId) {
      return request.auth.uid == userId;
    }

    function isOwner(userId) {
        return request.auth.uid == resource.data.uid;
    }

  }
}

Use case:

Steven Smith Shared his homework list with Tonya Benz.

Tonya Benz is logged into the app to view her friend Steven's homework. The app runs this query to get the homework list of Steven Smith.

var homeworkRef = db.collection("homework");
var query = homeworkRef.where("owner", "==", "fK3ddutEpD2qQqRMXNW");

Question:
What is the proper Firestore match rule that takes the "owner" field from the homework collection to look up it up as the id in the permissions collection when the user Tonya Benz is signed in so this query can run.

Upvotes: 1

Views: 593

Answers (1)

Doug Stevenson
Doug Stevenson

Reputation: 317868

With your current query and database structure, you won't be able to achieve your goal using security rules.

Firstly, it sounds like you're expecting to be able to filter the results of the query based on the contents of another document. Security rules can't act as query filters. All the documents matched by the query must be granted read access by security rules, or the entire query is denied. You will need to come up with a query that is specific about which documents should be allowed access. Unfortunately, there is no single query that can do this with your current structure, because that would require a sort of "join" between permissions and homework. But Firestore (like all NoSQL databases), do not support joins.

You will need to model your data in such a way that is compatible with rules. You have one option that I can think of.

You could store the list users who should have read have access to a particular document in homework, within that same document, represented as a list field. The query could specify a filter based on the user's uid presence in that list field. And the rule could specify that read access only be granted to users whose IDs are present in that list.

{
    id:  adfsajkfsk4444,
    owner:  fK3ddutEpD2qQqRMXNW,
    name: "Math Homework",
    isDone: false,
    readers: [ 'list', 'of', 'userids' ]  // filter against this list field
}

The bottom line here is that you'll need to satisfy these two requirements:

  1. Your query needs to be specific about exactly which documents that it expects to be readable. You can't use a rule to filter the results.
  2. Your rule needs a way to determine, using nothing more complicated than the fields of the document itself, or a get() on other known documents, what the access should be for the current uid.

Upvotes: 1

Related Questions