Marwan Tragha
Marwan Tragha

Reputation: 131

Different rule behaviours for the same scheme on Realtime Database

I have added new security rules for my Firebase database, here they are:

{
  "rules": {
    "Users" : {
      "$uid" : {
        ".read" : "auth != null && auth.uid == $uid",
        "numberOfConnections" : {
         ".write" : "(auth != null && auth.uid == $uid) && (newData.val() - data.val() == 1 || newData.val() == 0)",
        },
        "mail" : {
          ".write" : "auth != null && auth.uid == $uid",
        },
        "numberOfFeedBack" : {
          ".write" : "auth != null && auth.uid == $uid",
        },
        "username" : {
          ".write" : "auth != null && auth.uid == $uid",
        }
      }
    },
    "Feedback" : {
      "$uid" : {
        ".read" : "auth != null && auth.uid == $uid",
        ".write" : "auth != null && auth.uid == $uid",
      }
    },
  }
}

When I write with updateChildValues in FeedBack it works, but when I write in Users I get a denied permission.

Here is the code:

// This don't work -> Permission Denied
static func createNewUsersDb(userId : String, username : String, mail : String) {
        let update : [String : Any] = [
            "/Users/\(userId)/username" : username,
            "/Users/\(userId)/mail" : mail,
            "/Users/\(userId)/numberOfConnections" : 0,
            "/Users/\(userId)/numberOfFeedBack" : 0
        ]
        dbRef.updateChildValues(update)
    }

//This work
private static func createNewFeedBackDb(_ informationForFeedback : String){
        let now = DateFormatter()
        now.dateStyle = .medium
        now.timeStyle = .medium
        now.locale = Locale(identifier: "EN-en")
        
        let update : [String : Any] = [
            "/Users/\(userId!)/numberOfFeedBack" : ServerValue.increment(NSNumber(value : 1)),
            "/FeedBack/\(userId!)" : [
                "date" : now.string(from: Date()),
                "information" : informationForFeedback
            ]
        ]
        
        dbRef.updateChildValues(update)
    }

It seems that it's because I didn't write a ".write" rule for Users.uid but I don't understand why it's blocking since I don't write when in the child nodes for which I did set a ".write" rule. Also, if I write a ".write" rule in Users.uid, Firebase's rule handling would cause my sub-rule for numberOfConnections to be ignored.

That's why I specified the same ".write" rule for all child nodes of Users.uid except numberOfConnections, it seems a bit overkill but it's the only way I found.

Upvotes: 1

Views: 58

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 598951

I'm not exactly sure why your write rules don't work, since you're only writing to allowed paths/values as far as I can see. However, I'd typically implement the case differently, which may solve your problem anyway:

"Users" : {
  "$uid" : {
    ".read" : "auth != null && auth.uid == $uid",
    ".write" : "auth != null && auth.uid == $uid",
    "numberOfConnections" : {
      ".validate" : "(newData.val() - data.val() == 1 || newData.val() == 0)",
    },
    "mail" : {
      ".validate" : true
    },
    "numberOfFeedBack" : {
      ".validate" : true
    },
    "username" : {
      ".validate" : true
    },
    "$other" : {
      ".validate" : false
    }
  }

So now you only check for authorization once (on /Users/$uid) and then use validation checks to:

  • Allow mail, numberOfFeedBack, and username.
  • Allow numberOfConnections based on the value written.
  • Reject any other child nodes - the $other rule matches all child nodes that are not matched by another/named rules already.

Upvotes: 1

Related Questions