Einschränken des Zugriffs von Kindern / Feldern mithilfe von Sicherheitsregeln

Ich schreibe eine App, mit der Benutzer Nominierungen einreichen können, die moderiert werden, bevor sie anderen Benutzern angezeigt werden. Dies erfordert eine Reihe von Einschränkungen, deren Implementierung mit Sicherheitsregeln bisher nicht erfolgreich war:

Verstecke alle Nominierungen, die noch nicht genehmigt wurdenPrivate Felder vor Übermittlung verbergen (Telefon, Genehmigungsstatus, Erstellungsdatum usw.)

Meine aktuellen Regeln lauten wie folgt:

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

Untergeordnete Regeln (z.$nomination) Verhindern Sie nicht, dass das gesamte Kind vom Elternteil gelesen wird. Wenn ich zuhörechild_added aufhttps://my.firebaseio.com/nominations Es gibt glücklich alle Kinder und alle ihre Daten zurück, selbst wenn die oben genannten Sicherheitsregeln gelten.

Meine aktuelle Problemumgehungsidee hierfür ist, einen separaten Knoten mit dem Namen zu behaltenapproved und verschieben Sie die Daten einfach zwischen Listen, wenn jemand eine Nominierung genehmigt oder ablehnt, aber es scheint ein fürchterlich gebrochener Ansatz zu sein.

Aktualisieren

Im AnschlussMichael Lehenbauer's exzellenter Kommentar Ich habe die ursprüngliche Idee mit minimalem Aufwand umgesetzt.

Die neue Datenstruktur sieht wie folgt aus:

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

Jede Nominierung wird unter gespeichertentries mit privaten daten wie telefonnummer, e-mail etc. unterprivate und öffentlich zugängliche Daten unterpublic.

Die aktualisierten Regeln lauten wie folgt:

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

Und die JavaScript-Implementierung:

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

Der einzige Teil der Implementierung, mit dem ich zu kämpfen habe, ist die Behandlung von Statusänderungen. Mein derzeitiger Ansatz kann sicherlich verbessert werden:

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

Ich hatte vor zu benutzenchild_changed aufnominations/status aber ich konnte es nicht zuverlässig zum Laufen bringen.

Antworten auf die Frage(3)

Ihre Antwort auf die Frage