具有事件观察功能的Firebase对象所有权

时间:2016-05-26 20:59:16

标签: ios firebase

我在iOS应用中使用Firebase。我希望每个对象都有一个creatorId属性,其值是经过身份验证的用户ID(authData.uid,带有Firebase身份验证对象)。我正在使用自定义令牌生成器进行身份验证,但也可以使用匿名登录重现该问题。

我希望用户只能阅读(和写,但我们现在专注于阅读,因为那是我遇到问题的地方)他们创建的对象。换句话说,查询用户的经过身份验证的用户ID将与他们提取的对象的creatorId匹配。

当我制作查询和规则以实现此目的时,我遇到了权限问题。

Here是规则和安全的Firebase文档。

以下是Task对象的Firebase信息中心的外观:

+ firebase-base
    + tasks
        + {task_id}
             + creatorId: 
             + title: 

其中task_id是Firebase在插入时生成的唯一标识符。

我的规则看起来像这样(再次,让我们暂时忽略编写规则):

{
   "rules": {
       "tasks": {
         "$task_id": {
           ".read": "auth.uid === data.child('creatorId').val()"          
         }
       }
   }
}

阅读特定任务可以正常工作,但我希望能够使用observeEventType和相关函数进行查询,“获取我创建的所有任务”。这对我不起作用。我收到“Permission Denied”错误。

以下是我在Swift中的观察方式:

let reference = Firebase(url: "https://{My-Firebase-Base-Reference}/tasks")
reference.observeEventType(.ChildChanged, 
     withBlock: { (snapshot: FDataSnapshot!) -> Void in

                       // Success

             }) { (error: NSError!) in

                       // Error: I get Permissions Denied here.

              }

Per @ Ymmanuel的建议,我也尝试在我的查询中更具体,如下:

let reference = Firebase(url: "https://{My-Firebase-Base-Reference}/tasks")
reference.queryOrderedByChild("creatorId").queryEqualTo({USER_UID}).observeEventType(.ChildChanged, 
     withBlock: { (snapshot: FDataSnapshot!) -> Void in

                       // Success

             }) { (error: NSError!) in

                       // Error: I get Permissions Denied here.

              }

这些块都不起作用,我总是得到“Permission Denied”错误。我做错了什么?

2 个答案:

答案 0 :(得分:3)

您缺少的是您假设安全规则是查询而不是真实。

检查 链接中的Rules are Not Filters部分。

安全规则仅验证您是否可以读取或写入firebase数据库的特定路径。

如果您只想接收特定用户的更改,则应使用firebase查询。

例如,如果您想获得特定用户的所有任务,您应该这样做:

let reference = Firebase(url: "https://{My-Firebase-Base-Reference}/tasks")
reference.queryOrderedByChild("creatorId").queryEqualTo(YOUR_CURRENT_USER_UID).observeEventType(.ChildChanged, 
     withBlock: { (snapshot: FDataSnapshot!) -> Void in
          // Success
     }) { (error: NSError!) in
          // Error: Get Permissions Denied Here.
}

通过这种方式,您可以获取仅与您的用户相关的所有事件,并通过应用安全规则来保护信息。

如果您只想让创建者编写自己的任务,您还应该考虑创建任务的情况并编写如下内容:

  "tasks": {
     //You need to include the $task_id otherwise the rule will seek the creatorId child inside task and not inside your auto-generated task
     "$task_id": {
       ".read": "auth.uid === data.child('creatorId').val()",
       //this is to validate that if you are creating a task it must have your own uid in the creatorId field and that you can only edit tasks that are yours...
       ".write":"(newData.child('creatorId').val() === auth.uid && !data.exists()) || (data.child('creatorId').val() === auth.uid && data.exists())",
       //you should add the index on to tell firebase this is  a query index so your queries can be efficient even with larger amounts of children
       ".indexOn":"creatorId",        
     }
   }

(检查语法,但这是一般的想法)

答案 1 :(得分:1)

几条评论:

如果启用了本地持久性,并且您没有互联网连接并且没有本地值,则不会调用任何块。

问题中的观察代码是在困扰我,这没有错,但如果它是这样的话可能会更清楚:

    reference.observeEventType(.ChildChanged, withBlock: { snapshot in

            print(snapshot.value)

        }, withCancelBlock: { error in

            print(error.description)

    })

编辑:

现在问题更清楚了

  

我希望用户只能阅读他们创建的对象。

我将通过以下内容进行演示:

Firebase结构:

posts
  post_0
    created_by: uid_0
    msg: some message
  post_1
    created_by: uid_1
    msg: another message
  post_2
    created_by: uid_2
    msg: yippee

以及允许用户仅从他们创建的帖子节点读取的规则

"rules": {
    ".read": false,
    ".write": false,
    "posts": {
      ".read": false,
      "$post_id": {
        ".read": "root.child('posts').child($post_id).child('created_by').val() == auth.uid",
        ".write": "auth != null"
      }
    }
  }
然后是要测试的代码。我们假设当前用户uid是uid_0:

let reference = self.myRootRef.childByAppendingPath("posts/post_0")

reference.observeEventType(.ChildAdded, withBlock: { snapshot in

        print(snapshot.value)

    }, withCancelBlock: { error in

        print(error.description)    
})

上面的代码将允许从节点post_0读取,该节点是由created_by子节点指示创建的post uid_0。

例如,如果路径更改为posts / posts_1(或其他任何内容),则拒绝读取。

这个答案我没有直接回答这个问题,好像你期望规则“阻止”或“过滤”结果数据,而不是规则(根据其他答案)。

因此,您可能希望通过构建查询来根据created_by = auth.uid(例如

)提取您想要使用的帖子,从而走另一条道路。
let reference = self.myRootRef.childByAppendingPath("posts")

reference.queryOrderedByChild("created_by").queryEqualToValue("uid_1")
    .observeEventType(.Value, withBlock: { snapshot in

            print(snapshot.value)

        }, withCancelBlock: { error in

            print(error.description)    
    })