Firebase写入父节点但阻止直接写入子节点

时间:2015-10-08 18:45:18

标签: firebase firebase-security

我有一个用例,我需要用户向Firebase中的某个位置提供一组数据,但不能同时更改该位置的各个子节点。

作为一个例子,假设我有一个用户能够写入一个不同的系统正在观看的树requests,以便它可以处理请求。此外,我们不是使用.push()生成Firebase pushID来唯一标识请求,而是只有一个写入请求的单一位置。结构可能如下所示:

requests: {
  UserID1: {
    requestParam1: "Some parameter",
    requestParam2: "Another parameter"
  }
}

要明确的是,处理请求的系统将监听requests/UserID1,以便通知结构的任何更改并处理请求。

我们还要说requestParam1requestParam2都是必需的。每次写入UserID1节点时都需要这些参数的原因是为了防止用户写入requestParam1,然后处理请求的系统无法获得基本上接收 requestParam1 requestParam2的请求。

很容易确保在写入UserID1时,包含两个参数,但我无法弄清楚如何直接停止对子节点的写入。

到目前为止,这是我的规则:

"requests": {
  "$userID": {
    ".write": "auth !== null && newData.exists()",
    ".validate": "newData.hasChildren(['requestParam1', 'requestParam2'])"
    "requestParam1": {
      ".validate": "newData.isString()"
    },
    "requestParam2": {
      ".validate": "newData.isString()"
    }
    "$other": {
      ".validate": false
    }
  }
}

如果我尝试在没有其中一个请求参数的情况下写入UserID1,它会拒绝写入 - 这是正确的。但是,当我直接写入UserID1/requestParam1时,尝试成功,这不是理想的行为。

我在两个".write": false中都包含requestParam个规则,但仍然允许写入。我相信这是因为写入被父节点中的写规则认为是允许的,因此级联到子节点。根据文档,我认为没有办法解决这个问题。但是,我认为我会变得聪明,并让每个子节点检查newData.parent()的子内容,如下所示:

"requests": {
  "$userID": {
    ".write": "auth !== null && newData.exists()",
    ".validate": "newData.hasChildren(['requestParam1', 'requestParam2'])"
    "requestParam1": {
      ".validate": "
        newData.parent().exists() &&
        newData.parent().hasChildren(['requestParam1', 'requestParam2']) &&
        newData.isString()
      "
    },
    "requestParam2": {
      ".validate": "
        newData.parent().exists() &&
        newData.parent().hasChildren(['requestParam1', 'requestParam2']) &&
        newData.isString()
      "
    }
    "$other": {
      ".validate": false
    }
  }
}

我不确定是否添加newData.parent().exists()支票,所以我把它放在那里以获得良好的衡量标准,但我认为newData().parent().hasChildren([...])规则会使这一切都有效,但我错了。仍然允许写给个别孩子。

想法?

编辑1:

我还尝试将先前位于newData.hasChildren([...])部分的.validate规则移至.write规则中的$userID部分,但没有运气。

1 个答案:

答案 0 :(得分:0)

正如@AnidMonsur所指出的那样,newData“是现有数据的'合并'加上正在编写的新数据”(from the docs),所以它实际上不是只是< / em>新数据,这肯定使问题更难回答。

实现此目标的一种可能方法是确保将写入每个位置的newData与已在该位置的数据(如果存在)不同。但是,如果用户打算将相同的数据写入现场,则这不是一个可行的解决方案。

但是,我认为我有一个可行的替代方案。对于每个数据,即requestParam1requestParam2,规则应强制规则的优先级等同于now并确保优先级数据匹配。这将强制客户端发出请求以在每个数据的请求中包括优先级。这导致以下规则:

"requests": {
  "$userID": {
    ".write": "auth !== null && newData.exists()",
    ".validate": "newData.hasChildren(['requestParam1', 'requestParam2'])"
    "requestParam1": {
      ".validate": "
        newData.isString() &&
        newData.getPriority() !== null &&
        newData.getPriority() === now &&
        newData.parent().child('requestParam2').getPriority() === newData.getPriority()
      "
    },
    "requestParam2": {
      ".validate": "
        newData.isString() &&
        newData.getPriority() !== null &&
        newData.getPriority() === now &&
        newData.parent().child('requestParam1').getPriority() === newData.getPriority()
      "
    }
    "$other": {
      ".validate": false
    }
  }
}

我不确定是否有办法颠覆这个策略...我想两个用户在同一时间写两个位置是可能,但我'我不确定如何避免这种情况(另外,似乎不太可能)。

另外,作为附注,如果有人想知道如何在数据中包含优先级(如果它只是字符串/数字/布尔类型),则可以按如下方式构建写入数据:

//before:
var data = "Some data"

/after:
var data = {
  ".value": "Some data",
  ".priority": Firebase.ServerValue.TIMESTAMP
}