0

I’m trying to create security rules for the “Save data as transactions” blogging app example from the Firebase guide.

The user can increase or decrease the star count for a post, having his own UID being included or removed from the node at the same time.

I’ve written the following rules: (I removed the rules for the counter increase/decrease since they are out of the scope of the question)

“stars”: {

    ".read": true,

    "$postId”: {
        ".write": "auth != null && (newData.child('users').child(auth.uid).exists() || data.child('users').child(auth.uid).exists())",

        "users": {
            "$userId": {
                ".validate": "$userId === auth.uid"
            }
        }
    }
}

And an exemple of a stars node:

“stars”: {

    “postId1”: {
        starCount: 2,

        "users": {
            “userId1”: true,
            “userId2”: true
        }
    }
}

The rules work fine for adding an user to the “users” node, but a problem arises when removing.

It’s possible for a mean-spirited user to remove any other user from the “users” node, just update it with a empty node. Or a node with all the users from before, minus one he chose to remove. The “.validate” rule ("$userId === auth.uid") does not work for a empty node being submited and I can't write a rule that checks if all the users that were in the database before the update are still there after.

The way I’d solve the problem if I wasn’t using transactions was to to move the “.write” rule to under “$userId”, limiting the uptate for only one user at a time and only with the same UID as the logged user.

Something like:

“stars”: {

    ".read": true,

    "$postId”: {
        "users": {
            "$userId": {
                ".write": "auth != null && $userId === auth.uid"
            }
        }
    }
    "starCount": {
        ".write": true
}

But since I’m doing the database update using transactions I need the “.write” rule under the "$postId”, permitting the update of the “users” node and the “starCount” node at the same time. Something that would not be possible in my last exemple (no “.write” rule under "$postId”).

So it seem like a Catch-22. Or I use transactions but I’m not able to secure the starCount with rules, or I do it as a normal multi-update but loose the concurrency benefits for increasing the counter.

How can I correctly secure the “Save data as transactions” blogging app exemple?

AL.
  • 36,815
  • 10
  • 142
  • 281
Dan Flict
  • 117
  • 8
  • 1
    In Firebase you can do atomic updates using something they call ["deep path updates"](https://firebase.googleblog.com/2015/09/introducing-multi-location-updates-and_86.html). Are you doing this and still have a problem with transactions? This stackoverflow post describes something similar: http://stackoverflow.com/questions/17437695/firebase-how-do-i-update-multiple-resources-atomically – Lyla Dec 26 '16 at 19:58
  • @Lyla I know about "deep path updates", that's what I referred (wrongly) to as "multi-update". But I don't think there's a way of doing a "deep path update" which one of the updates is a "transaction operation" (to increase/decrease a counter, for exemple). So if a do it this way I loose the concurrence benefits of the transaction operation and risk corrupting the counter. – Dan Flict Dec 26 '16 at 21:06
  • @Lyla actually the stack overflow post you gave me is pretty helpful. It does the update with "deep path updates" and implements the "transaction" with rules. I still need to implement the retrial if the value changed by other user before the update, something I got for free with the "transaction" model on the client, but at least it should work. Thanks! – Dan Flict Dec 26 '16 at 21:19
  • 3
    What you'll end up doing is coding your own "transaction like" mechanism using client-side code and server-side security rules to enforce your constraints. Another answer where I cover a similar scenario: http://stackoverflow.com/a/37956590 – Frank van Puffelen Dec 26 '16 at 22:42
  • Yes, it seems that's the solution. But it's weird that an exemple from the Firebase guide cannot be secured using their own rules. – Dan Flict Dec 27 '16 at 00:12
  • 1
    @Dan Flict: I agree.. it seems there's no way to secure the transaction detailed in the Firebase guide. The only way I could get the transaction to work was by setting global read and permissions in the "stars" node (e.g. non-user specific), which is not secure. Frank van Puffelen's answer is the best thing I've come across, though it still needs a work around to handle failures from simultaneous writes :( – oortCloud Jan 12 '17 at 14:01

0 Answers0