Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firestore Rules : Property is undefined on object

In my local environement using the firebase emulator, I have the following rule that raises a FirebaseError: Property managers is undefined on object. for 'list':

rules_version = '2';
service cloud.firestore {
    match /databases/{database}/documents {
        match /businesses/{business} {
            allow read: if request.auth != null && request.auth.uid in resource.data.managers;
        }
    }
}

If I test the same rule on the rules Playground, it works correctly (Simulated read allowed)..

To prove I have the managers property defined and available, if I change the rule to if true, I can loop on the list with the following local data:

// code
...
.then((snap) => snap.forEach((doc) => console.log(doc.id, " => ", doc.data())))

// output
VzaU8rX8HOgejk0fyOFK  =>  {..., managers: Array(1)}

What am I doing wrong?

Thank you !

UPDATE

This is the code I would like to have the rule applied on:

    useEffect(() => {
        db.collection("businesses")
            .where("slug", "==", params.slug)
            .get()
            .then((snap) => {
                if (!snap.empty) {
                    // slug is supposed to be unique, so I retrieve the first and only element
                    const doc = snap.docs[0]
                    setBusiness({ id: doc.id, data: doc.data() })
                    setState({ ...state, loading: false, data: true })
                } else {
                    setState({ ...state, loading: false, error: "Business not found" })
                }
            })
            .catch((error) => {
                console.log("Error getting document:", error)
                setState({ ...state, loading: false, error: "Error getting document" })
            })
    }, [])
like image 672
Binajmen Avatar asked Jun 14 '20 10:06

Binajmen


2 Answers

Your rule works in the console simulator because it's only testing the read of a single docyment at a time. Your client code, however, is performing a query for multiple documents, which is different. One very important thing to know is that security rules are not filters (be sure to read that, and this). Security rules will not remove documents from the result set if the rule would reject a document. Instead, the query must be able to fetch all document allowed by the rule, or the rule will reject the query entirely.

A query must explicitly have the same filter as required by the rules. Your rule requires that the query only filter for documents where the current user is in the managers array field. Therefore, it the query must be more like this:

db.collection("businesses")
    .where("slug", "==", params.slug)
    .where("managers", "array-contains", uid)

Where uid is the current user's uid.

like image 110
Doug Stevenson Avatar answered Sep 28 '22 05:09

Doug Stevenson


At first, Could you try db.collection("businesses").get("VzaU8rX8HOgejk0fyOFK");?

Secondly, Did you use array-contains, in, or array-contains-any as where() method comparison operation?

If you not, Coud you try the following code?

ex.

db.collection("businesses").where("managers", "in", uid)
    .get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
        });
    })
    .catch(function(error) {
        console.log("Error getting documents: ", error);
    });
like image 37
zkohi Avatar answered Sep 28 '22 05:09

zkohi