1
votes

I made a button that is supposed to let users delete all their data based on their userid, the button has the same purpose like a "delete your account" button, but it is not working. I tried to delete all documents in the collection using this code:

final String userid = FirebaseAuth.getInstance.getCurrentUser().getUid();

        db.collection("main").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
            @Override
            public void onComplete(@NonNull Task<QuerySnapshot> task) {
                for (QueryDocumentSnapshot queryDocumentSnapshot : task.getResult()){
                    db.collection("main").document(userid).delete();
                }
            }
        })

There is a runtimeExecution : no permission error

    com.google.android.gms.tasks.RuntimeExecutionException: com.google.firebase.firestore.FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions.

I set the security rules as below so that no other users could access other users' data.

service cloud.firestore {
  match /databases/{database}/documents {
    match /main/{userId}/{document=**} {
      allow read, write: if request.auth.uid == userId;
    }
  }
}

Not sure what to do, I thought the security rules could prevent security issues but I think it is causing this permission error?

Thank you in advance.

4

4 Answers

1
votes

This query is trying to get all documents in the collection called "main":

db.collection("main").get()

Your security rules don't allow that. The rules only allow a user to read and write their own document in main.

It's not clear what exactly you're trying to delete, but if it's just the user document under main, you don't need a query at all. Just do this:

db.collection("main").document(userid).delete();
1
votes

If you want to access all the documents under main collection you should change your Firebase rules to

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

Although if you want to delete only single user data which you are doing in your code here :-

db.collection("main").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
        @Override
        public void onComplete(@NonNull Task<QuerySnapshot> task) {
            for (QueryDocumentSnapshot queryDocumentSnapshot : task.getResult()){
                db.collection("main").document(userid).delete();
            }
        }
    })

You shouldn't right the whole query you can simply delete the user document using

db.collection("main").document(userid).delete();
0
votes

There is no API to delete an entire collection (or its contents) in one go.

From the Firestore documentation: https://firebase.google.com/docs/firestore/manage-data/delete-data

"To delete an entire collection or subcollection in Cloud Firestore, retrieve all the documents within the collection or subcollection and delete them. If you have larger collections, you may want to delete the documents in smaller batches to avoid out-of-memory errors. Repeat the process until you've deleted the entire collection or subcollection."

0
votes

Update:

After doing some research and read so many other answers on this site about this matter, I used this method and I think it is so simple for a dummy like me but never mentioned on other answers I am not sure why:

To remove a user completely from authentication, I used the Firestore extension called "Delete User Data" which is a great help, it is magical and allows me to remove a user just by using this code:

FirebaseAuth.getInstance().getCurrentUser().delete().addOnSuccessListener(new OnSuccessListener<Void>() {...}

It doesn't only delete user authentication, with just that code, that particular user's data in the firestore and the images in the storage are also gone. It is fantastic! The important point is that you have to connect the user UID from the authentication and connects the UID to the firestore and the storage.

To know more about how to use the "Delete User Data" Firestore extension(it is still in beta mode), try looking at this great blog tutorial from JORGE VERGARA : Installing the Delete User Data extension

How I set the configuration of the extension to delete a user: enter image description here

How to use the extension: It is so simple I am so happy that it helps me to delete a user so easily without fuss enter image description here

This is the storage structure: enter image description here

The structure of my Firestore:

main(collection)--->userID---->journal(collection)----->journalIDdsvsfbsf
                                                  ----->journalIDdfvdbgnd
                --->userID2--->journal(collection)----->journalIDdsvsfbsf
                                                  ----->journalIDdfvdbgnd

enter image description here

The Authentication structure: enter image description here

See how the firestore and storage have to be connected with the same user UID so that the Firestore extension could delete a user easily by deleting the authentication.

If you still want to use the old method: I noticed that you cannot delete a firestore document that contains one or multiple sub-collections with just delete method. For example, if you want to delete a document that contains 3 sub-collections, in order to delete that document(a user in my case), you have to get all the documents in one of the sub-collection, delete them all, then proceed to do the same to all the other sub-collections in that document. Once all the sub-collections in the document has been deleted, then the document that contains them will be gone itself. That's how Firestore works it seems.

To put it in code: to delete a document that contains a sub-collection "journal" Without using the extension, I have to do it like this:

FirebaseFirestore.getInstance().collection("main").document(userid).collection("journal").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()) {
                        for (final QueryDocumentSnapshot document : task.getResult()) {
                            if (document.exists()){
                                db.collection("main").document(userid).collection("journal").document(document.getId()).delete().addOnSuccessListener(new OnSuccessListener<Void>() {
                                    @Override
                                    public void onSuccess(Void aVoid) {

                                    }
                                }).addOnFailureListener(new OnFailureListener() {
                                    @Override
                                    public void onFailure(@NonNull Exception e) {
                                        Toast.makeText(SettingsActivity.this, "Journal: " + e.getMessage(), Toast.LENGTH_LONG).show();
                                    }
                                });
                            } else {
                                Toast.makeText(SettingsActivity.this, "No data in JOURNAL", Toast.LENGTH_SHORT).show();
                            }
                        }
                    } else {
                        Toast.makeText(SettingsActivity.this, "Journal task: " + task.getException(), Toast.LENGTH_LONG).show();
                    }
                }
            }).addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    Toast.makeText(SettingsActivity.this, "Error getting all the documents: " + e.getMessage(), Toast.LENGTH_LONG).show();
                }
            });

If you have multiple sub-collections in that particular document, to delete it you have to repeat that code multiple times for different sub-collections which will make the codes very long.

I hope this is helpful for someone out there who is trying to delete a user from Firestore. :) Use the extension already.