0
votes

In my Firestore, I want a Donors collection. Here are my security rules for it:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /Donors/{donor} {
      allow read: if true;
      allow create: if request.auth != null;
      allow update, delete: if request.auth != null && request.auth.uid == resource.data.userId;
    }
  }
}

As can be seen from the rules, I want anybody to read any of the documents in the collection, I only want authenticated users to be able to create documents, and I want only the creators of the document to be able to change the document (see my Donor class below).

public class Donor {
    private String Email;
    private String Name;
    private String City;
    private String Country;
    private String ImageURL;
    private int Balance;
    private String userId;

    public Donor(String email, String name, String city, String country, String userId) {
        this.Email = email;
        this.Name = name;
        this.City = city;
        this.Country = country;
        this.Balance = 0;
        this.userId = userId;
    }
}

I use the following code to create the document.

if (firebaseAuth.getCurrentUser() != null) {
    Log.d("cs50", firebaseAuth.getCurrentUser().getEmail() + " signed in");
} else {
    Log.d("cs50","signed out");
}

if (radioDonor.isChecked()) {
    database.collection("Donor").document(email).set(new Donor(email,
                                                               getIntent().getStringExtra("name"),
                                                               getIntent().getStringExtra("city"),
                                                               getIntent().getStringExtra("country"),
                                                               firebaseAuth.getCurrentUser().getUid()))
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    Log.e("cs50", "Error creating donor", e);
                }
            });
}

I get a message in my logcat "[email] signed in", which means that the user is authenticated using Firebase authentication. But the document is not created with the following error message:

2020-09-20 18:24:47.298 15566-15566/com.example.treeapp D/cs50: [email protected] signed in
2020-09-20 18:24:48.651 15566-15566/com.example.treeapp E/cs50: Error creating donor
    com.google.firebase.firestore.FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions.
        at com.google.firebase.firestore.util.Util.exceptionFromStatus(Util.java:117)
        at com.google.firebase.firestore.core.SyncEngine.notifyUser(SyncEngine.java:521)
        at com.google.firebase.firestore.core.SyncEngine.handleRejectedWrite(SyncEngine.java:451)
        at com.google.firebase.firestore.core.MemoryComponentProvider$RemoteStoreCallback.handleRejectedWrite(MemoryComponentProvider.java:109)
        at com.google.firebase.firestore.remote.RemoteStore.handleWriteError(RemoteStore.java:714)
        at com.google.firebase.firestore.remote.RemoteStore.handleWriteStreamClose(RemoteStore.java:670)
        at com.google.firebase.firestore.remote.RemoteStore.access$600(RemoteStore.java:53)
        at com.google.firebase.firestore.remote.RemoteStore$2.onClose(RemoteStore.java:206)
        at com.google.firebase.firestore.remote.AbstractStream.close(AbstractStream.java:344)
        at com.google.firebase.firestore.remote.AbstractStream.handleServerClose(AbstractStream.java:398)
        at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.lambda$onClose$3(AbstractStream.java:151)
        at com.google.firebase.firestore.remote.AbstractStream$StreamObserver$$Lambda$4.run(Unknown Source:4)
        at com.google.firebase.firestore.remote.AbstractStream$CloseGuardedRunner.run(AbstractStream.java:67)
        at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.onClose(AbstractStream.java:137)
        at com.google.firebase.firestore.remote.FirestoreChannel$1.onClose(FirestoreChannel.java:135)
        at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:426)
        at io.grpc.internal.ClientCallImpl.access$500(ClientCallImpl.java:66)
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.close(ClientCallImpl.java:689)
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.access$900(ClientCallImpl.java:577)
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:751)
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:740)
        at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
        at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        at com.google.firebase.firestore.util.AsyncQueue$SynchronizedShutdownAwareExecutor$DelayedStartFactory.run(AsyncQueue.java:229)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: io.grpc.StatusException: PERMISSION_DENIED: Missing or insufficient permissions.
        at io.grpc.Status.asException(Status.java:541)
        at com.google.firebase.firestore.util.Util.exceptionFromStatus(Util.java:115)
        at com.google.firebase.firestore.core.SyncEngine.notifyUser(SyncEngine.java:521) 
        at com.google.firebase.firestore.core.SyncEngine.handleRejectedWrite(SyncEngine.java:451) 
        at com.google.firebase.firestore.core.MemoryComponentProvider$RemoteStoreCallback.handleRejectedWrite(MemoryComponentProvider.java:109) 
        at com.google.firebase.firestore.remote.RemoteStore.handleWriteError(RemoteStore.java:714) 
        at com.google.firebase.firestore.remote.RemoteStore.handleWriteStreamClose(RemoteStore.java:670) 
        at com.google.firebase.firestore.remote.RemoteStore.access$600(RemoteStore.java:53) 
        at com.google.firebase.firestore.remote.RemoteStore$2.onClose(RemoteStore.java:206) 
        at com.google.firebase.firestore.remote.AbstractStream.close(AbstractStream.java:344) 
        at com.google.firebase.firestore.remote.AbstractStream.handleServerClose(AbstractStream.java:398) 
        at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.lambda$onClose$3(AbstractStream.java:151) 
        at com.google.firebase.firestore.remote.AbstractStream$StreamObserver$$Lambda$4.run(Unknown Source:4) 
        at com.google.firebase.firestore.remote.AbstractStream$CloseGuardedRunner.run(AbstractStream.java:67) 
        at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.onClose(AbstractStream.java:137) 
        at com.google.firebase.firestore.remote.FirestoreChannel$1.onClose(FirestoreChannel.java:135) 
        at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:426) 
        at io.grpc.internal.ClientCallImpl.access$500(ClientCallImpl.java:66) 
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.close(ClientCallImpl.java:689) 
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.access$900(ClientCallImpl.java:577) 
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:751) 
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:740) 
        at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37) 
        at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123) 
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457) 
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) 
        at com.google.firebase.firestore.util.AsyncQueue$SynchronizedShutdownAwareExecutor$DelayedStartFactory.run(AsyncQueue.java:229) 
        at java.lang.Thread.run(Thread.java:764) 

When I allow anyone to read and write using the following rules, the document gets created without any problem.

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}

I can't figure out exactly what I'm doing wrong. I am confirming that the user is authenticated in my code, but the Firestore database does not recognize the user as authenticated.

Please help me out.

1
I don't immediately see what's going wrong. Can you: 1) log the UID value instead of merely saying the user is signed in, 2) show the document you're updating, or confirm that is doesn't exist yet? - Frank van Puffelen
@FrankvanPuffelen I just copied and pasted my old rules into the console (and changed the log) and due to some reason, it worked. Thanks a lot. By the way, you should state in the documentation (if you haven't already) that the first letter of a field in a document cannot be uppercase. I made that mistake, and it took me a little time to change all of my code. It wasn't a lot of effort, but I think it would have been better if I had already known it. - Huzaifa
There is no such limit to document fields. It can definitely be upper or lowercase. - Doug Stevenson
My guess it that you're affected by the Java-class from/to JSON mapping that Firebase performs. You can control that by specifying a PropertyName on the Java class. - Frank van Puffelen
@FrankvanPuffelen , Doug Stevenson Thank you for the tip - Huzaifa

1 Answers

1
votes

Posting as Community Wiki, as in the comments was confirmed that the issue was solved.

The issue was with the different collection names used on the security rules and in the class code. In the security rules the name was Donors, while in the class it was Donor. Once this mistype in the names was fixed, the issue was fixed from the creation of documents.