Firebase rules: Create if collection id does not exist - firebase

What I am trying to do is create a firebase rule that will allow create operations if an id does not exist in allow create. To be specific, I am using firestore, not the firebase real time db. I am really new to firebase, but have done some research before posting here. Most of the examples I see are around auth.
Consider the follow data structure. The name of the collection is called flags
US - //(This is the document ID. Not auto generated
- country: United Stages
- name: US
- colors: [red, white, blue]
UK -
- country: United Kingdom
- name: UK
- colors: [red, white, blue]
What I am trying to do write a rule, IF name in the posted data does not match an existing document ID. The reason why I opted to set my own document ID is because of this article
My current rule is:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
allow read: if isLoggedIn();
allow create: if ! exists(/databases/$(database)/documents/flags/$(inComing().name))
// functions
function isLoggedIn() {
return request.auth != null
}
function inComing() {
return request.resource.data
}
}
}
But when I try posting some data with a non existent document id, I am getting an access denied error. I am logged into the app so I know it is not failing on the first part of the rule.
In summary, my questions are:
How can i check if a document ID exists based on the posted datas name key?
So what i am try is allow create: if ! exists(/databases/$(database)/documents/flags/$(inComing().name)). In the simulator, I am using create, and setting the path to flags/US. The payload is:
{"__name__":"/databases/(default)/documents/flags/US","id":"US","data":{"name":"US"}}
If I run the simulator, i get access denied which is expected. But if i change the path to flags/CA or change the payload to {"name":"CA"}, which does not exist, I am getting getting an access denied.

Related

Firebase how check path contains value and allow read and write other path

At the moment i try to build a chat in flutter with google firebase.
Now i would like my database more secure.
That means only users (room_3) in chat can read and write data data.
Is it possible to check a path contains a user value
and if the user value is contains allow read a other path?
Here my database structer:
/chat/product_id/product_id_12345/chat_room_id/room_1/message_1/message2...
My idea is i add in 'room_1' the user id.
Then i check the user is contains in 'room_1'.
If the user is contains i allow read and write data the complete path (message_1/message_2...).
Here my example:
If you have any questions fell free to ask me.
Many thx.
In the security rules for the messages subcollection you can read the parent document and check whether the current user is in the user_id field with:
...
match /messages/{message} {
allow read:
if request.auth != null &&
request.auth.uid in
get(/databases/$(database)/documents/chat_room/$(chat_room_id)).data.user_id
}

Firebase Firestore security rules - read all nested docs based on resource (but it's null)

I think I'm doing something wrong but I can't figure which part... Thanks for all the clarification I can get.
So I have a collection named Bases that looks like this:
{
"id1": {
"name": "My base 1",
"roles": {
"idUser_123": {
"name": "John"
}
}
},
"id2": {
"name": "My base 2",
"roles": {
"idUser_456": {
"name": "Jane"
}
}
}
}
idUser_123 log in and want to access his collection. So I do:
db.collection('bases').get()
And I use a match rule to make sure John is not reading Jane's bases. And that's where I think I'm wrong cause I'm using rule for filter purpose.
match /bases/{document=**}{
allow read: if request.auth.uid in resource.data.roles;
}
Which failed because resource is null... I tried to do this:
match /bases/{baseId}{
allow read: if request.auth.uid in resource.data.roles;
}
This work in the simulator when requesting specific document but fails when I'm get() without baseId from client - cause I want them all.
So how am I supposed to handle this very basic use case (IMO)?
I can't put all user's baseId in user.token as it'll be over 1000 bytes quite fast.
I can make an other collection Roles to create a relation between a baseId and my user but that's seems overengineered for a simple use case.
Or I can make the request on a server and filter where("roles", "has", user.uid) ? Defeat the purpose of fetching data on client side very very quickly in my opinion...
Any recommendation on how to address this will be gladly appreciated! Thanks a lot :)
There are two problems here.
Firstly, your query is demanding to receive all of the documents in bases, but your rules do not allow anyone to simply receive all documents. It's important to realize that Firestore security rules are not filters. They do not strip documnts from a query - this actually does not scale. If a rule puts requirements on who can query certain documents based on their contents, the query will have to use a filter that matches what the rule requires.
Secondly, your data isn't structured in a way that the client can perform a filter of only certain documents based on the user's UID. Firestore doesn't have queries that can check for the existence of a map field. It can only check for map values. You might want to restructure your document data so that the client can actually perform a query with a filter to get only the documents for the current user.
If roles was an array of UIDs:
"id1": {
"name": "My base 1",
"roles": [ "idUser_123" ]
},
Then the client could filter on that field:
firebase.firestore()
.collection("bases")
.where("roles", "array-contains", uid)
And you could enforce the use of that filter in rules:
match /bases/{document=**} {
allow read: if request.auth.uid in resource.data.roles;
}
But you might want to do something different.

