Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

firebase multi path update and rules for each path

I'm implementing a users and events system. One restriction is that a user can only host 1 event at a time.

I initially tried to implement the restriction by querying the events node in the events .write rule, by looking for an event that already had a userId equal to auth.uid. But i couldn't figure out how to do that, so i decided to create a new node, hosts, as a lookup table. It just stores any userIds that are currently hosting an event. If a user tries to create a event, and their userId already exists in hosts, then it's an error.

Rules:

{
  "rules":{
        "users":{
            "$uid":{
                ".read": "auth.uid == $uid",
              ".write": "auth.uid == $uid",
              ".validate": "newData.exists()"
          }
      },
      "hosts":{
            "$uid":{
                ".read": "true",
                //to be a host, you must be a user
              ".write": "auth.uid == $uid &&    root.child('users').hasChild(auth.uid) &&   !root.child('hosts').hasChild(auth.uid)",
              ".validate": "newData.exists()"
          }
      },
      "events":{
          "$uid":{
                ".read": "true",
                //to create an event, you must be a host
              ".write": "root.child('hosts').hasChild(auth.uid)",
              ".validate": "true"
          }
      }
  }
}

Javascript:

createEvent(form){
    var user = firebase.auth().currentUser
    var db = firebase.database();
    var newEventRef = db.ref('events').push();
    var newEvent = {}
    newEvent[uid] = form*/

    var update = {
        ['hosts/'+user.uid]: true
        update['events/'+newEventRef.key] = form
    };
    db.ref().update(update)
        .then((...args)=>{
            ...
        })
        .catch(err=>{
            ...
        })
},

The problem with the update call is that it always fails. I'm trying to insert into hosts and events at the same time. But the events write rule expects the userId to be in hosts before it writes, so i guess that's why it fails.

Do i have to make 2 separate updates? 1 to insert userId into hosts, and then if that succeeds, another insert into events?

like image 645
Eric Guan Avatar asked Jul 16 '26 15:07

Eric Guan


1 Answers

In security rules there are three variables that refer to the data:

  • data - the data as it exists at the location of the rule before it is updated
  • newData - the data as it exists at the location of the rule after it is updated
  • root - the data as it exists at the root of thd database before it is updated

So when you refer to root in your rules, you get the data from before the update. To get the updated data, you have to refer to newData. Since unfortunately there is no newRoot, you will have to navigate from newData up to the required level by calling parent() multiple times.

Once example:

  "events":{
      "$uid":{
            ".read": "true",
            //to create an event, you must be a host
          ".write": "newData.parent().parent().child('hosts').hasChild(auth.uid)",
          ".validate": "true"
      }
  }
like image 79
Frank van Puffelen Avatar answered Jul 18 '26 05:07

Frank van Puffelen