Restricting child/field access with security rules

后端 未结 3 1508
南旧
南旧 2020-11-22 00:05

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 be

3条回答
  •  星月不相逢
    2020-11-22 00:55

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

提交回复
热议问题