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.
Related
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;
}
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))
);
}
}
}
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.
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);
}
}
I just installed FriendlyPix iOS and followed all the steps listed on the readme including deploying the cloud functions and rules. I am getting an error and I think it has to do with the storage rules. The error I get is:
User does not have permission to access gs://friendlypixsk.appspot.com/HS4uNP1BYNNnW2Qf59azZH8i3gp2/full/-LAoHLCCMnlD8RoFhUNd/jpeg.
This happens in the uploadPressed function in FPUploadViewController. It happens in the fullref.downloadURL function call. The closure passed to that function is getting an error.
The Firebase Storage rules I have are as follows:
// Returns true if the given UID matches the signed in UID,
// the uploaded file is an image and its size is below the given number of MB.
// Also allow deletes.
function isImageAndBelowMaxSize(uid, maxSizeMB) {
return request.auth.token.admin == true || (request.auth.uid == uid
&& (request.resource == null // Allow deletes
|| request.resource.size < maxSizeMB * 1024 * 1024 // Max size for the uploaded file
&& request.resource.contentType.matches('image/.*'))) // The file is an image
}
service firebase.storage {
match /b/{bucket}/o {
match /{userId}/thumb/{postId}/{fileName} {
allow read, write: if isImageAndBelowMaxSize(userId, 1);
}
match /{userId}/full/{postId}/{fileName} {
allow read, write: if isImageAndBelowMaxSize(userId, 5);
}
}
}
I have not modified them at all. The image does get uploaded and I can see it in the firebase console. I'm a little new to firebase and not sure exactly how these rules work. I have signed in using google sign in.
Any help would be greatly appreciated!!
Thanks