使用安全规则限制子/字段访问 [英] Restricting child/field access with security rules

查看:190
本文介绍了使用安全规则限制子/字段访问的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个应用程序,允许用户提交在被显示给其他用户之前已经过审核的提名。这需要我在执行安全规则方面的许多限制:


  1. 隐藏任何尚未批准的提名




  2. 隐藏专属字段(电话,审批状态,创建日期等) >我现在的规则如下:

      {
    rules:{
    nominations: {
    .read:true,

    $ nominationId:{
    .read:data.child('state')。val()== '认可'||认证!=空,/ /只读批准提名如果没有认证
    .write:!data.exists(),/ /只允许新的提名被创建

    phone:{
    .read:auth!= null//只允许经过认证的用户阅读电话号码
    },

    状态:{
    .read:auth!= null, //只允许通过身份验证的用户读取批准状态
    .write:auth!= null//只允许通过身份验证的用户更改状态







    $ b

    子规则(例如 $ nomination )不会阻止整个孩子从父母读取。如果我在 https://my.firebaseio上侦听 child_added com / nominations ,它乐意地返回所有的孩子和他们所有的数据,即使上述安全规则。

    我目前的解决方法是保持一个单独的节点名为批准,并且只要有人批准或拒绝提名就简单地移动列表中的数据,但这似乎是一个可怕的破碎的方法。

    更新

    以下是
















    <新的数据结构如下:

    $ p $ my-firebase
    |
    ` - 提名
    |
    ` - 条目
    | |
    | ` - 私人
    | ` - public
    |
    ` - 状态
    |
    ` - 等待
    ` - 批准
    ` - 被拒绝

    每个提名都存储在条目下,包含私人之下的私人数据,如电话号码,电子邮件等,并可公开查看数据在 public 下。



    更新后的规则如下:

    {
    rules:{
    nominations:{
    entries:{
    $ id:{
    .write:!data.exists(),

    public:{
    .read:true,
    },

    private:{
    .read:auth!= null
    }
    }
    },
    $ b $status:{
    pending:{
    .read:auth!= null,

    $ id:{
    .write:root.child('nominations / entries')。child($ id).exists()&& (auth!= null || newData.val()== true)

    },

    approved:{
    .read: true,

    $ id:{
    .write:root.child('nominations / entries')。child($ id).exists()&& auth!= null

    },


    rejected:{
    .read:auth!= null,
    $ b $$ id:{
    .write:root.child('nominations / entries')。child($ id).exists()&& auth!= null





    $ b code $

    以及JavaScript的实现:

    var db = new Firebase('https://my.firebaseio.com')
    var nominations = db.child('nominations')

    var entries = nominations.child('entries ')

    var status = nominations.child('status')
    var pending = status.child('pending')
    var approved = status.child('approved' )
    var rejected = status.child('rejected')

    //通过表单输入创建提名(未显示)
    var createNomination = function(){
    var data = {
    public:{
    name:'Foo',
    age:20
    },

    private:{
    createdAt :new Date().getTime(),
    phone:123456
    }
    }

    var提名= entries.push()
    nomination.setWithPriority(data,data.private.createdAt)

    pending.child(nomination.name()).set(true)
    }

    //检索当前提名状态
    var getStatus =函数(id,callback){
    approved.child(id).once('value',function(snapshot){
    if(snapshot.val()){
    callback(id,'approved')
    } else {
    rejected.child(id).once('value',function (快照){
    callback(id,snapshot.val()? 'pending':'pending')
    })
    }
    })
    }

    //改变提名状态
    var changeStatus (),函数(id,from,to){
    status.child(from).child(id).remove()
    status.child(to).child(id).set(true)



    $ b $ p
    $ b $ p $我只是在处理状态变化的一部分,目前的做法肯定可以改进:
    $ b

    _。each([pending,approved,rejected] ,function(status){
    status.on('child_added',function(snapshot){
    $('#'+ snapshot.name())。removeClass('pending approved rejected')。addClass (status.name())
    })
    })

    计划在提名/状态上使用 child_changed ,但我无法可靠地运作。

    解决方案

    加藤的权利。理解安全规则永远不会过滤数据是很重要的。对于任何位置,您都可以读取所有数据(包括其子项),或者不读取所有数据。所以在你的规则的情况下,在提名下面有一个.read:true会否定所有其他的规则。

    所以我推荐的方法是有3个列表。一个包含提名数据,一个包含已获批准提名名单,一个包含提名候选名单。


    您的规则可能如下:

      {
    rules:{
    //实际的提名。每个将被存储一个唯一的ID。
    提名:{
    $ id:{
    .write:!data.exists(),//任何人都可以创建新的提名,但不能覆盖现有的提名。
    public_data:{
    .read:true //每个人都可以读取公共数据。
    },
    phone:{
    .read:auth!= null,//只有经过认证的用户才能读取电话号码。
    }
    }
    },
    approved_list:{
    .read:true,//每个人都可以阅读获得批准的提名名单。
    $ id:{
    //经过身份验证的用户可以将提名的ID添加到批准列表
    //通过创建一个名称为值。
    .write:auth!= null&& root.child('nominations')。child($ id).exists()& newData.val()== true


    pending_list:{
    .read:auth!= null,//只有经过认证的用户才能读取待处理列表。
    $ id:{
    //任何用户都可以将提名添加到待处理列表中,由
    //一个经过验证的用户进行管理(然后可以从该列表中删除) 。
    .write:root.child('nominations')。child($ id).exists()&& amp;(newData.val()== true || auth!= null)



    $ b code
    $ b

    未经身份验证用户可以添加一个新的提名:

      var id = ref.child('nominations')。push({public_data:不管,电话:555-1234}); 
    ref.child('pending_list')。child(id).set(true);

    通过身份验证的用户可以批准消息:

      ref.child( 'pending_list')子(ID)卸下摆臂(); 
    ref.child('approved_list')。child(id).set(true);

    为了呈现已批准和待处理的列表,您可以使用如下代码:

    $ ('child_added',function(childSnapshot)){
    var nominationId = childSnapshot.name('approved_list')(b)
    $ b

      ref.child (nominationDataSnap){
    console.log(nominationDataSnap.val('name'));
    ref.child('nominations')。child(nominationId).child('public_data')。 ));
    });
    });

    通过这种方式,您可以使用approved_list和pending_list作为轻量级列表(可以通过未经身份验证和身份验证用户),并将所有实际的提名数据存入提名名单(没有人可以直接列举)。

    I'm writing an app that allows users to submit nominations which are moderated before being displayed to other users. This requires a number of restrictions I've so far been unsuccessful in implementing with security rules:

    1. Hide any nominations that haven't been approved yet
    2. Hide private fields from submission (phone, approval status, creation date etc.)

    My current rules are as follows:

    {
        "rules": {
            "nominations": {
                ".read": true,
    
                "$nominationId": {
                    ".read": "data.child('state').val() == 'approved' || auth != null", // Only read approved nominations if not authenticated
                    ".write": "!data.exists()", // Only allow new nominations to be created
    
                    "phone": {
                        ".read": "auth != null" // Only allow authenticated users to read phone number
                    },
    
                    "state": {
                        ".read": "auth != null", // Only allow authenticated users to read approval state
                        ".write": "auth != null" // Only allow authenticated users to change state
                    }
                }
            }
        }
    }
    

    Child rules (e.g. $nomination) don't prevent the entire child from being read from the parent. If I listen for child_added on https://my.firebaseio.com/nominations it happily returns all children and all their data even with the above security rules in place.

    My current workaround idea for this is to keep a separate node named approved and simply move the data between lists whenever someone approves or rejects a nomination, but it seems like a horribly broken approach.

    Update

    Following Michael Lehenbauer's excellent comment I've reimplemented the initial idea with minimal effort.

    The new data structure is as follows:

    my-firebase
        |
        `- nominations
            |
            `- entries
            |   |
            |   `- private
            |   `- public
            |
            `- status
                |
                `- pending
                `- approved
                `- rejected
    

    Each nomination is stored under entries with private data such as phone number, e-mail etc. under private and publicly viewable data under public.

    The updated rules are as follows:

    {
        "rules": {
            "nominations": {
                "entries": {
                    "$id": {
                        ".write": "!data.exists()",
    
                        "public": {
                            ".read": true,
                        },
    
                        "private": {
                            ".read": "auth != null"
                        }
                    }
                },
    
                "status": {
                    "pending": {
                        ".read": "auth != null",
    
                        "$id": {
                            ".write": "root.child('nominations/entries').child($id).exists() && (auth != null || newData.val() == true)"
                        }
                    },
    
                    "approved": {
                        ".read": true,
    
                        "$id": {
                            ".write": "root.child('nominations/entries').child($id).exists() && auth != null"
                        }
                    },
    
    
                    "rejected": {
                        ".read": "auth != null",
    
                        "$id": {
                            ".write": "root.child('nominations/entries').child($id).exists() && auth != null"
                        }
                    }
                }
            }
        }
    }
    

    And the JavaScript implementation:

    var db = new Firebase('https://my.firebaseio.com')
    var nominations = db.child('nominations')
    
    var entries = nominations.child('entries')
    
    var status = nominations.child('status')
    var pending = status.child('pending')
    var approved = status.child('approved')
    var rejected = status.child('rejected')
    
    // Create nomination via form input (not shown)
    var createNomination = function() {
        var data = {
            public: {
                name: 'Foo',
                age: 20
            },
    
            private: {
                createdAt: new Date().getTime(),
                phone: 123456
            }
        }
    
        var nomination = entries.push()
        nomination.setWithPriority(data, data.private.createdAt)
    
        pending.child(nomination.name()).set(true)    
    }
    
    // Retrieve current nomination status
    var getStatus = function(id, callback) {
        approved.child(id).once('value', function(snapshot) {
            if (snapshot.val()) {
                callback(id, 'approved')
            } else {
                rejected.child(id).once('value', function(snapshot) {
                    callback(id, snapshot.val() ? 'rejected' : 'pending')
                })
            }
        })
    }
    
    // Change status of nomination
    var changeStatus = function(id, from, to) {
        status.child(from).child(id).remove()
        status.child(to).child(id).set(true)
    }
    

    The only part of the implementation I'm struggling with is handling status changes, my current approach can surely be improved upon:

    _.each([pending, approved, rejected], function(status) {
        status.on('child_added', function(snapshot) {
            $('#' + snapshot.name()).removeClass('pending approved rejected').addClass(status.name())
        })
    })
    

    I was planning on using child_changed on nominations/status but I haven't been able to get it working reliably.

    解决方案

    Kato's right. It's important to understand that security rules never filter data. For any location, you'll either be able to read all of the data (including its children), or none of it. So in the case of your rules, having a ".read": true under "nominations" negates all of your other rules.

    So the approach I'd recommend here is to have 3 lists. One containing nomination data, one to contain the list of approved nominations, and one to contain the list of pending nominations.

    Your rules could be like so:

    {
      "rules": {
        // The actual nominations.  Each will be stored with a unique ID.
        "nominations": {
          "$id": {
            ".write": "!data.exists()", // anybody can create new nominations, but not overwrite existing ones.
            "public_data": {
              ".read": true // everybody can read the public data.
            },
            "phone": {
              ".read": "auth != null", // only authenticated users can read the phone number.
            }
          }
        },
        "approved_list": {
          ".read": true, // everybody can read the approved nominations list.
          "$id": {
            // Authenticated users can add the id of a nomination to the approved list 
            // by creating a child with the nomination id as the name and true as the value.
            ".write": "auth != null && root.child('nominations').child($id).exists() && newData.val() == true"
          }
        },
        "pending_list": {
          ".read": "auth != null", // Only authenticated users can read the pending list.
          "$id": {
            // Any user can add a nomination to the pending list, to be moderated by
            // an authenticated user (who can then delete it from this list).
            ".write": "root.child('nominations').child($id).exists() && (newData.val() == true || auth != null)"
          }
        }
      }
    }
    

    An unauthenticated user could add a new nomination with:

    var id = ref.child('nominations').push({ public_data: "whatever", phone: "555-1234" });
    ref.child('pending_list').child(id).set(true);
    

    An authenticated user could approve a message with:

    ref.child('pending_list').child(id).remove();
    ref.child('approved_list').child(id).set(true);
    

    And to render the approved and pending lists you'd use code something like:

    ref.child('approved_list').on('child_added', function(childSnapshot) {
      var nominationId = childSnapshot.name();
      ref.child('nominations').child(nominationId).child('public_data').on('value', function(nominationDataSnap) {
        console.log(nominationDataSnap.val());
      });
    });
    

    In this way, you use approved_list and pending_list as lightweight lists that can be enumerated (by unauthenticated and authenticated users respectively) and store all of the actual nomination data in the nominations list (which nobody can enumerate directly).

    这篇关于使用安全规则限制子/字段访问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