Firestore security rule should not allow deletion, but it allows - firebase

Firebase security rules do not seem to be working.
I want it to be forbidden to delete other's person objects.
I configured rule for delete:
if resource == null || request.auth.uid == resource.data.owner
I have test that verifies the request fails if user tries to delete other's object. However, the request succeeds.
These are my all security rules:
service cloud.firestore {
match /databases/{database}/documents {
match /{collection}/{document} {
allow create:
if request.auth.uid == request.resource.data.owner
&& (collection != "Person"
|| request.resource.data.owner == request.resource.data.id)
allow update:
if request.auth.uid == resource.data.owner
&& request.auth.uid == request.resource.data.owner
&& (collection != "Person"
|| request.resource.data.owner == request.resource.data.id)
allow get, delete:
if resource == null
|| request.auth.uid == resource.data.owner
allow list:
if collection == "XfCard"
}
}
}
What can be missing?

The resource.data.owner is more a path than a variable that you can use to compare to another. Considering that, it's usually needed that you set this as a specific path and not just perform the direct, usual comparison.
As seen in this other question from the Community here, it's usually necessary to compare the owner with the path of the owner of document, something, more or less like the below code:
if resource.data.owner == /databases/$(database)/documents/users/$(uid_owner)
This way, you should be able to use it and confirm the identity of the owner, in relation to the document.
In addition to checking the above Community post and giving a try using the above code - not tested, but I believe it's a good starting point - , I would recommend you to take a look at the below official documentation, that you can find more information on security rules for group queries.
Collection group queries and security rules
Let me know if the information helped you!

After debugging I found out that the request succeeds, but the object is NOT deleted.
My HTTP request just does not return error message.

Related

Why are my firebase storage security rules are not enforcing and letting users upload images over the limit?

I have setup my storage security rules to only allow authenticated users to post images that are less than 3mb with the following rule:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
// Only allow uploads of any image file that's less than 3MB
allow write: if request.resource.size < 3 * 1024 * 1024
&& request.resource.contentType.matches('image/.*');
allow read, write: if request.auth != null;
}
}
}
However, I just tested from my client and I was able to upload a picture that is 14mb. I have given plenty of time for security rules to set.
How did I bypass this rule?
While the other answers provided you with security rules you could use, it’s also good to add that you can test your security rules using the Firebase rules playground. Using this tool, you can tell exactly the resolution of each of your rules when tested against an event (create, read, update, delete).
I tested your security rules and received this output:
As you can see, while your file size limit rule is working fine, the broader allow write: if request.auth != null; rule is still letting your request go through. This is what the other answers pointed out and what is included in the documentation:
If any of the “allow” rules for the method are satisfied, the request is allowed. Additionally, if a broader rule grants access, rules grant access and ignore any more granular rules that might limit access.
Please see here:
In the case where multiple allow expressions match a request, the access is allowed if any of the conditions is true
So in your case:
allow read, write: if request.auth != null;
let the user to write files with any size.
I think this is the rule you want:
allow write: if request.auth != null &&
request.resource.size < 3 * 1024 * 1024 &&
request.resource.contentType.matches('image/.*');
allow read: if request.auth != null;
You also have allow read, write: if request.auth != null; which overrides the previous one. Try removing write from 2nd line:
match /{allPaths=**} {
// Only allow uploads of any image file that's less than 3MB
allow write: if request.resource.size < 3 * 1024 * 1024
&& request.resource.contentType.matches('image/.*');
allow read: if request.auth != null;
}

Firestore Rules Occasional Permission Denied

So I'm making a journaling app with Flutter that has journals and journal entries.
Here are my firestore rules that have been working for the most part:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, update, delete: if request.auth != null && request.auth.uid == userId;
allow create: if request.auth != null;
}
match /journals/{journal} {
allow read, update, delete: if resource.data.users.hasAny([request.auth.uid]);
allow create: if request.auth != null;
}
match /journalEntries/{journalEntry} {
allow read, update, delete: if request.auth.uid != null && get(/databases/$(database)/documents/journals/$(resource.data.journal)).data.users.hasAny([request.auth.uid]);
allow create: if request.auth != null;
}
}
}
The issue I run into happens about half the time when adding journals:
W/Firestore(31194): (24.0.1) [Firestore]: Listen for Query(target=Query(journalEntries where journal == GivKPhcnen8OmCDOcXs3 order by -dateCreated, -__name__);limitType=LIMIT_TO_FIRST) failed: Status{code=PERMISSION_DENIED, description=Missing or insufficient permissions., cause=null}
E/flutter (31194): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: [cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation.
To clarify, I add a journal document, and then immediately query all the journal entries for that journal, which there are none initially.
Everything works fine if I remove the "get(/databases/$(database)/documents/journals/$(resource.data.journal)).data.users.hasAny([request.auth.uid])" part in the rules but I need that for security. The only thing I can think of is that there is some race condition happening where firestore rules don't think the journal document has been created yet when I start querying the journal entries for the new journal.
Any ideas of what is going on?
Any ideas of what is going on?
Writing to the Firestore database is asynchronous. What's probably going on is that you try to read before the write operation is complete and therefore the security rule does not find the newly created journal document.
We don't see your code, but do you wait that the asynchronous write operation is complete before fetching the journalEntries collection?
In addition, remember that Firestore security rules are not filters. It means that your query to the journalEntries collection must filter the documents that have the user uid in the users array.

What is the proper way to handle "Permission Denied" when a Firestore document does not exist

I have written rules for Firestore, however, when a document does not exist it throws a "Missing or Insufficient Permissions error". For example
firebase.firestore().collection('shipments').doc(order_doc_id)
.onSnapshot(fsResponse => {
...
)}
throws the "Missing or Insufficient Permissions error" when order_doc_id does not exist.
Here is my security rule for the 'shipments' collection:
allow read: if request.auth != null && (resource.data.book_data.seller_id == request.auth.uid || resource.data.buyer_id == request.auth.uid)
Ideally I would like for it to resolve successfully with fsResponse.exists === false and then show the user a "not-found" screen, but this is not the case and the error says nothing about the existence of the document.
What is the proper way to handle this case?
This answer builds on #RenaudTarnec's answer.
The reason your current rules fail, is because they throw an error when used on a non-existent document. Any rule that throws an error is treated as if the rule blocked access.
Your current read rule:
allow read: if request.auth != null
&& (resource.data.book_data.seller_id == request.auth.uid
|| resource.data.buyer_id == request.auth.uid);
For a document that exists, the rule allows read access if the accessing user is the seller or buyer. But when the document doesn't exist, resource will be null - this leads to syntax error where you try to read the property data on a null object - blocking the read. You can see this behaviour in action when testing your rules in the Rules Playground on the Firebase Console.
To allow reads of a non-existant document, you would need to add a null-check for resource. To prevent abuse, you will probably still want to require a user to be logged in.
allow read: if request.auth != null
&& (resource == null
|| resource.data.book_data.seller_id == request.auth.uid
|| resource.data.buyer_id == request.auth.uid);
Based on your comments, your problem is as follows:
The security rule for the shipments collection is
allow read: if request.auth != null && (resource.data.book_data.seller_id == request.auth.uid || resource.data.buyer_id == request.auth.uid)
therefore resource.data.book_data.seller_id == request.auth.uid cannot be true if the doc does not exist
and therefore the rule throws the "Missing or Insufficient Permissions error" when I set a listener on the non-existing document.
HOWEVER,
I would still like to know if it doesn't exist instead of getting
permission denied error
The following security rule, using !exists to check if the document DOES NOT exist, should do the trick:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /shipments/{shipmentId} {
allow read: if (
(!exists(/databases/$(database)/documents/shipments/shipmentId))
|| (request.auth != null && (resource.data.book_data.seller_id == request.auth.uid || resource.data.buyer_id == request.auth.uid))
);
}
}
}

