0
votes

I've a very simple countdown in my Firebase database

I have this rule granting the node's access:

"actions":{
         ".indexOn":[
            "timestamp"
         ],
         ".read":"auth != null",
         "$act":{
            "countdown":{
               ".write":"auth != null && data.val() - newData.val() == 1 && newData.val() >= 0"
            }
         }
      }

the innitial value is only set manually by me... so at runtime users only can read and decrement value by 1. there is absolutelly only one call to write this node in my code and I use the following transaction:

@NonNull
public Transaction.Result doTransaction(@NonNull MutableData mutableData) {
    long counter;
    if (mutableData.getValue() == null)
        counter = this.action.getCountdown();
    else
        counter = (long) mutableData.getValue();

    if (counter > 0) {
        mutableData.setValue(counter - 1);
        return Transaction.success(mutableData);
    } else
        return Transaction.abort();
}

public void onComplete(@Nullable DatabaseError databaseError, boolean commited, @Nullable DataSnapshot dataSnapshot) {
        if (databaseError != null && !databaseError.getMessage().contains("network disconnect")) {
            Crashlytics.log("firebase user: " + FirebaseAuth.getInstance().getUid() + " error on action: " + this.action.toString() + " " + (dataSnapshot != null ? dataSnapshot.getValue().toString() : ""));
            Crashlytics.logException(databaseError.toException());
        } else if (commited) {
     do stuff
        }}

but I'm getting a very high number of "Permition Denied" during my onComplete. all cases the: FirebaseAuth.getInstance().getUid() is != null. so I'm sure there isnt any authentication problem.

My app is used by around 5k users per day
what is wrong with my code to explain why so many Permition Denied in such a simple counter?

======= UPDATE ==========
Explaining the context of this: this code runs only during the 'onCreate' of the Activity called right after login.
The innitial if should not even be necessary since im 100% sure user only gets here after a sucessful login, but as I didnt find anywhere a concrete oficial information saying if the FirebaseAuth can be invalidated when the user switches between apps for a long time I found easier just place the if.

this is absolutelly the only code that touches the refered database node.

if(FirebaseAuth.getInstance().getCurrentUser() != null) 
    App.getDatabaseInstance().getReference(Firebase.BRANCH_ACTIONS).addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull final DataSnapshot dataSnapshot) {        
        if (dataSnapshot.hasChildren()) {
            for (final DataSnapshot actionDataSnapshot : dataSnapshot.getChildren()) {
                try {
                    final Action action = actionDataSnapshot.getValue(Action.class);
                    //noinspection ConstantConditions
                    max = action.getTimestamp();

                    if (action.getCountdown() > 0)
                        actionDataSnapshot.child("countdown").getRef().runTransaction(new ActionTransaction(action));
                } catch(Exception e){}
            }
        }
    }
});
Can you edit your question to show the DatabaseReference on which yuo run the transaction? Also, if you're sure FirebaseAuth.getInstance().getUid() is not null, it helps if you prove that by logging its value right before you read/write and include the update code and the output of the log statement in your question.Frank van Puffelen
@FrankvanPuffelen as requested add more informationRafael Lima
Thanks for that. Please learn to indent your code correctly, as I now had to spend time making it more readable. 1) "I'm 100% sure" Cool. So prove that in your code with a Log.d(TAG, "UID: "+FirebaseAuth.getInstance().getCurrentUser().getUid() or maybe even if (FirebaseAuth.getInstance().getCurrentUser() == null) throw new Exception("User is null"). I trust code much more than anyone's (including my own) certainty. 2) Your secure rule contains 3 clauses that must all be true. Which one is failing? You can determine that by removing them one by one temporarily.Frank van Puffelen
@FrankvanPuffelen, what you are saying makes no sense... why should i log the value of FirebaseAuth.getInstance().getUid() right after i checked it at the 'if' clause? ('FirebaseAuth.getInstance().getCurrentUser().getUid()' this can throw nullpointerexception) I also would like very much to know what clause is failing but firebase doesnt provide any way to debug that... and is very childish to play with database rules in production just for the sake of debugRafael Lima
I'm trying to help here Rafael. Unless you can provide the additional information, I won't be able to do so.Frank van Puffelen