Allow write based on the value of a document fileld - firebase

I want all authenticated users to read the collection but only the user with the uid that is specified in the field named uid should be able to write.
service cloud.firestore {
match /taken/{doc}{
allow read: if request.auth.uid != null;
allow read,write: if request.auth.uid == doc.id;
}
}
}
However, the above code does not allow write access even if the query has the right uid.

Try this:
service cloud.firestore {
match /taken/{doc}{
allow read: if request.auth != null;
allow create: if true; // resource.data is not defined on create, hence the separate case
allow update, delete: if request.auth.uid == resource.data.uid;
}
}
}
And you can even force the uid field to be set on create, depending on your logic:
service cloud.firestore {
match /taken/{doc}{
allow read: if request.auth != null;
allow create: if request.auth.uid == request.resource.data.uid; // Forces uid to be set to the user's uid
allow update, delete: if request.auth.uid == resource.data.uid;
}
}
}

You can read data of the document being access using resource object as explained in the documentation.
service cloud.firestore {
match /taken/{doc}{
allow read: if request.auth != null;
allow write: if request.auth.uid == resource.data.id;
}
}
}
The above rules allow any authenticated users to read the documents in 'taken' collection but only user with that UID in the documentation to write it.

Related

Firebase security rules with users collection for Signin

I have a "users" collection to store some user data such as city, age, etc. I use firebase Authentication as a document key.
An example path is: /users/ZXHSGdhjfdfwd
I want users to read-only their data while signup/signin. So I have a rule:
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 /posts/{post}/{document=**}{
allow read, write: if request.auth != null;
}
}
}
This doesn't work, and rules monitors show an error. As a result, users cannot sign in as I read users' collections as part of the sign-in process.
When I remove userId and change the rule to:
match /users/{userId} {
allow read: if (request.auth != null);
allow create: if request.auth != null;
}
It works, however, does that mean that userID from firestore (key) and auth.uid does not match, or one of them is null?
This is a pretty standard rule. Why is it not working?
Thanks

Firebase Security Rules - Undefined Resource Error

I have collection Users which has documents with the same Id as the user.uid. I want to allow logged in users to create documents and only update, delete and read their documents which is specified with the same UID as mentioned.
I tried this but it keeps failing.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{document} {
allow create, : if request.auth != null;
allow update, delete, read: if request.auth != null && request.auth.uid == request.resource.data.UID;
}
}
}
In this code i am trying to compare the uid of the logged user with a document field called UID
Have a look at the documentation, it shows exactly the response to your question.
service cloud.firestore {
match /databases/{database}/documents {
// Make sure the uid of the requesting user matches name of the user
// document. The wildcard expression {userId} makes the userId variable
// available in rules.
match /users/{userId} {
allow read, update, delete: if request.auth != null && request.auth.uid == userId;
allow create: if request.auth != null;
}
}
}
The key point is to use the {userId} wildcard expression to match the ID of the document being read/updated/deleted with the uid of the user (i.e. request.auth.uid);

Firebase Cloud Database Rules - how to retrieve data only where the user id matches a key in the object?

Is there a way using firebase rules to retrieve all the projects that have a specific "userID" value. I tried reading all the rules and came up with this rule but they do not work:
I only want to read the documents which matches auth.uid == userID in database.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Allow public read access, but only content owners can write
match /debtors/{userID}/{documents=**} {
allow read: if request.auth.uid != null && request.auth.uid == userID;
allow create: if request.auth.uid != null;
allow update, delete: if request.auth.uid != null && request.auth.uid == userID;;
}
}
}
I have the following object in collection:
{firstname: "Aacis"
relation: "friend"
userID: "7ScK2T0T3SMUR1NJxjiCiRzTnm62"}
Fetching stream in flutter with:
stream: Firestore.instance
.collection("debtors")
.where("userID", isEqualTo: user.uid)
.snapshots()
Did you set auth.uid as document id, and object key named userID?
Firestore.instance.collection("debtors").doc("7ScK2T0T3SMUR1NJxjiCiRzTnm62").set({
firstname: "Aacis",
relation: "friend",
userID: "7ScK2T0T3SMUR1NJxjiCiRzTnm62"
})
If you want to allow only onwer to read access to then you should set allow read: if request.auth.uid != null && request.auth.uid == userID;
If you want to read the documents which matches auth.uid == resource.data.userID then you should set allow read: if request.auth.uid != null && request.auth.uid = resource.data.userID;
If you want to allow public to read access then you should set allow read: if true;
And More, A read rule can be broken into get and list.
service cloud.firestore {
match /databases/{database}/documents {
// A read rule can be divided into get and list rules
match /cities/{city} {
// Applies to single document read requests
allow get: if <condition>;
// Applies to queries and collection read requests
allow list: if <condition>;
}
// A write rule can be divided into create, update, and delete rules
match /cities/{city} {
// Applies to writes to nonexistent documents
allow create: if <condition>;
// Applies to writes to existing documents
allow update: if <condition>;
// Applies to delete operations
allow delete: if <condition>;
}
}
}
See:
https://firebase.google.com/docs/firestore/security/rules-conditions
https://firebase.google.com/docs/firestore/security/rules-structure#granular_operations
https://firebase.google.com/docs/reference/rules/rules.firestore.Resource
Updated
A userID in the following code means the document id is auth.uid, and allow only onwer to read access.
match /debtors/{userID}/{documents=**} {
allow read: if request.auth.uid != null && request.auth.uid == userID;
So you don't set auth.uid as document id, and the stored value named userID in the document match auth.uid, you should use resource.data.userID and request.resource.data.userID.
Please try the following code.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /debtors/{document=**} {
allow read: if request.auth.uid != null && request.auth.uid == resource.data.userID;
allow create: if request.auth.uid != null && request.auth.uid == request.resource.data.userID;
allow update, delete: if request.auth.uid != null && request.auth.uid == resource.data.userID;
}
}
}
The resource variable refers to the requested document, and resource.data is a map of all of the fields and values stored in the document. For more information on the resource variable, see the reference documentation.
When writing data, you may want to compare incoming data to existing data. In this case, if your ruleset allows the pending write, the request.resource variable contains the future state of the document. For update operations that only modify a subset of the document fields, the request.resource variable will contain the pending document state after the operation. You can check the field values in request.resource to prevent unwanted or inconsistent data updates:
See:
https://firebase.google.com/docs/firestore/security/rules-conditions#data_validation
https://firebase.google.com/docs/reference/rules/rules.firestore.Resource
https://firebase.google.com/docs/reference/rules/rules.firestore.Request#resource

