Pregunta

Continuing from this thread, on HN: https://news.ycombinator.com/item?id=5462769

Reading through the firefeed rules file answered a lot of questions for me, except for these two:

  1. Editing an existing tweet isn't allowed (".write": "!data.exists()"). How can you make it not editable, but deletable by the author?
  2. How would you securely handle liking/unliking or upvoting/downvoting? write if authenticated, validate for the increase/decrease by one, if the user hasn't modified this before? How would that work? Would there have to be a child list of people who edited this? I'm just really curious about this specific use case as it seems pretty common in many apps, yet seems to me, would be really complicated to implement in firebase?
¿Fue útil?

Solución

1. Not editable but deletable by the author

".write": "!data.exists() || (!newData.exists() && data.child('author') === auth.id)"

2. Liking/Upvoting

On the client, use a transaction which allows you to increment the value safely:

ref.transaction(function(currentValue) {
   return (currentValue||0)+1;
}, function(error) {
   if( error ) /* failed too many times */
   else /* it worked */
});

Security is also straightforward:

".validate": "newData.isNumber() && newData.val() === data.val()+1"

2.5 Ensuring Unique Votes

I'm not sure what this means; the records can't be edited and presumably if they could, only the author would be able to do so; so I don't really understand "modified" in this context: "if the user hasn't modified this before? How would that work?"

To ensure votes are unique, you just store them by user ID. The user can remove their vote by deleting the record.

I'd recommend storing these in a separate path than the sparks and still maintaining a simple increment (the messages that are getting voted up/down) as you don't want to have to retrieve the entire list of voters each time you fetch the spark.

The security rules would look like so:

"votes": {
    "$spark_id": {
       "$vote": {
          ".read": "$vote === auth.id",
          ".write": "$vote === auth.id", 
          // to allow downvoting in addition to up or delete, just add -1 here
          ".validate": "newData.val() === 1 || newData.val() === null"
       }
    }
}

And now add a check to the validate rule for the increment:

".validate": "!root.child('votes').child($spark_id).child(auth.id).exists() && newData.isNumber() && newData.val() === data.val()+1"

Otros consejos

Now that Firebase Functions has been released (in beta) to the general public, it seems to be a good option: https://firebase.googleblog.com/2017/03/introducing-cloud-functions-for-firebase.html

The idea is to have each user be allowed to add their name, by key, to an "upvoters" collection for the tweet. They can create or delete their entry -- but there can only be one, since it's by-key and the security rule only allows control of their one key.

When finding of the "upvote count" is to take place, the client could get the full list of upvoters and tally the number. But instead, for performance's sake, we create a Firebase Function which is triggered whenever an upvote entry is added or removed.

All it does then is increase or decrease an "upvote count" property on the tweet. This is the same as before, except that we make a security rule that only lets the cloud-hosted Function modify this field. Thus, the modification is always trusted and safe, and removes the need for the client to receive the list of upvoters just to get the upvote-count.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top