How To DENY Access to entire Firestore database in security rules - firebase

Lets say I want to have a document in my Firestore having a key on whether to allow access to database or not.
Something like server_online = True
now in my firebase rules I want to check this rule first before going into rules for each collection and document.
I know that a complicated way of doing this is to put this thing in a function and then check this function along with other functions in my firebase rules for every access specifier, but that would be very long since i have a very long ruleset.
So how should i tackle this problem?
I wanted it to be something like.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
allow read: if get(/databases/$(database)/documents/server/status).data.status;
match /users/{userID} {
allow read: if request.auth.uid == userID;
allow create
}
Now first the above rule will check if the variable is True and if so only then proceed to the later ones.
I know that the above code wont work, as Firebase checks the bottom most rule first and doesnt overwrite access.
Any ideas on how to tackle this problem?

Since overlapping rules are OR'ed together, there's no easy way to enforce your AND condition on all rules in one place.
The shortest way I've found is to create a function (say isOnline) and then call that in all rules or in other *higher level) functions that my actual rules depend on.

Related

About tens of Firestore collections have the same access rule, must I repeat writing same `match` sentence for each collection

the match I'm using is below
match /{document=**} {
allow read: if true;
allow write: if false;
}
but firebase always sends alarm, so I want to modify rules.
if a match sentence for a collect, the repeat will be too much.
so I wonder if there is any solution to avoid repeat?
One way to add same rule to multiple collections would be to use a wildcard on collection name as shown below:
match /{collectionName}/{docId} {
allow read: if collectionName in ["colName1", "colName2"];
}
However, this rule is applied to all the collections so if you have any exceptions (e.g. colName3) make sure you define rules for those collections separately as this rule will reject reads for colName3.

Firebase rules not applying to subcollection documents

Here are my rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /Users/{id}{
allow read : if (request.auth.uid == resource.data.uid);
allow write : if false;
}
match /Class/{id}{
allow read : if (request.auth.uid == resource.data.instructor.uid || (request.auth.uid == resource.data.admin.uid));
allow write : if false;
}
match /Class/{id}/Topics/{doc} {
allow read:
if request.auth.uid == resource.data.topicOwnerUID || request.auth.uid == resource.data.adminUID
allow write: if false;
}
}
}
Everything is fine except trying to pull all the topics from Class/docs/Topics/docs. I use getDocuments and try to get all documents using limit(50), I only have 1 topic now, but I also tried making limit to 1 and still don't work.. In each of the Topics docs, there is an adminUID and a topicOwnerUID field. When the owner tries to pull all documents I am getting an error: "Error: Missing or insufficient permissions.". I checked and all the required fields are there and this should allow.
Edit: seems like if i remove .limit and just get a single doc with a specific docID it works. But this isn't what I want :/. Maybe I will have to make the subcollection into its own collection.
Edit2: doesn't work even if it is not a subcollection. completely lost rn. Looks like the only way is to get each doc individually but this ruins lazyLoading. Firebase is lagging behind. will be switching to another db.
Security rules do not mean anything without the matching queries - remember that Security Rules ARE NOT FILTERS - they will NOT "just give you the records that are allowed" - you MUST use queries to match your rules. If your query could return a document that isn't allowed by the rules, then the ENTIRE query is disallowed.
I can tell you from EXTENSIVE use that Firebase/Firestore do not lag behind in any way, and I use complex queries continuously.

Super basic firestore security rule won't work

I can't get the literal simplest firestore security rule I can write to work in the play ground. Just for testing, I've made a Cloud Firestore database with a collection named users. It has one field stuff. In the playground, these are my rules:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{user} {
allow read, write: if true;
}
match /{document=**} {
allow read, write: if false;
}
}
}
I'm simulating a get on location: /databases/(default)/documents/users, but it always fails due to the document=** match, and never matches /users/{user}. Why is this! Feels like I'm following the most basic examples from the docs.
Added a couple screenshots for clarify.
In the "Rules playground", in the location field, you don't need to enter /databases/(default)/documents/. This part of the path is already taken into account, as it is shown above the editable field with the pale grey (or greyed out) /databases/(default)/documents string.
So, by just entering users/C8YDk... it will work, since your rule allows reading the doc, due to an overlapping matching statement.
More info on how to use the playground is to be found here.