Firestore Security Rule: Check user input on get query

I'm creating a blog with Firestore. I have two collections called users and blogPosts. Each document in blogPosts contains name, createdAt, createdBy and password (plain string) field.
I want to create a security rule so clients can access a document only if they provide the correct document password.
According to an idea in this link, I wrote a rule like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /blogPosts/{postUid} {
allow write: if
request.resource.data.createdBy == request.auth.uid &&
request.resource.data.name is string &&
request.resource.data.name.size() > 2 &&
request.resource.data.name.size() < 32 &&
request.resource.data.password is string &&
request.resource.data.password.size() > 5 &&
request.resource.data.password.size() < 32
allow read: if
request.auth != null &&
request.resource.data.password == resource.data.password // <---- THIS LINE IS NOT WORKING
}
}
}
I get this error in playground with the rule above: Error: simulator.rules line [16], column [8]. Property resource is undefined on object. So it means we don't have resource.data on read queries.
How can I achieve my goal with Firebase security rules, so only clients that has blogPosts password can access to documents?
What you're trying to do isn't possible with security rules (and also isn't really "secure" at all). A client app can't simply pass along some password in a query. The only time input is checked is for document fields in a write operation, not document reads.
If you want to check a password, you will have to make some sort of API endpoint and require that the caller provide the password to that endpoint. Again, bear in mind that this is only as secure as your ability to keep that password a secret, because once it becomes known (perhaps by simply reverse engineering your app), anyone will be able to use it.

Firebase database security rule, check auth.email in resource.data

Error [firebase.firestore] FirebaseError: Missing or insufficient permissions.
I have an object that a few users are allowed to update. An admin will set their email, and these users who sign in with those email will be allowed to update.
These are the rules that I have tried:
allow update: if request.resource.data.managerEmails.val().contains(request.auth.email) && request.resource.data.id == resource.data.id;
allow update: if request.resource.data.managerEmails.contains(request.auth.email) && request.resource.data.id == resource.data.id;
allow update: if request.resource.data.managerEmails.includes(request.auth.email) && request.resource.data.id == resource.data.id;
The resource to update:
{
id: "someid",
...fields,
managerEmails: "abcde#email.com,anothermanager#email.com",
}
User auth who is updating:
{
uid: "rSTLnYD9aisyZJHQPC6sg7mlsZh1",
email: "abcde#email.com",
...
}
Update:
Using request.auth.uid has been working in other rules, but in this case, I have to use emails because the users might not have signed up yet.
Using Rules Playground, I get Property email is undefined on object. Maybe using request.auth.email is not possible?
I was having the same problem but switched from using firebase.auth.email to firebase.auth.token.email and it now works.
I strongly suggest doing two things.
Firstly, store UIDs instead of email addresses. A UID is the preferred way to identify a single Firebase Auth account and is always guaranteed to exist and be unique. Email addresses are not.
Secondly, store the list of users as an array type field instead of comma separated string. This will be much easier to manage overall.
After you do these two things, the rule becomes simple:
allow update: if resource.data.managerUids.hasAny([request.auth.uid]);
use: request.auth.token.email insted request.auth.email
documentation here

Firestore Security rule - Works on SIM but not in code (Missing permission for checking parent document ID)

My Firestore setup looks like the following:
match /Profile/{profile}/Sites/{site} {
allow create : if request.auth.uid == profile;
}
I want the document to be created under the profile of the user only! So I am looking for something like this, but it is not working, I am expecting the profile ID to be populated above.
The following works but then it doesn't check that the site is being created under that user only
allow create : if exists(/databases/$(database)/documents/Profile/$(request.auth.uid));
UPDATE: I tried the following condition on the 'create' document in simulator and it works there:
Profile/*myUIDHere*/Sites/{site}
My security rule which worked on simulator is :
match /Profile/{profile}/Sites/{site} {
allow create : if request.auth.uid == profile;
}
But it is not working when I am running the code from the phone - I am getting missing or insufficient permissions error. The code I am using is as follows:
Firestore.instance
.collection('Profile/${currentFirebaseUser.uid}/Sites')
.document(site.uID)
.setData(site.toJson())

Resources