¿La forma en que la base de datos rápida de Firebase maneja los conteos es segura?

Quiero crear un campo de incremento para los “Me gusta” de artículos.

Me refiero a este enlace: https://firebase.google.com/docs/database/android/save-data#save_data_as_transactions

En el ejemplo, hay código para el campo de incremento:

if (p.stars.containsKey(getUid())) { // Unstar the post and remove self from stars p.starCount = p.starCount - 1; p.stars.remove(getUid()); } else { // Star the post and add self to stars p.starCount = p.starCount + 1; p.stars.put(getUid(), true); } 

Pero, ¿cómo puedo estar seguro de si el usuario ya le gustó / no le gustó el artículo?

En el ejemplo, el usuario (pirata informático) también podría borrar el mapa de estrellas enteras como este y lo salvará de todos modos:

 p.stars = new HashMap(); 

y arruinará la lógica para otros usuarios que ya les gustó.

Ni siquiera creo que puedas establecer reglas para esto, especialmente para la acción de “disminución de conteo”.

Cualquier ayuda, sugerencias?

Las reglas de seguridad pueden hacer algunas cosas:

  • asegúrese de que un usuario solo puede agregar / eliminar su propio uid al nodo de stars

     "stars": { "$uid": { ".write": "$uid == auth.uid" } } 
  • asegúrese de que un usuario solo puede cambiar el valor de starCount cuando agrega su propio uid al nodo de stars o lo elimina de allí

  • asegúrese de que el usuario solo pueda boost / disminuir starCount en 1

Incluso con estos, aún podría ser complicado tener una regla de seguridad que asegure que el valor de starCount sea ​​igual al número de uids en el nodo de stars . Sin embargo, le recomiendo que lo intente y comparta su resultado.

La forma en que he visto a la mayoría de los desarrolladores lidiar con esto es:

  • empiece a contar con el cliente (si el tamaño del nodo de stars no es demasiado grande, esto es razonable).
  • tener un proceso confiable ejecutándose en un servidor que agrega las stars en starCount . Podría usar eventos child_added / child_removed para incrementar / disminuir.

Actualización: con un ejemplo de trabajo

Escribí un ejemplo de trabajo de un sistema de votación. La estructura de datos es:

 votes: { uid1: true, uid2: true, }, voteCount: 2 

Cuando un usuario vota, la aplicación envía una actualización de ubicación múltiple:

 { "/votes/uid3": true, "voteCount": 3 } 

Y luego para eliminar su voto:

 { "/votes/uid3": null, "voteCount": 2 } 

Esto significa que la aplicación necesita leer explícitamente el valor actual de voteCount , con:

 function vote(auth) { ref.child('voteCount').once('value', function(voteCount) { var updates = {}; updates['votes/'+auth.uid] = true; updates.voteCount = voteCount.val() + 1; ref.update(updates); }); } 

Básicamente se trata de una transacción con varias ubicaciones, pero luego incorpora el código de la aplicación y las reglas de seguridad en lugar del Firebase SDK y el servidor en sí.

Las reglas de seguridad hacen algunas cosas:

  1. asegúrese de que voteCount solo pueda subir o bajar en 1
  2. asegurarse de que un usuario solo pueda agregar / eliminar su propio voto
  3. asegurarse de que un aumento en el conteo vaya acompañado de un voto
  4. asegurarse de que una disminución en el recuento se acompañe de un “no voto”
  5. asegurarse de que una votación vaya acompañada de un aumento en el conteo

Tenga en cuenta que las reglas no:

  • Asegúrate de que un “no voto” vaya acompañado de una disminución de conteo (se puede hacer con una regla .write )
  • reintentar votos / votos no válidos (para manejar votos concurrentes / no votaciones)

Las normas:

 "votes": { "$uid": { ".write": "auth.uid == $uid", ".validate": "(!data.exists() && newData.val() == true && newData.parent().parent().child('voteCount').val() == data.parent().parent().child('voteCount').val() + 1 )" } }, "voteCount": { ".validate": "(newData.val() == data.val() + 1 && newData.parent().child('votes').child(auth.uid).val() == true && !data.parent().child('votes').child(auth.uid).exists() ) || (newData.val() == data.val() - 1 && !newData.parent().child('votes').child(auth.uid).exists() && data.parent().child('votes').child(auth.uid).val() == true )", ".write": "auth != null" } 

jsbin con algún código para probar esto: http://jsbin.com/yaxexe/edit?js,console