Firebase Storage rule different for a specific folder - firebase

I have a firebase storage bucket with this hierarchy e.g.
-Bucket
--UserData
--Folder A
--Folder B
--Folder C
--Folder D
I want to apply these rules for [Folder A, Folder B, Folder C, Folder D]
match /{allPaths=**} {
allow read;
allow write;
}
but for UserData folder I want to apply these rules
match /UserData/{user_id} {
allow read;
allow write: if request.auth != null && request.auth.uid == user_id;
}
in real life, I have far more folders [A,B,C,D ........] and only one folder [UserData]
so it is not possible to write storage rule for each specific folders

What you're trying to do is not supported by security rules. You would have to list out each top-level folder separately - there are no wildcards for this.
The easiest way to apply the same rules to unstructured folder content is to organize all that content under a single prefix (folder), and write the rules for that prefix. If you moved everything under EverythingElse, then you could apply a single rule to all of it recursively:
match /UserData/{user_id} {
allow read;
allow write: if request.auth != null && request.auth.uid == user_id;
}
match /EverythingElse/{allPaths=**} {
// your rules here
}

Related

What is the correct Firestore security Rule for the following scenario?

I have a firestore db like this
users
{userId}
boards
{boardId}
Board Data
I have firestore security rules setup like this.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth != null;
}
match /users/{userId}/{document=**} {
allow read, create, update, delete: if request.auth != null && request.auth.uid == userId ;
}
}
}
How should I setup the rules that-
Users can only access their own data.
No one can access the root users collection
There is an overlapping match statement in your rules, since you use match /{document=**} which maps to ALL documents in your database (see the doc).
So, since:
In the case where multiple allow expressions match a request, the access is allowed if any of the conditions is true, and
For the match /{document=**} statement your rule is allow read: if request.auth != null;, then
every authenticated user can read any user document (you don't restrict on the uid as you do for the users collection. And actually you cannot do so, since match /{document=**} does not specifically target the users collection).
The best is to remove this block and just keep the following block.
match /users/{userId}/{document=**} {
allow read, create, update, delete: if request.auth != null && request.auth.uid == userId ;
}
And if you need to grant read access to other collections, use a rule for each collection, instead of using the wildcard approach.
PS: You may double check if you really need to do match /users/{userId}/{document=**}, which grants access to all subcollections of the users docs. You may just need to do match /users/{userId}.

How to have different rule for a specific collection in firebase firestore

I have a firestore db, for all collections I want to have below rule
allow read : if request.auth.uid != null ;
allow write: if request.auth.uid != null ;
except for service-account collection, which I don't want anybody has access (only firebase functions since they are running under admin service account) so I updated my rules to
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read : if request.auth.uid != null ;
allow write: if request.auth.uid != null ;
}
match /service-account/{serviceAccount} {
allow read: if false;
allow write: if false;
}
}
}
Then I tried to test it via Rules Play Ground, rules are teken into account but result is not correct
So my rule correctly ban access, but the first rule which is going to be applied for all, seems to allow read. I changed the order and no diffrenece.
How can I fix this.
The problem is that this rule is unconditionally applied to every single document in your database:
match /{document=**} {
allow read : if request.auth.uid != null ;
allow write: if request.auth.uid != null ;
}
With this in place, all documents are readable and writable by all users. You cannot override this with another rule. Once a document is deemed readable by any rule, that can't be changed.
See the documentation for overlapping match statements.
What you will need to do instead is call out each individual collection by its name (except service-account), and apply the permissions to them individually.

Limiting permissions to the author not working in Firestore rules

