在这种情况下编写Firebase规则的最佳做法是什么?

时间:2018-04-04 14:51:34

标签: firebase firebase-realtime-database vue.js firebase-authentication authorization

初始方法/问题

用户可以写入自己的用户节点。然后,用户必须能够编写他/她想要的多个建筑物以及他/她想要的 depts (还有房间但是为了清楚起见,我现在暂时把它留在一边)。用户应该能够读取(并写入)他自己的用户节点,建筑物和部门,而不能读取其他用户的节点,建筑物和部门。

基本上:

用户>用户的建筑>建筑部门(TOTAL读写权限)

用户>另一个用户的东西(根本没有权限)

这是数据库结构:

{
  "buildings" : {
    "-L9Bc9aazn3mNiW1elJk" : {
      "address" : "",
      "comments" : "",
      "hasDepts" : {
        "-L9FwBmYEnkZQzdFJ4lU" : true
      },
      "name" : "J house",
      "ownerID" : "6hwNde08Wuaa9bfReR28niSbOsF3"
    }
  },
  "depts" : {
    "-L9FwBmYEnkZQzdFJ4lU" : {
      "comments" : "",
      "inBuilding" : "-L9Bc9aazn3mNiW1elJk",
      "name" : "Dep 1"
    },
  },
  "users" : {
    "6hwNde08Wuaa9bfReR28niSbOsF3" : {
      "isAdmin" : {
        "-L9Bc9aazn3mNiW1elJk" : true,
      }
    }
  }

初步方法

每个用户节点都有一个子节点 isAdmin ,其中包含此用户创建的建筑物的所有推送键。使用相同的逻辑,建筑物'节点包含 hasDepts 节点,其中包含用户在该建筑物中创建的 depts 的所有推送键

如果你能提供帮助我真的很感激。在那里有人吗?

我正在使用vue.js写入firebase:

addBuilding: function () {
  let userId = firebase.auth().currentUser.uid;
  let buildingKey = buildingsRef.push().key;
  this.newBuilding.ownerID = userId;
  buildingsRef.child(buildingKey).set(this.newBuilding);
  usersRef.child(userId).child('isAdmin').child(buildingKey).set(true);
}

目前为止最近的解决方案/ @AndréKool(部分使用初始方法)

"users": {
  "$uid": {
    ".read": "$uid === auth.uid",
    ".write": "$uid === auth.uid"
  },
}, 
"buildings": {
    "$pushKey" : {
        ".read": "root.child('buildings').child($pushKey).child('ownerID').val() === auth.uid",
        ".write": "!data.exists() || root.child('buildings').child($pushKey).child('ownerID').val() === auth.uid" 
  }
},
"depts": {
  "$pushKey": {
    ".read": "root.child('buildings').child(root.child('depts').child($pushKey).child('inBuilding').val()).child('ownerID').val() === auth.uid",
        ".write": "root.child('buildings').child(root.child('depts').child($pushKey).child('inBuilding').val()).child('ownerID').val() === auth.uid" 

  }
},

}

使用@AndréKool的尝试,模拟器允许它读/写 buildings / $ pushKey 节点。然而,虽然它一旦创建就在前端显示节点,但当我们刷新浏览器或添加新建筑时,节点将从前端消失(保留在数据库中)。 Firebase也不允许写入 depts 节点。任何线索和可能的解决方案?

更新:另一种方法。

1)确保建筑物 depts 节点都具有 ownerId 子节点,如下所示:

{
  "buildings" : {
    "-L9HIbKu5fIe8rfoePgi" : {
      "address" : "",
      "comments" : "",
      "hasDepts" : {
        "-L9HIdScisDItysCnMlm" : true
      },
      "name" : "building 1",
      "ownerID" : "6hwNde08Wuaa9bfReR28niSbOsF3"
    }
  },
  "depts" : {
    "-L9HIdScisDItysCnMlm" : {
      "comments" : "",
      "inBuilding" : "-L9HIbKu5fIe8rfoePgi",
      "name" : "dep 1",
      "ownerID" : "6hwNde08Wuaa9bfReR28niSbOsF3"
    }
  },
  "users" : {
    "6hwNde08Wuaa9bfReR28niSbOsF3" : {
      "isAdmin" : {
        "-L9HIbKu5fIe8rfoePgi" : true
      },
      "name" : "João Alves Marrucho",
      "userEmail" : "joaomarrucho@hotmail.com"
    }
  }
}

2)使用 ownerId 授权所有建筑物和部门的读写:

"users": {
      "$uid": {
        ".read": "$uid === auth.uid",
        ".write": "$uid === auth.uid"
      },
    }, 
    "buildings": {
      "$id": {
        ".read": "data.child('ownerID').val() == auth.uid" , 
        ".write": "data.child('ownerID').val() == auth.uid"  
      }
    },
    "depts": {
      "$id": {
        ".read": "data.child('ownerID').val() == auth.uid" , 
        ".write": "data.child('ownerID').val() == auth.uid"  
      }
    },
  }

