【问题标题】:What is the best practice to write the rules of Firebase in a situation like this?在这种情况下编写 Firebase 规则的最佳做法是什么?
【发布时间】:2018-09-14 04:48:21
【问题描述】:

初步方法/问题

用户将能够写入他自己的用户节点。然后用户必须能够写出他/她想要的任意数量的建筑物和任意数量的部门(还有房间 但为了清楚起见,我暂时将其放在一边)。用户应该能够读取(和写入)他自己的用户节点、建筑物和部门,但不能读取(和写入)其他用户的节点、建筑物和部门。

基本上:

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

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

这是数据库结构:

{
  "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,
      }
    }
  }

初步方法

每个 users 节点都有一个子节点 isAdmin,其中包含该用户创建的 buildings 的所有按键。使用相同的逻辑,buildings' 节点包含一个 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) 确保 buildingsdepts 节点都具有 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 不允许用户读/写 buildings 节点,但它允许读/写 buildings /$pushKey 节点。 firebase 是否需要用户能够读取/写入父节点(buildings)和子节点(buildings/$pushKey)?如果是这样,您如何防止用户删除(.set)整个 buildings 节点?

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

【问题讨论】:

  • “我知道这看起来很乱” - 我完全同意这一点。您能否编辑您的问题,说明您想要什么(例如:用户可以读/写他们自己的用户节点、每座建筑物,以及他们拥有的建筑物的唯一部门),并可能减少一些解释性文字。过多的解释/阐述实际上会使它更难理解。
  • 关于不支持的 javascript 的第一个错误:你有一个 .在最后一个孩子之后:.child.('isAdmin')", 这应该是.child('isAdmin')",
  • 感谢@André 的提示。我已经删除了所有凌乱的代码,只留下了问题。
  • 你能把你最后的规则尝试放回你的问题吗?类似“我试过这个(规则)但我得到了这些错误”
  • 您在问题中包含了 JSON 树的图片。请将其替换为作为文本的实际 JSON,您可以通过单击 your Firebase Database console 中的导出 JSON 链接轻松获得该文本。将 JSON 作为文本使其可搜索,让我们可以轻松地使用它来测试您的实际数据并在我们的答案中使用它,一般来说这只是一件好事。

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


【解决方案1】:

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

对于用户节点,我删除了更全局的读写规则,因为如果它们是假的,它们是多余的,因为这是默认状态。如果它们是真的,它们将覆盖这些规则,因为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 的建筑物是否有 ownerID,即您的 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"
    }
}

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

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

【讨论】:

  • 第二个解决方案我必须不断地遍历所有应用程序方法的 data.snapshots。第一个解决方案,虽然它允许我编写一个新建筑物,但它并没有将 ownerId 节点写入 buildings (也没有读取任何内容)。如果我添加它会起作用:buildings": { ".read": true, ".write": true, "$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 上写一个 ownerID 节点,就像在 building 节点中一样,以轻松缓解痛苦。我会更新我的问题,提到这一点。
  • 第一个解决方案的问题是您分两步编写建筑物,首先是建筑物,然后您尝试添加 ownerid。第二步失败,因为已经存在数据但它们没有(或为空)ownerid。第二种解决方案是一个更笼统的想法。也许更好的解释方式是“考虑如何更改数据结构以适应您的需求/用例。”
  • 如果我们丢失了“!data.exists()”函数怎么办?
  • 我认为不可能写出任何东西,至少不是你现在这样做的方式。在您的情况下,您必须检查谁拥有该建筑物。如果没有 buildingid,则无法检查。解决方案是在创建建筑物的第一个写入中包含您的 ownerid。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-29
  • 1970-01-01
相关资源
最近更新 更多