只读列表中具有auth.uid值的字段的子项

时间:2018-06-08 06:05:30

标签: firebase firebase-realtime-database firebase-security firebase-security-rules

这是我的数据库结构

"tasks"
  "$taskId"
      ...
      "user": "firebase user id"

我已在".read": data.child('user').val() === auth.uid"下编写了一条规则$taskId。当我尝试访问单个任务时,此规则将生效。

这也可以保证,如果我编写firebase.database().ref('/tasks').orderByChild('status').limitToFirst(1)之类的查询,我只会获得用户ID字段为auth.uid的任务。或者我还应该在.read

下写一个tasks条款

2 个答案:

答案 0 :(得分:2)

您的问题有几个方面需要回答:

1 /您应该在哪个级别编写安全规则?

如果您只在task级别编写,如下所示,您将无法查询整个任务集。

您可以通过执行以下操作来测试它:

规则:

{
  "rules": {
    "tasks": {
       "$taskID": {
        ".read": "auth != null",
        ".write": "auth != null"
      }
    }
  }
}

JS:

  var db = firebase.database();
  var ref = db.ref('tasks');

  firebase.auth().signInWithEmailAndPassword("....", "....")
    .then(function(userCredential) {
      ref.once('value').then(function(snapshot) {
        snapshot.forEach(function(childSnapshot) {
           console.log(childSnapshot.val());
        });
     });
  });

这将失败,并显示“错误:来自/ tasks的permission_denied:客户端无权访问所需数据。”

如果您将var ref = db.ref('tasks');更改为var ref = db.ref('tasks/123456');(123456是现有任务ID),您将获得结果。

如果您将规则更改为以下内容,则前两个查询将起作用。

{
  "rules": {
    "tasks": {
        ".read": "auth != null",
        ".write": "auth != null"
    }
  }
}

2 /您应该如何才能将具有用户ID字段的任务作为auth.uid?

要注意的第一点是“规则不是过滤器”,详见此处:https://firebase.google.com/docs/database/security/securing-data#rules_are_not_filters

因此,如果您按如下方式实施安全规则:

{
  "rules": {
    "tasks": {
        "$taskId": {
            ".read": "auth != null && data.child('user').val() === auth.uid",
            ".write": "auth != null"
        }
    }
  }
}

您需要在用户uid上编写包含相同限制的查询,如下所示:

var db = firebase.database();

firebase.auth().signInWithEmailAndPassword("....", "....")
  .then(function(userCredential) {

      var ref = db.ref('tasks').orderByChild('user').equalTo(userCredential.user.uid);

      ref.once('value').then(function(snapshot) {
         snapshot.forEach(function(childSnapshot) {
            console.log(childSnapshot.val());
         });
     });

});

但是,此查询将无效,因为“错误:在/ tasks中的permission_denied:客户端无权访问所需数据。”

您既不能执行以下操作,因为“较浅的安全规则会覆盖较深路径的规则。”:

{
  "rules": {
    "tasks": {
        ".read": "auth != null",
        ".write": "auth != null"
        "$taskId": {
            ".read": "auth != null && data.child('user').val() === auth.uid",
            ".write": "auth != null"
        }
    }
  }
}

一种解决方案是使用基于查询的规则(请参阅文档here)并按如下方式编写规则:

{
  "rules": {
    "tasks": {
        ".read": "auth != null &&
            query.orderByChild == 'user' &&
            query.equalTo == auth.uid",
        ".write": "auth != null"
    }
  }
}

但是,正如您可能已经注意到的那样,这将阻止您通过user之外的其他内容(例如status)来订购查询(并对其进行过滤),因为“您只能使用一次一个订单方法。“

因此,解决方案是创建与现有结构并行的第二个数据结构,您可以将用户添加为顶级节点,例如

"tasks"
  "$taskId"
      ...
      "user": "firebase user id"
"tasksByUser"
  "$userId"
      "$taskId"
      ...

您可以使用update()方法同时写入两个数据结构。请参阅文档here

答案 1 :(得分:0)

我在.read: true下提供tasks,并且在返回结果之前正在考虑在各个任务对象下编写的规则。