Search code examples
iosswiftfirebasefirebase-realtime-databasefirebase-security

How to create a security rule depending on different generic nodes?


I would like to implement a rule that checks if the new data entered in the current node is lower than the one in another node that I update in the same request. I explain myself with some code:

// x1 and x2 are some Integer
let update : [String : Any] = [
            "/Users/\(userId!)/A" : ServerValue.increment(NSNumber(value : x1)),
            "/Feedback/\(userId!)/B" : x2
        ]
        
        dbRef.updateChildValues(update)

And here is the rule:

".validate" : "newData.isNumber() && (root.child('Users').child('$uid').child('A').val() >= newData.val())"

I have two questions:

  • Since the safety rule of B depends on the new value of A, am I sure that A has been modified before the update of B (since, in the code, the update of A is placed before that of B).
  • Why my rule for B just doesn't work (regardless of whether B is updated before or after A, because the difference in my tests is so great that it shouldn't reject the request), I've seen examples similar to mine on the net and the syntax looks good.

Thanks for your attention !


Solution

  • The newData variable contains the data as it will exist after the write operation is completed (assuming it is allowed).

    So your newData.isNumber() checks the new value of the node.

    But your root.child('Users')... is checking the current data in the database, so before the write operation. Since you want to check the post-write value, you have to navigate from newData to the root by calling parent() as many times as needed. So if the rule in your question is defined on /Feedback/$uid/B, that'd be:

    newData.parent().parent().child('Users')...
    

    This also looks like a mistake:

    child('$uid')
    

    This tries to use the literal string '$uid', while you more likely want to use the value of the $uid variable. That'd be:

    child($uid)