Firestore rules - allow by path - firebase

I use Firebase's Firestore. I have a collection called "root" which has an item called "db".
I want to allow read and write access to everybody on exactly that item.
This is my Rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if
request.path == 'root/db';
}
}
}
This rule was there by default, except this line request.path == 'root/db'; which is my custom. I tried to set a timestamp rule and it works. But this by path does not work.
This is my request from the browser:
const ref = doc(dbApi, "root", "db");
console.log(ref.path);
let db: Database;
try {
db = (await getDoc(ref)).data() as Database;
} catch (err) {
console.log(err);
throw err;
}
ref.path logs root/db and err logs FirebaseError: Missing or insufficient permissions.
(Using "firebase": "9.6.4" javascript package in the app)
I've also tried some variations on this https://firebase.google.com/docs/reference/security/storage#path

Rather than use the catch all (/{document=**}) and then filter, just filter at the match statement instead:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /root/db { <-- change it here
allow read, write: if true;
}
}
}
While this works, you should further restrict the rules to limit the shape of this document. If your database is all based on a single document, consider the RTDB instead as it provides finer grain controls over the data in the database.

Related

Firebase Security Rules allow if datafield is less than x

I have an Events collection and a Users collection. My Event page increments a counter (in Users collection) when an event is uploaded. I want to limit the number of events per user to 10.
This what I have so far and it doesn't seem to work as expected:
rules_version = '2';
service cloud.firestore {
match /databases/collection/Events{
allow read;
allow write: if 50 >
get(/databases/collection/Users/$(request.auth.uid)).data.numberOfEvents;
}
}
//Tried this:
rules_version = '2';
service cloud.firestore {
// Do not change this
match /databases/{database}/documents {
// Path to your document
match /Users/{doc=**} {
allow read;
allow write;
}
match /Events/{doc=**} {
allow read;
allow write: if get(/databases/$(database)/documents/Users/$(request.auth.uid)).data.numberOfEvents < 50;
}
}
}
Actually the last one seems to work, let me test some more, thanks for all help!
It seems to work! Thanks!
You are not specifying the document path correct. Assuming events and users are both root level collections. Try the following rules:
rules_version = '2';
service cloud.firestore {
// Do not change this
match /databases/{database}/documents {
// Path to your document - collection names are case sensitive
match /events/{eventId} {
allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.numberOfEvents < 10;
}
}
}
You should however not allow users to write to numberOfEvents directly from client as they can reduce the number and try to add more events. Firestore triggers for Cloud functions might be useful here to increment/decrement the value securely from backend.

Check for username without login/user_credential

While a new user register I have to check if the desired username is already given. Therefor I check the username with the following query:
result = await FirebaseFirestore.instance
.collection('user')
.where('usernameName', isEqualTo: usernameEditController.text)
.get();
userNameExists = result.docs.isEmpty;
Code works fine and give back the right boolean if the username is already given.
Now there's a problem with the firebase rules. Because I ask for the username (code above) with being loged in I have to set the firebase firestore rules to:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
But with these rules everyone is allowed to read and write. Is there a possibility to check the username without changing the rules or with another query for the username?
You can allow all users to check the "users" collection even without authenticating. This is not an optimal solution because anyone will be able to read the user's collection.
Another thing you can do is to have another collection called "usernames", which you will add new users to, but the only data that will be there will be the username.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{collectionName}/{documentId} {
allow read : if (collectionName == "users" || request.auth != null);
allow write : if request.auth != null;
}
}
}

Firebase Firestore Database Rules: using arrays from another document

I am trying to make a web app that displays different elements based on what permissions I give to a user.
All the permissions are stored in the the Cloud Firestore database at /users/{userId} in the field "permissions", which is an array containing the permissionId's.
In /photo_libraries/{libraryId} I have a field called permissionId, which is a string.
I now want to give users that have the right permissionId to be able to read the document in /photo_libraries/{libraryId} that has that permissionId.
I've tried this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if request.auth.uid == userId;
}
match /photo_libraries/{libraryId} {
allow read: if get(/database/$(database)/documents/photo_libraries/$(libraryId)).data.permissionId in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.permissions;
}
}
}
But this doesn't seem to work, I'm quite new to the Firestore rules. Can anyone help me out?
P.S. This is how my database looks like:
This is the code I try to run:
const db = firebase.firestore(); const auth = firebase.auth();
auth.onAuthStateChanged(user => {
if (user) {
db.collection('photo_libraries').get().then(snapshot => {
// set up the UI
}, err => {
console.log(err.message);
});
} else {
// Logging out stuff
};
});
In the console I get the error message:
Missing or insufficient permissions.
Thank you,
Jonas
Try this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if request.auth.uid == userId;
}
match /photo_libraries/{libraryId} {
allow read: if resource.data.permissionId in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.permissions;
}
}
}
But a better solution would be to add the persmissions array as a custom claim, then you dont need to call get.
When querying Firestore make sure you are only querying the documents you can actually access. Look for "rules are not filter" on google and you will get plenty of hits on SO and in the official Firebase documentation.

Firestore Security Rule for users with their own collection

What would be the best Firestore rules when users should only be able to read and write their own collections, i.e. the collection name is the same as the userId? Currently I have the following which works, but is it secure enough?
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{userId}/{document=**} {
allow read, write: if request.auth.uid != null;
}
}
}
I also tried the following which didn't work.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{userId}/{document=**} {
allow read, write: if request.auth.uid == userId;
}
}
}
The first rule is indeed not sufficient, since there is no check on the collection name: any authenticated user can read all the collections named with any users' uid.
The second one should work for your requirements ("Users should only be able to read and write their own collections, i.e. the collection name is the same as the userId"). You are probably having an error somewhere else, e.g. with the code for writing or reading or for authenticating the user. You should share this code in order we double check it

How can I setup rules so I can write documents to firestore protected by uid?

I want to make it so I can have authenticated users write to a collection that is only read/write/create-able to that user only. I'm struggling with the most basic Firestore setup. My firestore rules look like this:
service cloud.firestore {
match /databases/{database}/documents {
match /{userId} {
allow read, write, create: if request.auth.uid == userId;
}
}
}
I'm assuming this will prevent read/writes/creates to the database unless the initial part of the path matches the UID of the logged in user.
My JavaScript code looks like this:
function addSomeData(data) {
console.log( "Sending data with: ", user.uid, data );
db.collection(user.uid).add({ data })
.then(function(docRef) {
console.log("Document written with ID: ", docRef.id);
})
.catch(function(error) {
console.error("Error adding document: ", error);
});
}
I definitely have user.id set correctly after successful login.
Using things this way always gives me this error in the console:
Error adding document: Error: Missing or insufficient permissions.
If I revert to the original rules like this then the document is successfully created:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
What I'm trying to do is have each collection start with the UID of the logged in user. So, a sample shape of the data might be this:
/ax323/brty/data="Hello"
/ax323/98da/data="Goodbye"
/br981/ha31/data="No comment"
So, ax323 is a UID in Firebase, as is br981. ax323 has two documents in the collection, while br981 has one.
Is the "shape" of my data the problem?
I don't really understand what the {document=**} means in the original rules, and whether I need to convert my authentication rule to something similar.
In your database rules you have used match /users/{userId} {
This rule will apply only to the document mathching that particular path.
So if your document path is /ax323/brty/data then your rules should be like
service cloud.firestore {
match /databases/{database}/documents {
match /{userId}/{document=**} {
allow read, write, create: if request.auth.uid == userId;
}
}
}
Also looking at your question, I can't get what brty means when you mentioned
/ax323/brty/data="Hello" ?

Resources