Firestore安全规则:无法理解为什么某些规则不起作用

时间:2018-03-20 15:27:42

标签: firebase google-cloud-firestore firebase-security

我的firestore数据库的结构使得“leagues”是顶级集合,每个联盟包含一个以每个已批准的userID(带有数字值)命名的字段。

每个联盟还有一个子集“用户”的文件,以每个批准的用户ID命名。

以下是一个示例firestore查询:

FirebaseAuth mAuth = FirebaseAuth.getInstance();

// userId = ABCDEF123 for this example
String userId = mAuth.getCurrentUser().getUid();

FirebaseFirestore firestore = FirebaseFirestore.getInstance();
firestore.collection("leagues").whereLessThan(userId, 99).get();

有人可以告诉我为什么这条规则有效:

match /leagues/{league} {
    allow read, write: if resource.data.ABCDEF123 != null;

但不是这样:

match /leagues/{league} {
    allow read, write: if resource.data.request.auth.uid != null;

此外,为什么这条规则有效:

//"ZYXWV987" is an example of a league the user is in
match /leagues/{league} {
    allow read, write: if exists(/databases/$(database)/documents/leagues/$('ZYXWV987')/users/$(request.auth.uid));

但不是这样:

match /leagues/{league} {
    allow read, write: if exists(/databases/$(database)/documents/leagues/$(league)/users/$(request.auth.uid));

我得到的错误是“com.google.firebase.firestore.FirebaseFirestoreException:PERMISSION_DENIED:缺少权限或权限不足”

我希望了解这些规则的工作原理,以及如何为我的数据库实现适当的规则!

修改

我现在意识到这有效(仍然是WIP):

match /leagues/{league} { 
    allow read, create, update: if request.auth.uid != null; 

    //only ever deleting a single league at a time 
    allow delete: if exists(/databases/$(database)/documents/leagues/$(league)/users/$(request.auth.uid));

    match /{docs = **} { 
        allow read, write: if exists(/databases/$(database)/documents/leagues/$(league)/users/$(request.auth.uid))} 
}

我有点理解发生了什么(在请求中读/写潜在的多个联盟时我不能使用{联盟}通配符?),但我仍然不确定为什么?

1 个答案:

答案 0 :(得分:2)

Can someone please tell me why this rule works:

match /leagues/{league} {
    allow read, write: if resource.data.ABCDEF123 != null;

but not this:

match /leagues/{league} {
    allow read, write: if resource.data.request.auth.uid != null;

I guess there is no field named request in resource.data. How should the dot syntax know that you want to evaluate the last part (request.auth.uid) first and use the result as the next key? I'm not sure but you could try resource.data[request.auth.uid] instead.

Also, why does this rule work:

// "ZYXWV987" is an example of a league the user is in 
match /leagues/{league} {
    allow read, write: if exists(/databases/$(database)/documents/leagues/$('ZYXWV987')/users/$(request.auth.uid));

but not this:

match /leagues/{league} {
    allow read, write: if exists(/databases/$(database)/documents/leagues/$(league)/users/$(request.auth.uid));

From the docs on security rules:

Every database request from a Cloud Firestore mobile/web client library is evaluated against your security rules before reading or writing any data.

The first rule is evaluated and depending on the result, the user is allowed to read ALL or NONE league documents. The second rule would have to be evaluated for every single document depending on the content. That is not possible before reading them all.

You have to define your security rules in a way that Firestore can evaluate them based only on the definition of your query independent of the possible result.