I am attempting to move my Firebase rules from testing where every user could read and write every document to one where only the author can update or delete documents they create.
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth != null;
allow update, delete: if request.auth != null && request.auth.uid == resource.data.author_uid;
allow create: if request.auth != null;
}
}
}
This set of rules is resulting in a 'Missing or insufficient permissions" error at Firebase init and if I attempt to delete a document. If I go with my original rules then everything works.
allow read, create: if request.auth != null
I do a standard firebase.initializeApp (but dont want to publish my keys here - lets just say the same initialize works with the basic rules and on three other firebase projects I have). The delete call is as follows and works with the simpler rule set as well but not the tighter rules above:
const decrement = firebase.firestore.FieldValue.increment(-1);
firestore.collection('story').doc(storyid).delete().then(function() {
firestore.collection('users').doc(getUserID()).update({
saves: decrement
});
})
(thank to Sam Stern on the FB team for guidance)
First, there was a mistake in the rules description. While request.auth.uid is defined by firebase the resource.data.author_id needs to be defined by the developer on each of their documents. In this case the 'story' document contains a 'creator' property that is the userid of the owner. So the correct variable would be resource.data.creator in the above rules.
In addition its possible to define the documentid as the userid, as is often the case when you are creating a 'user' object for each account in your firebase app. In this case the userId is the same as the documentId so the following rule would control update permissions that only allow the owner of that document to change it.
match /users/{userId} {
// In this scope the "userId" variable is equal to the documentId because of the wildcard declaration {userId}
allow update: if request.auth.uid == userId;
}

How do rules cascade in Cloud Firestore?

I'm trying to learn to use Google Cloud Firestore for storing and securing some simple data, so I started writing some basic rules to verify that the data passed from the API is reasonable.
My initial thinking was that each rule would be evaluated and if any one failed it would fail the request, but I'm finding that requests and don't match a rule are still succeeding. Can someone explain how to create progressively stronger security rules for sub collections?
Here is my current ruleset:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
allow write: if request.auth.uid != null;
}
match /projects {
allow write: if resource.data.keys().hasAll(['title', 'description']);
}
}
}
In Firestore Rules, if any allow grants the request, then the request is permitted. The allow statements applied to any given request are all match blocks that match the resource name.
Since the match /{document=**} pattern overlaps with the match /projects pattern, it will be possible to write to the projects document simply by being authenticated e.g. request.auth.uid != null. This was probably not what was intended.
The match /projects is fixed length match against /databases/*/documents/projects, whereas the match /{document=**} will match any document name that starts with /databases/*/documents. The presence of the ** indicates zero or more additional paths.
In general, it is good practice to avoid overlaps in match patterns. If you need to write a rule which matches most things but carves out an exception for a specific path, it would need to be as follows:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
// allow writes to anything except the 'projects' document.
allow write: if request.auth.uid != null
&& /databases/$(database)/documents/$(document)
!= /databases/$(database)/documents/projects
}
match /projects {
// allow _authenticated_ writes to the projects document if they
// have the proper form.
allow write: if request.auth.uid != null
&& resource.data.keys().hasAll(['title', 'description']);
}
}
}
I implement rules as follows:
Generic or Admin role rules match and execute first and thus fail first (if user is not an admin)
More Specific or Public role rules match and execute
Only if both 1 and 2 are false does the rule fail.
Once a rule returns true, you can't fail it or make it false again because of another match.
Note: Place your matches in order of most likely to return true most of the times in order to save on unnecessary charges.
Also try to create functions that you can reuse.
Here is one of the best resources I have found.
https://www.fullstackfirebase.com/cloud-firestore/security-rules

Firebase Storage Rules customMetadata not working

I made some rules to access shared files only for a group of people in Firebase Storage.
The way I do this is to put all the uid's in the customMetadata as keys
[uid: Value]
When I evaluate if the user can read and write the data, I do this:
service firebase.storage {
match /b/{bucket}/o {
match /{accountId}/{allPaths=**} {
allow write: if request.auth.uid in request.resource.metadata.keys() && request.auth != null;
allow read: if request.auth.uid in request.metadata.keys();
}
}
}
I can write successfully, but I just can't read the data.
I have tried all kind of ways:
request.metadata[request.auth.uid] == 'theValue'
request.resource.metadata[request.auth.uid] == 'theValue
request.auth.uid in request.metadata
request.auth.uid in request.resource.metadata.keys()
Nothing works.
You should use:
allow read: if request.auth.uid in resource.metadata.keys();
I see that you've tried request.resource many times. This variable is only accessible when writing to Firebase Storage. When reading you must use resource directly.

Resources