firestore rules matching parent data, only work in simulator - javascript

I'm trying to make a simple direct message part. I have a document with some meta informations and a subcollection with the single messages.
When I try to get the messages the rules are valid in the simulator, but in js I get everytime: Uncaught (in promise) FirebaseError: Missing or insufficient permissions.
The mongodb structure:
chatRooms
roomId
metaInformation (senderId/Name, recieverId/Name, timestamp, etc.)
messages
messageId
name
text
timestamp
The rules:
match /chatRooms/{chatRoom} {
allow create: if request.auth.uid != null;
allow update, delete: if resource.data.uid == request.auth.uid;
function isChatPartner() {
return parentDoc().receiverId == request.auth.uid || parentDoc().senderId == request.auth.uid;
}
function parentDoc() {
return get(/databases/$(database)/documents/chatRooms/$(chatroom)).data;
}
match /messages/{message} {
allow read: if isChatPartner();
}
}
The js-request:
db.collection("chatRoom").doc(_roomId).collection("messages").get().then(msg => {
console.log(msg);
})
Has anyone an idea what maybe could be wrong?

Your database and rules don't match. In the rule, the top level collection is called "chatRooms", but in your code, it's called "chatRoom". The collection names need to match exactly.
There's another problem. Your code is trying to get all of the documents in the subcollection, but the rules don't allow that. The rules are checking certain fields for access. This won't work in security rules, since rules are not filters (be sure to read and understand those docs). The query must only request documents that will definitely pass the rules - the rules will not check each document and exclude the ones that don't match.

Related

How to access my data field in Firebase Security Rules

Security Rules
I am trying to grant permission based at a document field:
match /users/{user}/{documents=**} {
allow read, write: if resource.data.uid == request.auth.uid
}
Firebase query
Here is how my query looks:
query(collection(db, "users", match.params.uid, "promotors"));
Error message
But I keep geting this message:
FirebaseError: Missing or insufficient permissions.
Your query is not in any way checking the data in a field in the documents, so it will never meet this part of your rules: resource.data.uid.
Instead what you seem to have is a case where the document ID matches the UID of the user, which you can check with:
match /users/{user}/{documents=**} {
allow read, write: if user == request.auth.uid
}
Also see the documentation on content owner only access.

Why do I get an insufficient permissions error while trying to delete an array element from firestore, yet I can delete the entire document

I have a collection in my firestore named "reports"
one of the fields in the reports map is an array of objects. These objects reference a photo in cloud storage. They look like this (url points to cloud storage):
{
id: uid value
url: some-url
}
my firebase rules are set up like this for the reports document:
match /databases/{database}/documents {
match /reports/{report} {
allow read: if request.auth != null && request.auth.uid == resource.data.userID;
allow create: if request.auth != null && request.resource.data.userID == request.auth.uid;
allow delete, update: if request.auth != null &&
request.auth.uid == resource.data.userID;
}
}
for some reason, I can delete the entire document if I want to proving that I have delete permission.....but when I attempt to delete an item from the photos array like this:
const reportRef = db.collection('reports')
reportRef.doc(activeReport.id).update({
photos: firebase.firestore.FieldValue.arrayRemove(photoToDelete)
})
I end up with an error stating:
Unhandled promise rejection: FirebaseError: Missing or insufficient permissions
Why? Haven't I given permission to update this document properly?
Go in Database ->
Rules ->
For development:
Change allow read, write: if false; to true;
Note: It's a quick solution for development purposes only because it will turn off all the security. So, it's not recommended for production.
For production:
If authenticated from firebase: Change allow read, write: if false; to request.auth != null;
In this case it was a bad parameter that was passed in. I needed to pass activeReport not activeReport.id.
This is a misleading error message. I would delete this question but stackoverflow doesn't want me to because an answer has been posted here. Silly.

Firebase authentication rules using array elements

