Firebase Firestore - security rule based on "where" query parameters - firebase

I'm trying to secure requests to a collection to allow any single get, but to allow list only if a specific key is matched.
Database structure is like this:
posts
post1
content: "Post 1 content"
uid: "uid1"
post2
content: "Post 2 content"
uid: "uid1"
post3
content: "Post 3 content"
uid: "uid2"
The Firestore query I'm making from Vue:
// Only return posts matching the requested uid
db
.collection("posts")
.where("uid", "==", this.uid)
The security rules I'd like to have would be something like this:
match /posts/{post} {
allow get: if true // this works
allow list: if [** the uid in the query **] != null
I want to do this so you can list the posts of a specific user if you know their uid but can't list all posts of the system.
Is there a way to access the requested .where() in the security rules or how can I write such rule or structure my data in this case?
Relevant & credits:
Seemingly, I can make a request on a query's limit, offset, and orderBy. But there's nothing on where. See: #1 & #2.
I copy-pasted much from this question. I don't see how the accepted answer answers the question. It seems like it answers another case where a user is allowed to list some other users' posts. That is not my case; in my case, what's public is public. So, it doesn't answer the main question in my case, it seems.

There's currently no way, using security rules, to check if a field is being used in query. The only thing you can do is verify that a document field is being used as a filter using only values you allow.
Instead, consider duplicating enough data into another collection organized like this:
user-posts (collection)
{uid} (document using UID as document ID)
posts (subcollection)
{postId} (documents using post ID as document ID)
This will require the client to call out a UID to query in order to get all the posts associated with that user. You can store as much information about the post documents as you like, for the purpose of satisfying the query.
Duplicating data like this is common in NoSQL databases. You might even want to make this your new default structure if you don't want your users to query across all posts at any given moment. Note that a collection group query naming the "posts" subcollection would still query across all posts for all users, so you'd have to make sure your security rules are set up so that this is enabled only when you allow it to happen.
Also note that UIDs are typically not hidden from users, especially if your web site is collaborative in nature, and you combine multiple users' data on a single page.

Related

Can firebase security rules be used to deliver the right data or its only here for malicious users?

Imagine the case I want to request a chat (a document) from two user Ids (user1 and user2).
Chat document contains an array with participants ids so users1 and users2 are both in particiants array.
If I request everything that contains user1 or user2 in participant array I may get user1 chats with other users too, not only user1 with user2.
Code for the example :
ref.where('participantsIds', 'array-contains-any', [u1.id , u2.id])
Now imagine I add a security rule that only allow getting chats if the user making the request is in participants array.
Rule for example :
allow read, write: if request.auth!=null && request.auth.uid in resource.data.participantsId
Is this a bad practice? Can I use rules to control data behaviors for each instance of the app?
The rule will probably add some milliseconds (or not even?) to the request no matter if the result is true or false.
And yes I know will affect cost and request quotas.
Thanks !!
Firebase security rules are not filters. They can't change the data that would be returned from a query. All they do is stop people from accessing data based on the logic you supply.
If you apply the rule shown above, then any query where the user requests any document where their uid is not in participantsId, or they make a query that does not use their uid as a required filter on participantsId, then the query will simply fail. This is something that you should pretty easily be able to observe for yourself.
I suggest reading: What does it mean that Firestore security rules are not filters?

Firestore Rules to check if user liking post exists in users Cloud Firestore

I'd like to check if the user liking the post is already present in our users cloud Firestore.
There are two different collections here, need to check if one's reference is present in another.
Users collection has user details - UID/Name/Email
Posts collection has details : Post Text / Post Likes / Post Comments
Where Likes is an Map which has userId of person who has liked the image and userName.
From backend its possible to exploit the posts collection and enter malicious value in post documents.
I am trying to achieve this for ON UPDATE condition : To check the user liking post exists in users collection.
------ Tried Below So far but it doesnt work --------------------
match /posts/items/{document}
allow update: if exists(/database/$(database)/documents/users/$(request.resource.data.likes.userId));
What I want is to check if the users liking are already existing or not.
This doesnt work because the likes column is an array and can have multiple objects.
Any help would be great.

Cloud Firestore rule for Read - on the basis of where clause [duplicate]

I'm trying to secure requests to a collection to allow any single get, but to allow list only if a specific key is matched.
Database structure is like this:
posts
post1
content: "Post 1 content"
uid: "uid1"
post2
content: "Post 2 content"
uid: "uid1"
post3
content: "Post 3 content"
uid: "uid2"
The Firestore query I'm making from Vue:
// Only return posts matching the requested uid
db
.collection("posts")
.where("uid", "==", this.uid)
The security rules I'd like to have would be something like this:
match /posts/{post} {
allow get: if true // this works
allow list: if [** the uid in the query **] != null
I want to do this so you can list the posts of a specific user if you know their uid but can't list all posts of the system.
Is there a way to access the requested .where() in the security rules or how can I write such rule or structure my data in this case?
Relevant & credits:
Seemingly, I can make a request on a query's limit, offset, and orderBy. But there's nothing on where. See: #1 & #2.
I copy-pasted much from this question. I don't see how the accepted answer answers the question. It seems like it answers another case where a user is allowed to list some other users' posts. That is not my case; in my case, what's public is public. So, it doesn't answer the main question in my case, it seems.
There's currently no way, using security rules, to check if a field is being used in query. The only thing you can do is verify that a document field is being used as a filter using only values you allow.
Instead, consider duplicating enough data into another collection organized like this:
user-posts (collection)
{uid} (document using UID as document ID)
posts (subcollection)
{postId} (documents using post ID as document ID)
This will require the client to call out a UID to query in order to get all the posts associated with that user. You can store as much information about the post documents as you like, for the purpose of satisfying the query.
Duplicating data like this is common in NoSQL databases. You might even want to make this your new default structure if you don't want your users to query across all posts at any given moment. Note that a collection group query naming the "posts" subcollection would still query across all posts for all users, so you'd have to make sure your security rules are set up so that this is enabled only when you allow it to happen.
Also note that UIDs are typically not hidden from users, especially if your web site is collaborative in nature, and you combine multiple users' data on a single page.

Store and Query Posts in Firestore in a performant way

So I need to store Posts that are created by Users, now the data modell is the problem, bringing all existing Posts in a Posts Collection with a field of creatorUserID will make it able to show posts belonging to a user.
Now a User has a Subcollection called Followers with the ID of people following, the problem with that is that Im not sure how a query would look to show only Posts of People that the User follows.
Also im worried about performance when there are 10mio+ Posts in the collection.
In order to query a document in Firestore the data you want to query by needs to be on the Document you want to query, there is no way of querying a collection by the data of a document from another collection. This is why your use-case is a bit tricky. It might not seem very elegant, but this is a way of solving it:
We use two collections, users and posts.
User
- email: string
- followingUserIDs: array with docIds of users you are following
Posts
- postName: string
- postText: string
- creatorUserID: string
To find all the posts belonging to all the users the logged in user is following, we can do the following in the code:
1 Retrieve the logged in user document
2 For each id in the "followingUserIDs" array I would query Firestore for the Posts. In JavaScript it would be something like:
followingUserIDs.map(followingUserId => {
return firestore.collection('Posts', ref => ref.where('creatorUserID',
'==', followingUserId));
})
3 Combine the result from all the queries into one array of posts

Cloud Firestore: Query secured documents by field content

EDIT: Seems to be an open issue in Firestore. Also see this post.
In Google Cloud Firestore, I want to model a collection of groups. Each group contains a name, the list of it's users and some secretGroupData. For me, the natural way to do this would be:
/groups
/group1 {
name: "Group 1"
users: { //object can be queried, simple array not
"user1": true,
"user5": true
}
secretGroupData: ...
}
/group2 { ... }
Given a user like user1, I want to query all groups he is member of. This query works fine:
groupsRef.where("users.user1", "==", true)
However, I want to secure the group data. This query only works, when all groups are readable for all users. When I protect the group to be readable only by the group members, by the rule
match /groups/{groupId} {
allow read: if resource.data.users[request.auth.uid] == true;
}
the above query does not work any more, because as soon as it sees a group where the current user is not a member of, read access is denied and the whole query fails.
What is the best solution for this problem in Firestore? Should I
tell Firestore to return only the allowed groups and ignore the other ones, instead of throwing an error? If so, how can I achieve this?
make the groups readable for all users and move the secretGroupData into subcollections, where I can then restrict the access to just the group members
add redundancy by adding the IDs of all groups of a user into the user's profile document (/users/user1/groupIds: ["group1"]), so I know the groups beforehand and can query them by ID
use a totally different solution?
Thank you very much for your ideas.

Resources