3)这种方法的新问题!

根据上述规则,使用模拟器,在我看来,firebase不允许用户读/写建筑物节点,但它允许读/写建筑物/ $ pushKey 节点。 firebase是否需要用户能够读取/写入父(建筑物)和子节点( buildings / $ pushKey )?如果是这样,你怎么能阻止用户删除(.set)整个建筑物节点?

我问这个是因为如果我们在 $ wildcards 之前添加“。read”:true,“。write”:true 它会写出预期的数据库结构虽然渲染级联上的下一个规则完全无用 ...所以这不好,但至少它提示解决方案的一部分可能存在。

 "users": {
      ".read": true, <<<<<<
      ".write": true,   <<<<<<
      "$uid": {
        ".read": "$uid === auth.uid",
        ".write": "$uid === auth.uid"
      },
    }, // but is should also be able to write to his own buildings
    "buildings": {
      ".read": true,   <<<<<<
      ".write": true,   <<<<<<
      "$id": {
        ".read": "data.child('ownerID').val() == auth.uid" , 
        ".write": "data.child('ownerID').val() == auth.uid"  
      }
    },
    "depts": {
      ".read": true,   <<<<<<
      ".write": true,   <<<<<<
      "$id": {
        ".read": "data.child('ownerID').val() == auth.uid" , 
        ".write": "data.child('ownerID').val() == auth.uid"  
      }
    },
  }
  

(其他想法)   我不明白为什么用户不能读/写建筑物父节点(前提是规则阻止他删除整个建筑物节点并且仅限授予他完全访问他创建的 buildings / $ pushKeys 的权限。那复杂吗?我可能会弄错,但是在知道哪一个属于用户之前,firebase是否需要扫描建筑物节点?   如果firebase规则无法像这样解决这个问题,这意味着理论上,用户需要在其中读取自己的内容并将其自己的内容写入实时数据库的每个firebase应用程序(通常都是这种情况)需要将信息存储在一个 users.uid 节点,使其可供他/她使用。这似乎违背了“保持你的数据库尽可能平坦”的firebase一般指令,并且它也不能很好地解决这样一个事实:任何带有firebase子项的数据库引用的函数都需要data.snapshots噩梦迭代。   此外,如果您无法设计浅层结构,并且用户ID将不断更改,您将如何在Apps firebase配置中编写数据库引用: const buildingsRef = db.ref('users /'+ < strong> userId !!!! +'/ buildings'); :&gt; How to constrain read/write rules to the users that create the nodes while keeping this structure?   必须有一个好的方法来做到这一点而不会向后弯腰。   ?

1 个答案:

答案 0 :(得分:2)

以下是数据结构各部分的规则:

对于users节点,我删除了更多的全局读写规则,因为如果它们为false,则它们是冗余的,因为这是默认状态。如果它们是真的,他们将覆盖这些规则,因为rules cascade。允许用户只读/写自己的数据:

"users": {
  "$uid": {
    ".read": "$uid === auth.uid",
    ".write": "$uid === auth.uid"
  }
}

接下来是建筑物节点。在这里你可以阅读如果你是所有者(ownerID ===你的uid)并写下there is no data或你是所有者:

"buildings" : {
    "$pushKey" : {
        ".read": "root.child('buildings').child($pushKey).child('ownerID').val() === auth.uid",
        ".write": "!data.exists() || root.child('buildings').child($pushKey).child('ownerID').val() === auth.uid"
    }
}

最后是部门节点,它有点棘手,因为用户只能读/写他所拥有的建筑物中的部门。所以我们必须检查用户是否有一个带有部门推送键的建筑物。在这里,我检查部门中具有inBuilding值的建筑物是否具有属于您的uid的所有者ID(令人头晕目眩):

"depts" : {
    "$pushKey" : {
        ".read": "root.child('buildings').child(root.child('depts').child($pushKey).child('inBuilding').val()).child('ownerID').val() === auth.uid",
        ".write": "root.child('buildings').child(root.child('depts').child($pushKey).child('inBuilding').val()).child('ownerID').val() === auth.uid"
    }
}

另一个选择是将您的数据结构更改为这样的位置,您可以在用户ID下存储建筑物和部门:

{
    "buildings" : {
        "$uid" : {
            ".read": "$uid === auth.uid",
            ".write": "$uid === auth.uid",
            "$buildingid" : {    
            }
        }
    },
    "depts" : {
        "$uid" : {
            ".read": "$uid === auth.uid",
            ".write": "$uid === auth.uid",
            "$deptid" : {    
            }
        }
    }
}