Restringindo o acesso de criança / campo com regras de segurança

Eu estou escrevendo um aplicativo que permite aos usuários enviar indicações que são moderadas antes de serem exibidas para outros usuários. Isso requer um número de restrições que até agora não consegui implementar com regras de segurança:

Ocultar quaisquer indicações que ainda não tenham sido aprovadasOcultar campos privados do envio (telefone, status de aprovação, data de criação etc.)

Minhas regras atuais são as seguintes:

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

Regras para crianças (por ex.$nomination) não impede que toda a criança seja lida dos pais. Se eu escutarchild_added emhttps://my.firebaseio.com/nominations Ele retorna alegremente todas as crianças e todos os seus dados, mesmo com as regras de segurança acima.

Minha ideia atual para isso é manter um nó separado chamadoapproved e simplesmente mova os dados entre listas sempre que alguém aprova ou rejeita uma indicação, mas parece uma abordagem terrivelmente quebrada.

Atualizar

SegueMichael LehenbauerO excelente comentário reimplementou a ideia inicial com um mínimo de esforço.

A nova estrutura de dados é a seguinte:

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

Cada nomeação é armazenada ementries com dados privados, como número de telefone, e-mail, etc.private e dados publicamente visíveis sobpublic.

As regras atualizadas são as seguintes:

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

E a implementação 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')

// 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)
}

A única parte da implementação com a qual estou lutando é lidar com mudanças de status, e minha abordagem atual pode certamente ser aprimorada:

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

Eu estava planejando usarchild_changed emnominations/status mas não consegui fazê-lo funcionar de forma confiável.

questionAnswers(3)

yourAnswerToTheQuestion