I am trying to configure Firebase Realtime to check if a user should have access to register or not.
To do this I have a value inside each register called "uid" that contains the uid of the user that can access it.
Under my logic, this rule should be enough, but it denies access to Usuarios, even if they match uid with auth.uid.
{
"rules": {
"Usuarios" :{
"$usuarioPk" : {
".read": "data.child('uid').val() == auth.uid"
}
}
}
}
¿What am I doing wrong?
EDIT: Here is the code (C# - Unity Game Engine).
Base Class FirebaseTable.cs
public class FirebaseTable : MonoBehaviour
{
Firebase.Auth.FirebaseAuth auth;
Firebase.FirebaseApp app;
private void Awake()
{
}
protected UnityEvent onDataUpdate = new UnityEvent();
public string dbPath = "";
protected virtual void OnEnable()
{
IniciarListenerDB();
}
protected virtual void OnDisable()
{
DetenerListenerDB();
}
#region Iniciar/Detener Listeners
void IniciarListenerDB()
{
FirebaseDatabase.DefaultInstance
.GetReference(dbPath)
.ValueChanged += HandleValueChanged;
}
void DetenerListenerDB()
{
FirebaseDatabase.DefaultInstance
.GetReference(dbPath)
.ValueChanged -= HandleValueChanged;
}
#endregion
#region Receptor de actualizaciones
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
protected virtual void HandleValueChanged(object sender, ValueChangedEventArgs args)
{
}
#endregion
Here is the child class
public class TablaUsuarios : FirebaseTable
{
public List<Usuario> usuarios;
protected override void OnEnable()
{
base.OnEnable();
dbPath = "Usuarios";
if(usuarios == null)
{
usuarios = new List<Usuario>();
}
}
protected override void OnDisable()
{
base.OnDisable();
usuarios.Clear();
}
protected override void HandleValueChanged(object sender, ValueChangedEventArgs args)
{
DataSnapshot dataSnapshot = args.Snapshot;
if(args.DatabaseError != null)
{
Debug.LogError(args.DatabaseError.Message);
}
List<Usuario> entitiesDB = DataSnapshotToList(dataSnapshot);
usuarios = entitiesDB;
//UpdatePublicList(entitiesDB, ref usuarios);
}
List<Usuario> DataSnapshotToList(DataSnapshot dataSnapshot)
{
List<Usuario> result = new List<Usuario>();
foreach(var children in dataSnapshot.Children)
{
if (children.Value is IDictionary<string, object> variables)
{
Debug.Log(dataSnapshot.GetRawJsonValue());
Usuario entity = new Usuario();
entity.pk = (string)variables["pk"];
entity.uid = (string)variables["uid"];
entity.nombre = (string)variables["nombre"];
entity.apellidos = (string)variables["apellidos"];
entity.email = (string)variables["email"];
entity.telefono = (string)variables["telefono"];
entity.tipoUsuario = (string)variables["tipoUsuario"];
entity.skContrato = (string)variables["skContrato"];
result.Add(entity);
}
}
return result;
}
void UpdatePublicList(List<Usuario> firebaseEntities, ref List<Usuario> localEntities)
{
List<Usuario> usuariosToCreate = new List<Usuario>(); // Añadiremos los usuarios que deben ser creados
List<Usuario> usuariosToUpdate = new List<Usuario>(); // Añadiremos los usuarios que deben ser actualizados
List<Usuario> usuariosToDelete = new List<Usuario>(); // Añadiremos los usuarios que deben ser eliminados
foreach (var element in localEntities) // Creamos una lista con los usuarios a eliminar
{
usuariosToDelete.Add(element);
}
foreach(var firebaseElement in firebaseEntities) // Iteramos por la lista publica de usuarios
{
if (!UsuarioExist(firebaseElement.pk, localEntities)) // Si no la encontramos
{
usuariosToCreate.Add(firebaseElement);
}
else if (UsuarioExist(firebaseElement.pk, localEntities)) // Si la encontramos pero ha cambiado
{
usuariosToUpdate.Add(firebaseElement);
}
}
foreach(var localElement in localEntities)
{
if(!UsuarioExist(localElement.pk, firebaseEntities))
{
usuariosToDelete.Add(localElement);
}
}
for(int i = 0; i < usuariosToDelete.Count; i++)
{
usuarios.Remove(FindUsuario(usuariosToDelete[i].pk, usuarios));
}
for (int i = 0; i < usuariosToCreate.Count; i++)
{
usuarios.Add(usuariosToCreate[i]);
}
for (int i = 0; i < usuariosToUpdate.Count; i++)
{
FindUsuario(usuariosToUpdate[i].pk, usuarios).CopyDataFrom(usuariosToUpdate[i]);
}
}
public Usuario FindUsuario(string pk, List<Usuario> data)
{
foreach(var usuario in usuarios)
{
if(usuario.Pk == pk)
{
return usuario;
}
}
return null;
}
public bool UsuarioExist(string pk, List<Usuario> data)
{
if(FindUsuario(pk, data) != null)
{
return true;
}
return false;
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
And the error log:
This client does not have permission to perform this operation. UnityEngine.Debug:LogError (object) TablaUsuarios:HandleValueChanged (object,Firebase.Database.ValueChangedEventArgs) (at Assets/Scripts/Tablas Firebase/NuevoSistema/Tables/TablaUsuarios.cs:36) Firebase.Database.Internal.InternalValueListener/c__AnonStorey1:<>m__0 () (at Z:/tmp/tmp.gpjxyegeCh/firebase/database/client/unity/proxy/InternalValueListener.cs:72) Firebase.ExceptionAggregator:Wrap (System.Action) (at Z:/tmp/tmp.AqqnQIVt80/firebase/app/client/unity/src/Platform/ExceptionAggregator.cs:112) Firebase.Database.Internal.InternalValueListener:OnCancelledHandler (int,Firebase.Database.Internal.Error,string) (at Z:/tmp/tmp.gpjxyegeCh/firebase/database/client/unity/proxy/InternalValueListener.cs:65) Firebase.AppUtil:PollCallbacks () (at Z:/tmp/tmp.RYyft9kBZb/firebase/app/client/unity/proxy/AppUtil.cs:32) Firebase.Platform.FirebaseAppUtils:PollCallbacks () (at Z:/tmp/tmp.RYyft9kBZb/firebase/app/client/unity/proxy/FirebaseAppUtils.cs:33) Firebase.Platform.FirebaseHandler:Update () (at Z:/tmp/tmp.AqqnQIVt80/firebase/app/client/unity/src/Unity/FirebaseHandler.cs:208) Firebase.Platform.FirebaseMonoBehaviour:Update () (at Z:/tmp/tmp.AqqnQIVt80/firebase/app/client/unity/src/Unity/FirebaseMonoBehaviour.cs:45)
Your code is trying to read the /Usarios
node. But if we look at the rules, nobody has permission to read the /Usarios
node, so that read operation gets (correctly) rejected.
One common source of this mistake is that rules do not filter data. Instead the rules merely check whether the operation you are trying to perform is allowed.
If you want to securely filter data, you will have to encode the condition both in your rules and in your code, as shown in the documentation on query based rules. Here that could be:
"Usarios": {
".read": "auth.uid != null &&
query.orderByChild == 'uid' &&
query.equalTo == auth.uid" // restrict profile access to owner of the profile
}