I have a simple chat app on Cloud Firestore with the following security rule to only allow users the ability to send messages in a chat room if they "joined" the room in the app itself:
match /hangouts/{hangout}/messages/{message} {
allow create, write, update, read: if exists(/databases/$(database)/documents/users/{userId}/hangout/{hangout});
}
If the user has a hangoutID in their collection on Firestore then they should be allowed to send the message to that hangoutID. However, I'm still getting error messages:
Write at hangouts/ChIJPRVm2R7H54kRKLP2ttsuUko/messages/17225E70-B708-4033-AE5A-D0CBBD1BC69F failed: Missing or insufficient permissions.
I also have 2 other rules related to hangouts. Is it possible these are interfering?
match /hangouts/{hangout} {
allow read, update, write, create: if request.auth.uid != null;
}
match /hangouts/{hangout}/members/{userId} {
allow read: if request.auth.uid != null;
allow create, update, write, delete: if request.auth.uid == userId;
}
The first one is so people can create and see chatroom hangouts, and the second one is to allow people to join them.
What am I doing wrong? I'm a little new to this concept.
Thanks!!
It looks like you're not doing variable substitution correctly for document paths. In the match part, you use {curly braces} to indicate where the wildcards are, but in the path expression, you use the name of the wildcard like $(this).
/databases/$(database)/documents/users/$(userId)/hangout/$(hangout)
Related
I have a news application connected with Firebase and I add the news manually in Firestore only, inside 5 Collection, I am not good at using Firebase, I use the test mode and now I need your help in changing it, the application is for news, meaning no one can add in it only read the data from Everyone without even logging in, I get many security messages from Google, please write me the code here because I browsed a lot of questions here and I didn't understand anything and it seems that I need a course on Firebase security.
I use a pay-per-use plan, and I'm afraid of high bills. Please help me with a strong security that protects me from fake reading or even writing.
It is hard to answer without an understanding of your firestore collection names but here is a general solution.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// in this example, only admins can write or delete, but anyone can read.
match /news/{docId}{
allow read;
allow write, delete: if isAdmin(request);
}
// This is an example for a users collection, where the doc ID is the
// users authentication Id
// Note the userId=** -> this will apply rules to all subcollections
match /users/{userId=**}{
allow read: if isAuthenticated(request); // logged in users can read
// Only admins or the user with a matching doc Id can write.
allow write: if isAdmin(request) || isOwner(userId);
allow delete: if isAdmin(request); // only admins can delete
}
function isOwner(userID){
return request.auth.uid == userID;
}
function isAuthenticated(request){
return request.auth != null;
}
// this requires you to have set up custom claims
function isAdmin(request) {
return request.auth.token.admin
}
}
}
To learn more about protecting your collections with role based authentication, take a look at Custom Claims
In firebase's firestore security rules:
I want to check the auth.uid with the uid data available in the document of the collection.
I tried using below code but it is not working....is there any way to get this done?
match /databases/{database}/documents {
match /candidates/{candidate}/{document=**} {
allow read: if request.auth != null && request.auth.uid == resource.data.userId;
allow write: if true ;
}
This is some improvement for your code. Check whether it would help to solve your problem.
Here in line 2 the syntax should be in the following format.
match /collection/document/collection
So because I think you referring to the documents in the candidate collection it should be as below if you don't want to the rule to be applied deep in the hierarchy.
match /candidates/{candidate}/ {
or as below if you need the deep hierarchy application of the rule.
match /candidates/{document==**}/ {
for the rule statements the first rule can be re-written as
allow read: if request.auth.uid == resource.data.userId;
since looking for the auth.uid in the request token will automatically block unauthenticated users since they won't have auth property on them.
For the second rule I guess you're using only for testing purposes. Since it is dangerous to allow anyone to edit documents without any permission. You can either same rule for the read operation or use a different rule to not allow unauthenticated write access.
My uidConnections collection is a linked list where the uid is the request.auth.uid and connectedUid == specialEvents.uid.
How do I write a Firebase Cloud Firestore rule where a user can only access specialEvents where the uidConnections.connectedUid == specialEvents.uid and uidConnections.uid == request.auth.uid?
Where I'm currently at...
function isSignedIn() {
return request.auth != null;
}
match /specialEvents/{document=**} {
allow read: if isSignedIn()
&& get(/databases/$(database)/documents/uidConnections).data.uid == request.auth.uid
&& get(/databases/$(database)/documents/uidConnections).data.connectedUid == resource.data.uid;
}
I know that I could simply create an "allowed" list of uid's in specialEvents, but this would be limited to allowed document size. Hence the uidConnections linked list rather than an "allowed" list in specialEvents.
If I correctly understand, you would like, in your security rule to find a document in the uidConnections collection that verifies the different contraints.
With Firestore rules this is not possible, because, as explained the doc "the get() and exists() functions both expect fully specified document paths". In other words, you cannot execute a query in the rules, you need to know upfront the exact reference/path of the document you want to point to.
One solution would be to use the value of connectedUid as the ID of the doc. If you cannot do that you will need to find another approach. The one you mentioned in your question is a possible one. If you think there is a risk of reaching the size limit for one Firestore document, use a (sub)collection.
This was asked quite a lot but I still don't know what I can do to silence the warning I am getting every week from Firebase ...
Your Cloud-Firestore database has insecure rules
As you can guess my rules are all can read & write.
My problem is that I actually need that. Inside the SignUp process I need to access all the users emails and usernames to check if the currently selected is available or not. And after the User has signed up he is able to search for other users and create/delete/change his own files.
I don't quite get how I can get rid of the security issue. Where is the threat ?
As I said in then comments if your users can create/delete/update only his own files then your rules wouldn't be read/write true. As i understand you need to have all users to be able to read but you want them to write only to their own documents.
On way to do it is by putting the document id the same as the user id and then use the below rules to check if it is valid.
match /users/{userId} {
allow write: if request.auth.uid == userId;
allow read: true;
}
So, finally got it right. With this solution everyone can read and user can only write their own stuff.
Here is my code:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow write: if request.auth.uid == userId;
allow read;
match /{allSubcollections=**} {
allow write: if request.auth.uid == userId;
allow read;
}
}
}
}
I have an angular app connected to a Cloud Firestore and I am still trying to work out how to specify the rules. As of now I have the use case where a user is a kitchen (this is an app for a student dorm), which means that there can be X number of users, one for each kitchen, and I have the following rules:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
//Only allow authenticated users to read
allow read: if request.auth != null;
}
match /kitchens/{userId}/{document=**} {
// Only the kitchen that is logged in can write to its own data
allow write: if request.auth.uid == userId;
}
}
}
The thing is that since there is a certain number of kitchens, the register screen lets the user choose which kitchen that wants to register from a dropdown list. The list is then filtered based on which kitchen id's that have not already been registered, so I need permission to read the kitchen id's. I just don't want anyone but the kitchens themselves to read the data that belongs to that kitchen. I tried with:
match /kitchens/{id=**} {
// Unauthenticated users can read kitchens
allow read: if true;
}
But sending a http request with Postman just gives me all that I asks for that exists under the kitchen.
So is there a way to only be able to read the fields of the root document and not any of the collections contained in the root, so I can see which kitchens that are already registered, but not e.g. the residents of the kitchen?
This part of the rules is causing you trouble:
match /{document=**} {
//Only allow authenticated users to read
allow read: if request.auth != null;
}
You might think that it's only affecting the "root" documents, but it's actually affecting all documents. That ** will match any document at any depth. With this rule in place, any authenticated user can read every single document in the database.
You should remove this rule and replace it with one that more specifically addresses your requirements.
You have a similar problem here:
match /kitchens/{id=**} {
// Unauthenticated users can read kitchens
allow read: if true;
}
This allows anyone at at all to read the kitchens collection, and *all** of the nested documents in subcollections. Again, that's how the ** wildcard works.
If you want to restrict a match to just a single collection, don't use these "greedy" wildcards. Just use a normal one that only matches documents in the immediate subcollection:
match /kitchens/{id} {
// Unauthenticated users can read kitchens
allow read: if true;
}
You might want to go over the documentation about recursive wildcards.