FireStore: permission-denied in get match - firebase

I have the next firestore rule:
service cloud.firestore {
match /databases/{database}/documents {
match /{usuarios=**} {
allow write: if get(/usuarios/$(request.auth.uid)).data.level == 0 || get(/usuarios/$(request.auth.uid)).level == 0;
}
}
}
And I get "permission-denied" when I tried this query:
firebase.firestore().collection('usuarios').doc(uid).set({...});
This is my DB actually:
pd: I want to add info about new users in my db (by his UID)

That's going to be very expensive and inefficient since you pay per get request. Instead, you should write your rules as if you're defining the database's structure. Here's what I mean:
service cloud.firestore {
match /databases/{database}/documents {
match /usuarios/{uid} {
// Give write access to any field within the document who's id is the uid
// Add other validation as needed.
allow write: if uid == request.auth.uid
// Give admin access to anyone who's `level == 0`
// Make sure to add the `databases...` boilerplate
|| get(/databases/$(database)/documents/usuarios/$(request.auth.uid)).data.level == 0;
// If necessary, give access to sub-collections.
// (We can't do it for uid or it will become a path object that we can't use as a string.)
/{sub=**} {
allow write: if uid == request.auth.uid;
}
}
}
}
If you'd like to see fully flushed-out rules that are doing something very similar, I have an open source exemple. Cheers!

Related

Firestore allow read if collection name matches

I'm trying to implement what I thought was a basic security rule in Cloud Firestore, namely to allow read access to a specific collection.
service cloud.firestore {
match /databases/{collectionName}/documents {
match /{document=**}{
allow read : if collectionName=="metadata";
}
}
}
so in the rules playground, the query for /metadata/status gets denied, however, if I switch the operator to != instead of ==, it allows any query for any collection, not just the ones that aren't metadata. Help?
The placement of that wildcard is incorrect. The collectionName would be name of the database which is (default) for default database and hence "(default)" == "metadata" returned false. Try the following rules:
service cloud.firestore {
match /databases/{database}/documents {
match /{collectionName}/{doc}{
allow read : if collectionName == "metadata";
}
}
}
Here collectionName would be name of collection being accessed.
This rule however will be applied for all collections. If you want to add that rule for 'metadata' collection only then you can add a separate rule for that:
match /metadata/{doc} {
allow read: if true;
}
if you want to set a rule on only a specific document, E.g: Inbox:
service cloud.firestore {
match /databases/{database}/documents {
// Allow public read access, but only authorized users can write
match /{document=**} {
allow read: if true
allow write: if (request.auth.uid != null);
}
match /Inbox/{document=**} {
allow read,write: if true
}
}
}

FireStore Rules : Read/Write user own document

In Firestore I have 2 collections
A users collection and a periods collection. I need a rule for my period collection : Read and write only by the user who create the period.
users collection look like
periods collection look like
This is what I try with no success
service cloud.firestore {
match /databases/{database}/documents {
function userDoc() {
return /databases/$(database)/documents/users/$(request.auth.uid);
}
match /users/{userId} {
allow read: if true;
allow create, update: if request.auth.uid == userId;
}
match /periods/{id} {
allow read : if userDoc() == request.resource.data.user;
allow write : if userDoc() == request.resource.data.user;
}
}
}
Replace
if userDoc() == request.resource.data.user
with
if userDoc() == request.resource.data.user_id
Other than that your security rule looks correct to me.
The object ‘request.resource.data.user’ doesn’t exist in your periods collection so it should be changed to ‘request.resource.data.user_id’.
Also the read and delete request doesn’t have a ‘request.resource’ object. So for the read and delete request, ‘request.resource.data.user_id’ should be replaced with ‘resource.data.user_id’.
I would suggest you to use more granular rules for write requests i.e. create, update, delete. For more details on firestore rules you can refer to this link.
For your use case please see the below firestore rule sample.
service cloud.firestore {
match /databases/{database}/documents {
function userDoc() {
return /databases/$(database)/documents/users/$(request.auth.uid);
}
match /users/{userId} {
allow read: if true;
allow create, update: if request.auth.uid == userId;
}
match /periods/{id} {
allow read, delete : if userDoc() == resource.data.user_id;
allow update : if userDoc() == request.resource.data.user_id;
allow create : if userDoc() == /databases/$(database)/documents/$(request.resource.data.user_id.path);
}
}
}

Firestore rules access to parent document

So i'm making an app with a friends system and trying to set up rules for firebase to handle reads & writes if the users is friends or not.
I'm very stuck at a particular call that i just don't have any idea on how to make.
My firestore is structured as follows:
users/userUID/places/documentsofplaces
each userdocument have some fields of the usual information, name, username, etc. and an array of friendsUID.
I have managed to get the first part down, that a user can only read and write if it's UID matches the documentUID, and looking in the friendslist a friend can only read but not write.
The next part, in the places collection, just throws me off, how can i get the parent document and compare the userUID to a UID in the friendslist?
This is what i have so far:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
// Allow write and read if user, and read if friend
allow write: if isUser(userId);
allow read: if isUser(userId) || isFriend();
function isUser(userId) {
return (request.auth.uid == userId);
}
function isFriend() {
return (request.auth.uid in resource.data.friendsList);
}
}
match /users/{userId}/places/{documents} {
allow write: if isUser(userId);
allow read: if isUser(userId) || isFriend(userId);
function isUser(userId) {
return (request.auth.uid == userId);
}
function isFriend(userId) {
return (request.auth.uid in get(/databases/$(database)/documents/users/userId.resource.data.friendsList));
}
}
}
}
Any help is greatly appreciated!
Your document get() should look more like this:
get(/databases/$(database)/documents/users/$(userId)).data.friendsList
You have to use variables with $(var) notation inside the document path. get() returns a Resoruce object with a data property. I suggest reading over the documentation for accessing other documents for more details.

How to prevent duplicate document entries into firestore based on fields in Firestore rules

I'm trying to prevent the creation of a new document if there is one that currently exists in the collection with two id fields with the same values as the incoming data.
Right now, I'm trying to attempt this with Firestore rules.
I've found way no way in the simulator to actually do this as I need to test the incoming data against every document in the collection.
I'm not quite sure what I'm missing here...
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
}
match /someCollection/{someCollectionID} {
allow write: if existingData().someValue != incomingData().someValue &&
existingData().anotherValue != incomingData().anotherValue
}
}
function existingData() {
return resource.data
}
function incomingData() {
return request.resource.data
}
}

How can Firestore check a collection to allow write (rules)

I use a collection called "admin" in Firestore to define which users can write new documents (image below).
At moment, it is controled just by software. I would like to add rules to Firestore. I tried the rule below but it didn't work. What would be the correct rules in that case ?
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth != null;
allow write: if get(/admin/{anyDocument}).data.userId == request.auth.uid;
}
}
}
I'd recommend instead having a users collection with an admin field that can be set to true/false. Then you can do something like:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth != null;
allow write: if get(/users/${request.auth.uid}).data.admin == true;
}
}
}
As far i know this is not possible with your current database structure. Because the push key is not accessible in firestore rules unless it is with in the admin node.
One way is to save the admin with their uid as key like admin/userID/data...
now you can access it
allow write: if get(/databases/$(database)/documents/admin/$(request.auth.uid)).data.userId == request.auth.uid;;

Resources