简体   繁体   中英

Firestore document query allowed with get() but denied using where()

I am unit testing the firestore security rules below which allows a member of a group to read the groups collection if their userId is a key in the roles map.

function isMemberOfGroup(userId, groupId) {
    return userId in get(/databases/$(database)/documents/groups/$(groupId)).data.roles.keys();
}

match /groups/{groupId} {
    allow read: if isMemberOfGroup(request.auth.uid, groupId);
}

Sample groups collection

{
    name: "Group1"
    roles: {
        uid: "member"
    }
}

My unit test performs a get() of a single document which succeeds.

It then performs a get() using a where() query which fails.

it("[groups] any member of a group can read", async () => {
    const admin = adminApp({ uid: "admin" });
    const alice = authedApp({ uid: "alice" });

    // Groups collection initialisation
    await firebase.assertSucceeds(
        admin.collection("groups")
        .doc("group1")
        .set({ name: "Group1", roles: { alice: "member" } })
    );

    // This succeeds
    await firebase.assertSucceeds(
        alice.collection("groups")
        .doc("group1")
        .get()
    );

    // This fails
    await firebase.assertSucceeds(
        alice.collection("groups")
            .where(new firebase.firestore.FieldPath('roles', 'alice'), '==', "member")
            .get()
        );
    });

It fails with the error:

FirebaseError: 
Null value error. for 'list' @ L30

where L30 points to the allow read security rule

I know that firestore rules are not responsible for filtering and will deny any request that could contain documents outside of the rule. However from what I understand, my where() should be limiting this correctly.

Is there a problem with my where() query or my rules?

The problem is still that security rules are not filters. Since your rule depends on a specific document ID (in your case, groupId ), that value can never be used in the rule as part of a comparison. Since rules are not filters, the rule must work for any possible value of groupId without knowing it an advance , since the rule will not get() and check each individual group document.

The security check you have now will simply not work for queries for this reason. Instead, consider storing a list of per-user group access in custom claims or a single document that the user can read, so they can then iterate the list and get() only the specific groups they've been told they have access to.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM