Restricción del acceso de niños / campos con reglas de seguridad

Estoy escribiendo una aplicación que permite a los usuarios enviar nominaciones moderadas antes de mostrarlas a otros usuarios. Esto requiere una serie de restricciones que, hasta el momento, no he logrado implementar con las reglas de seguridad:

Ocultar cualquier nominación que aún no haya sido aprobadaOcultar campos privados del envío (teléfono, estado de aprobación, fecha de creación, etc.)

Mis reglas actuales son las siguientes:

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

Reglas secundarias (por ejemplo,$nomination) no impida que todo el niño sea leído por el padre. Si escuchochild_added enhttps://my.firebaseio.com/nominations felizmente devuelve a todos los niños y todos sus datos, incluso con las reglas de seguridad anteriores establecidas.

Mi idea alternativa actual para esto es mantener un nodo separado llamadoapproved y simplemente mueva los datos entre listas cada vez que alguien apruebe o rechace una nominación, pero parece un enfoque horriblemente roto.

Actualizar

SiguiendoMichael LehenbauerEs un excelente comentario. He reimplementado la idea inicial con un mínimo esfuerzo.

La nueva estructura de datos es la siguiente:

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

Cada nominación se almacena bajoentries con datos privados como número de teléfono, correo electrónico, etc. bajoprivate y datos públicamente visibles bajopublic.

Las reglas actualizadas son las siguientes:

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

Y la implementación de 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)
}

La única parte de la implementación con la que estoy luchando es manejar los cambios de estado, mi enfoque actual seguramente puede mejorarse:

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

Estaba planeando usarchild_changed ennominations/status pero no he podido hacerlo funcionar de manera confiable.

Respuestas a la pregunta(3)

Su respuesta a la pregunta