Firestore Invalid Permissions: request.auth.uid in

I am using Cloud Firestore and I can't seem to get the "IN" operator to work with the security rules. I have tried using array and map but neither work. Of course when I set it to allow read, write; it works fine. What am I doing wrong?
Rules:
service cloud.firestore {
match /databases/{database}/documents {
match /rooms/{roomId=**} {
allow read, write: if request.auth.uid in resource.data.users;
allow read, write: if request.auth.uid in resource.data.users2;
allow create: if request.auth != null;
}
match /user-rooms/{userId} {
allow read, write: if userId == request.auth.uid;
}
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
allow get, create: if request.auth != null;
}
}
}
Client:
db.collection("rooms")
.document(self.room.getRoomId())
.collection("messages")
.addSnapshotListener { .....
//Room is: d6l946swspNSouANzVdZ
//Username is: eX8gkxJNDREv
data will return it's direct children not it's sub-children(users and users2) so you should use get and exists instead of in
match /rooms/{roomId=**} {
allow read, write: if request.auth.uid in get(/databases/$(database)/documents/rooms/$(roomId)/users/$(request.auth.uid)).data;
allow read, write: if exists(/databases/$(database)/documents/rooms/$(roomId)/users2/$(request.auth.uid));
allow create: if request.auth != null;
}
checkout the doc
You're trying to access a variable named "users" inside resource.data which doesn't exist. The resource variable contains data from the object that is currently being written to the database.
What you're probably trying to do is check if this users exist in the fields users and users2, which can be achieved with the rules:
match /rooms/{roomId=**}{
allow read, write: if (exists(/databases/$(database)/documents/rooms/$(roomId)/users2/$(request.auth.uid)) ||
request.auth.uid in get(/databases/$(database)/documents/rooms/$(roomId)).data.users);
allow create: if request.auth!=null;
}

Access parent documents field in Firestore rules

I'm implementing a recipe book in Firestore where every user is able to see all the recipes all users created but only the original author of the recipe is allowed to edit or delete the recipe. Any user is also allowed to create a new recipe.
My problem is that I am unable to setup the permissions a subcollection to "listen" on a field of the subcollections parentdocument.
Each recipe document contains three things. A field called name where the name of the recipe is stored, a field called creatorUID where the request.auth.uid of the creators uid is stored and a subcollection called ingredients containing documents with some random fields.
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
match /ListOfRecipes/{recipe} {
allow read, create: if isSignedIn();
allow update, delete: if resource.data.creatorUID == request.auth.uid;
match /{list=**} {
allow read: if isSignedIn();
// Should return true if recipe.creatorUID has same value as request.auth.uid
allow write: if recipe.creatorUID == request.auth.uid;
}
}
}
}
The problem is that with these rules it only works to create the recipe document. The subcollection and it's documents are not created since the db says
FirebaseError: [code=permission-denied]: Missing or insufficient permissions.
FirebaseError: Missing or insufficient permissions.
The calls is made from Angular client and it's official library.
Rules don't cascade, so you'll need to perform whatever checks you need for the document being captured by the Rules.
Generally speaking, {x=**} rules are more often a mistake and the usage of =** only for extremely specific use cases.
From your question, I'm assuming your data mode is something like this:
/ListofRecipes/{recipe_document}/List/{list_document}
In this case, you'll need your Rules to be configured something like this:
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
match /ListOfRecipes/{recipe} {
allow read, create: if isSignedIn();
allow update, delete: if resource.data.creatorUID == request.auth.uid;
function recipeData() {
return get(/databases/$(database)/documents/ListOfRecipes/$(recipe)).data
}
match /List/{list} {
allow read: if isSignedIn();
allow write: if recipeData().creatorUID == request.auth.uid;
}
}
}
}
Dan's answer above works great! Just for reference, in my case I only needed the root parent document ID, you can use the variable from the match statement above the nested one, like this:
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
match /ListOfRecipes/{recipeID} {
allow read, create: if isSignedIn();
allow update, delete: if resource.data.creatorUID == request.auth.uid;
match /List/{list} {
allow read: if isSignedIn();
allow write: if recipeID == 'XXXXX';
}
}
}
}
Building upon Dan's answer, you should be able to reduce the number of reads on your database for update and delete on the subcollection by adding the creatorUID to the subcollection document.
You'll have to restrict create to just the creator and make sure the creatorUID is set. Here's my modification of Dan's rules:
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
match /ListOfRecipes/{recipe} {
allow read, create: if isSignedIn();
allow update, delete: if resource.data.creatorUID == request.auth.uid;
function recipeData() {
return get(/databases/$(database)/documents/ListOfRecipes/$(recipe)).data
}
match /List/{list} {
allow read: if isSignedIn();
allow update, delete: if resource.data.creatorUID == request.auth.uid;
allow create: if recipeData().creatorUID == request.auth.uid
&& request.resource.data.creatorUID == request.auth.uid;
}
}
}
}

Resources