How to debug firestore.rules variables and functions?

I am having difficulty trying to diagnose a particular rule in my firestore.rules file. See that question here for context.
Is there a way to debug the firestore.rules file and/or functions? I'm using unit testing and the emulators to test my rules, but I would really love to see exactly what values are being evaluated by the rules engine.
For instance, here is my firestore.rules file:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /organizations/{orgId} {
allow read: if isAdmin();
allow create, update: if isAdmin();
match /classes/{classId} {
allow read: if request.auth.uid != null;
allow create, update: if isAdmin();
match /students/{studentId} {
allow read: if isAdmin() || belongsToCurrentClass();
allow create, update: if isAdmin();
}
}
}
}
}
function isAdmin() {
// removed for security
}
function belongsToCurrentClass() {
// retuns true if the authenticated user is the teacher of the requested class
return get(/databases/$(database)/documents/organizations/$(orgId)/classes/$(classId)).data.teacherUid == request.auth.uid;
}
What I'd love to do is set breakpoints or step through the code. When attempting CRUD operations on a organizations/{orgId}/classes/{classId}/students/{studentId} path I'd love to inspect exactly what values the orgId, classId, and studentId variables are holding, as well as the resource and request parameters. I'd love to inspect exactly which document (if any) is returned by the get request in belongsToCurrentClass and what the return value is.
Does anyone know of any way to do this? I think I'd answer my question referred to above in 10 seconds if I could just see the data being evaluated.
There is a local emulator for Cloud Firestore security rules. This is your best (and really only) tool for digging into security rule execution. There is no step-through debugging, but you can see a lot of debug output in the console.
https://firebase.google.com/docs/rules/emulator-setup
We can add the built-in debug function to rules. As noted in a comment, you'll see an unhelpful message like this in the browser:
Received: [path] Expected: [bool]. for 'list' # L6
On the plus side, we won't forget to remove debug messages. Tail the log file to see the output: tail -f firestore-debug.log
For example, to see which paths are being called:
match /databases/{database}/documents {
match /{document=**} {
allow create, read, update, delete: if debug(request.path);
}
}

Firestore Security Rule denying read/write on firebase-storage

Trying to add security rules to storage. When I add a security rule to make sure only authenticated users should be allowed with read/write, simulator is working. But when I try to add another constraint on the size of a file, I'm encountering an error.
The following is the Security Rule:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null && request.resource.size < 5 * 1024 * 1024;
}
}
}
I have files under files\
I get the following error in simulator:
Simulated read denied
Error details:
Error: simulator.rules line [4], column [29]. Property resource is undefined on object.
Issue happens if I try to simulate write as well.
EDIT1: IMPORTANT
OK! I found this question and tried experimenting a bit on that line and got the simulator allowing read/write! I made the following change:
allow read, write: if request.auth != null && (request != null
|| request.resource.size < 5 * 1024 * 1024);
That's basically I added a null check. So, at the moment, I'm not clear what's going on here!
I was able to work around this with a rule like
match /users/{uid}/{document=**} {
allow read, create, update: if
request.auth != null &&
request.auth.uid == uid &&
(!("resource" in request) || request.resource.data.uid == request.auth.uid);
}
In this case I wanted to ensure that the "uid" property in the update matches the user's uid. However, if in the simulator you don't do "Build Document" first then request.resource is undefined (I think this is a bug; it should be defined but null IMO).
I believe this really only applies in the simulator, and is not a realistic scenario since create/update requests will always contain a document, even an empty one. But I think I'll keep it in my rule just in case.

Resources