Firestore rules: How do I allow access to a subcollection depending on a field of the document that contains it?

I am writing a small program in react that manages a league of sorts.
The problem I have is how do I allow access (read, write) to the matchdays of a league for the correct user. The matchdays are named "matchday-1", "matchday-2", and so on, and are collections of documents (the matches), and are subcollections inside the league.
My firebase structure looks like this basically:
leagues(collection) -> league(document) -> matchday-n(collection) -> match-m(document)
where n and m are numbers.
Here's the issue:
The league document contains a field called "creator", which contains the ID of the user that created that league. Only they are supposed to access it and its matchdays!
But as it seems, when I am accessing a matchday, the value for resource.data.creator in the firebase rules is different from when I am accessing the league itself.
My question is: Which rule do I have to implement, so that only the user who created the league can access it and its subcollections?
I tried to find a way to compare the request.auth.uid to the creator of the league.
I tried something like this as a condition:
request.auth.uid == get("path-to-league").creator
, as you can see in the code I provided.
But it doesn't seem to work this way, as I might be referencing the path incorrectly.
This is my code at the moment:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /leagues/{league} {
allow read, write: if request.auth.uid == resource.data.creator
}
match /leagues/{league}/{document=**} {
allow read, write: if request.auth.uid == get(/databases/$(database)/documents/leagues/$(league)).creator
}
}
}
Depending on how I fiddle with the rule set, but in case of my provided code, I get "missing or insufficient permissions" when trying to access (read or write from) a league that has the wrong creator, which is good, but even if it's the correct creator, I cannot access the matchdays.
You're missing a bit of syntax. It should probably be this:
match /leagues/{league}/{document=**} {
allow read, write: if request.auth.uid ==
get(/databases/$(database)/documents/leagues/$(league)).data.creator
}
Note that there is a data property before creator. This gives you access to the raw field values of the document Resource object.

How does Firebase read / write rules affect user experience on queries?

I have read the Firebase docs, and am wondering how Firebase rules affect a user's experience within an app. Let's say you can only access a group if you are apart of that group as per your Firestore rules. So would you instead of querying all groups where member.uid == auth.uid ( like you would without rules), would you just query the "groups" collection, and the user would only receive the groups that they are apart of?
Every time I read about Firestore rules, they pretty much give the same example, without giving a sample of what the user would see if they queried the database. If someone could point me in the right direction of this, or show how I could test something like this, that would be much appreciated.
So would you instead of querying all groups where member.uid == auth.uid ( like you would without rules), would you just query the "groups" collection, and the user would only receive the groups that they are apart of?
Firebase rules cannot filters data. You cannot have the client read an entire collection and then expect the rules to return a subset of the documents in that collection.
The reason for this is that listeners/observers are only validated once you attach them, after that they have access to the data. This is necessary in order to get the realtime update performance acceptable.
While you can't use rules to filter data, you can use them to validate queries. For an example of this, see the Firestore documentation on securely querying data:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Only the authenticated user who authored the document can read or write
allow read, write: if request.auth.uid == resource.data.author;
}
}
}
Which rejects this query:
// This query will fail
db.collection("stories").get()
But allows this one:
var user = firebase.auth().currentUser;
db.collection("stories").where("author", "==", user.uid).get()
You can see that we're essentially repeating the condition from the code in the rules. This allows you to securely expose a subset of the documents in a collection based on (for example) the user's UID.
The firebase rules are there just to prevent spam in your application and also other stuff.
If you use read:true and write:true then anyone can read and write to the database, that is why it is better to use:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth.uid != null;
}
}
}
This way only authenticated users can see the data that is being retrieved from the database or can send data to the database.
If you have a users in a group and you used this:
service cloud.firestore {
match /databases/{database}/documents {
// Match any document in the 'groups' collection
match /groups/{group} {
allow read, write: if true;
}
}
}
It means all members of that group can read from that database, so if you are in that group, then the data will appear in your application.
more info here:
https://firebase.google.com/docs/firestore/security/rules-structure

Resources