I'm using Firebase with react native. In my app I allow users to share content with: read, copy or modify permissions, with either specific users or All.
Since Firebase does not provide an OR function for queries, I'm using an array to store the user:permission combinations, so that I can use the array-contains-any capability to query like this
.where("access", "array-contains-any", [user_id, `${user_id}:R`, `${user_id}:C`, `${user_id}:M`, "All_R", "All_C", "All_M"])
This allows me to query all content that is either owned by user_id OR shared to user_id OR shared to "All" with either R, C OR M permissions.
Now I want to do something similar for the authentication rules, (so that only users with "M" permission can modify etc.) by comparing the authenticated user ID with the user IDs in the array. In order to do that it would require a capability similar to "array-contains-any" and string concatenation for authentication rules.
Is that possible or do I need to do something different?
I ran a few tests, and think I got a subset of the types you have working. I'll walk through the steps below, so that you can build from it at the end.
First step is to make this query work:
ref.where("access", "array-contains", user_id)
This is fairly easy to secure with:
allow list: if request.auth != null && request.auth.uid in resource.data.access;
So anyone can read the document if their UID is mentioned in the access array.
The next step is to allow this query:
ref.where("access", "array-contains-any", [user_id, user_id+":R"]);
When we run this against the earlier rules, it is not allowed. That is because we're requesting a value that the rules are not aware of.
To make it work, change the rules to:
allow list: if request.auth != null && (
request.auth.uid in resource.data.access ||
request.auth.uid+":R" in resource.data.access
);
So now the conditions in the code and in the rules match again, and the read is allowed.
If we add another condition for the all-access rules:
ref.where("access", "array-contains-any", [user_id, user_id+":R", "All_R"]);
Then once again with the above rules our permission is denied, because we have a value in the query that the rules don't know.
To fix it, we check for that value in our rules too:
allow list: if request.auth != null && (
request.auth.uid in resource.data.access ||
request.auth.uid+":R" in resource.data.access ||
"All:R" in resource.data.access
);
And with those rules, the query succeeds again.
At this point you can add your other conditions in here, and it should work.

Can a user update the script in a console and access other users' data in my firebase code?

For context, I display my Firebase app's configuration in the app.js file (is this safe?) and in order to give a user their data I list the following code:
db.collection('whispers').where("sender", "array-contains",
userID).orderBy('time').onSnapshot(snapshot => {
let changes = snapshot.docChanges();
changes.forEach(change => {
renderSay(change.doc);
});
});
Could a user remove the 'where' condition and access all data in this collection? I only use anonymous sign in so nothing special required in accessing their own data besides using the same device and not clearing data.
I doubt that could be possible by reverse-engineering but the actual security lies in the security rules.
You can change them from the Firebase Console
Here is a video from Firebase and here is the documentation
service cloud.firestore {
match /databases/{database}/documents {
// Match any document in the 'users' collection
match /users/{userID} {
allow read: if request.auth.uid === userID;
allow write: if request.auth.uid === userID;
}
}
}
Using the above security rules will allow users to read and write their data ONLY.
So even if they reverse-engineer your code, it will harm their data only and other data will be safe.
For more details please check the links above and you will get familiar with wildcard [{userID} in this case]
PS: Only you or anyone with access to your Firebase Console can change the rules
EDIT: This Medium Article has many types of rules explained. Please have a read there too :D

firestore security rule resource.data is empty object

In firestore security rule, the resource.data is an emtpy object always, is this a bug or something ?
My firestore rules:
service cloud.firestore {
match /databases/{database}/documents {
match /hospitals/{document=**}{
// allow read :if resource.data.size() == 0; //this return true, resource.data is an empty object
allow read :if resource.data.name != null; // this doesn't work
}
}
}
My javascript:
auth().onAuthStateChanged((user) => {
if (user) {
//db is the firestore instance
db.collection('/hospitals').get()
.then(printResult)
} else {
}
})
this is my current database snapshot
solved :
thanks for Frank's answer
the issue rely on that firestore security doesn't evaluate the actual document value when we query a over multiple document , in my case
//this doesn't firestore doesnt' evaluate the documetn
db.collection('hospitals').get()
//this will work ,if you need to compare the actual value
db.document('hospitals/somehospital').get()
Security rules don't filter data by themselves. They merely enforce rules on what data a client can read. Your client is currently trying to read all hospitals. Since your security rules have restrictions on what data a client can read, they reject this operation.
You need to ensure that what your client requests is no more than what the security rules allow, by reading the data through a query that matches the security rules. So something like
db.collection('/hospitals')
.where("name", ">=", "")
.get()
.then(printResult)
Note that this does require that the document has a name field, otherwise the name can't be empty.
For more info, see:
the Firestore documentation on securing queries
Firestore select where is not null

Categories