Firestore security rule to limit reads to use collectionGroup(..)? - google-cloud-firestore

Several questions address whether knowing a Firestore uid allows hackers to edit that person's data, like this question and this question. My question is about security rules to filter when users can read another's data.
Specifically, I have a social media app that allows people to post data anonymously. My data model is /users/{user}/posts/{post}. I use db.collectionGroup("posts") to build a timeline of posts, (some anonymous, others with users' names).
Posts that are not anonymous have a valid uid, so it wouldn't be tough for a hacker to figure out someone's uid, which I'm not concerned about. My concern is whether a hacker could then query usersRef.document(uid).posts.getDocuments(); to get all the posts of that user, including the anonymous ones?
Because my app builds timelines from users "posts" collection, I can't write a rule that they can't read another user's posts. Can I write a rule that they can only read posts with collectionGroup?

That's not going to be possible with the way things are structured now. Here's the way you write a rule to allow collection group queries, as described in the documentation
match /{path=**}/posts/{post} {
allow read: if ...condition...;
}
The path wildcard in the rule explicitly allows all reads for all collections named "posts". The rule does not limit the reads to only collection group queries - any normal collection query on any "posts" will be allowed.
Bear in mind also that a collection group query would not hide any data from the caller compared to a normal collection query. The query results will still contain a reference to the full path of each document, which includes the document uid in the path.

Related

How to organize FireStore Collections and Documents based on app similar to BlaBlaCar rides

It's my first time working with FireStore. I'm working on a ridesharing app with Flutter that uses Firebase Auth where users can create trips and offer rides similarly to BlaBlaCar, where other users can send requests to join a ride. I’m having difficulty not only deciding the potential collections and paths to use, but also how to even structure it.
For simplicity at this stage, I want any user to be able to see all trips created, but when they go to their “My Rides” page, they will only see the rides that they’ve participated in. I would be grateful for any kind of feedback.
Here are the options I’ve considered:
Two collections, “Users” and “Trips”. The path would look something like this:
users/uid and trips/tripsId with a created_by field
One collection of “Users” and a sub-collection of “Trips". The path seems to make more sense to me, which would be users/uid/trips/tripId but then I don't know how other users could access all the rides on their home feed.
I'm inclined to go with the first option of two collections. Also very open to any other suggestions or help. Thanks.
I want any user to be able to see all trips created, but when they go
to their “My Rides” page, they will only see the rides that they’ve
participated in
I make the assumption that participating in a ride is either being the author or being a passenger of the ride.
I would go for 2 collections: one for users and one for trips. In a trip document you add two fields:
createdBy with the uid of the creator
participants: an Array where you store the author's uid and all the other participants uids (passengers)
This way you can easily query for:
All the rides
All the rides created by a user
All the rides for which a user is a participant, using arrayContains.
(Regarding the limit of 1 MiB for the maximum size for a document I guess this is not a problem because the number of passengers of a ride shouldn't be so huge that the Array fields size makes the document larger than 1 Mib!)
Note that the second approach with subcollections could also be used since you can query with collections group queries but, based on the elements in your question, I don't see any technical advantage.

Firestore Rule - limiting "list" access

I have a collection on which I want to provide list access, but only in a limited manner for most users.
All users should be able to do this: (the string valuex can be anything)
collection("XYZ").where("fieldx", "==", "valuex").get()
Only admins can get all the documents:
collection("XYZ").get()
Note that as valuex can be anything, at the end of the day all users can see all documents. The difference is that non-admins need to know what to query, admins don't, they get it all directly.
The only solution I have found is to force non-admins to write to a document the value they are querying, prior to calling get. The rules then are:
allow list: if isadmin() || resource.data.fieldx == getvaluex();
function isadmin() { return request.auth.token.get("admin", false); }
function getvaluex() { return get(/databases/$(database)/documents/users/$(request.auth.uid).data.valuex; }
That way all returned documents must have the same value for fieldx. But this solution 1) needs 1 additional write 2) adds a read in the rules and 3) in my case valuex is sensitive and I dont want the user to have to store it in Firestore.
So is there any better solution?
Is it possible for instance to limit the usage of an index to only some users? (both queries above actually have more where statements and require each a specific composite index).
Is it possible to compare the returned documents between each others to ensure they all have the same value for fieldx?
The way I would do it is this:
Don't allow non-admins to make those direct requests to the database at all.
Instead, have them send a request to a Firebase Http function.
The Http function has admin access to the db, it can accept any valuex non-null value.
It queries the db using that valuex, on behalf of the non-admin users, and returns the results.
This way, you can keep the documents in collection XYZ locked to non-admins in your Firestore Rules.
You can even keep sensitive data in those documents, since you have control on what you share with users. You can control that by choosing which fields your HTTP function will return to clients.
Mind you, Firebase function invocations are way cheaper than making additional writes/reads.
Firestore works well for easy/normalized access from clients to collections and documents.
What you are trying to do is pretty specific to your implementation of the these "lists".
You may create another collection (list_auth) that tracks the accesses to the list.
In the security access you can create a security rule for the collection that looks up the permissions of user into the list by accessing the list_auth collection.
https://firebase.google.com/docs/firestore/security/rules-conditions#access_other_documents

Cannot Query in Firestore Rules Using Claims

In my firestore rules, this allows me to query a list of all of a user's "organizations":
match /organizations/{orgId}{
allow read: if request.auth.uid in resource.data.members;
}
Where members is an array of user id's.
But, if I change this to work with claims:
match /organizations/{orgId}{
allow read: if orgId in request.auth.token.organizations;
}
Where organizations is a list of organization id's.
It seems to work with:
match /organizations/{orgId}{
allow read: if request.auth.token.organizations[orgId] == true;
}
It will let me access the document, but not a list of documents. Is there any way around this?
This doesn't work because security rules are not filters. (Be sure to read the docs in that link.) Also read more here.
When you perform a query on a collection (not a single document get), the filters on the query must absolutely match the requirements of the rules, before the contents of any documents are known. The security rules will not remove individual documents from the results. In this respect, Firestore queries are "all or nothing" - either all of the requested documents are known to match ahead of time, or the entire query fails.
What you have now suggests that each document ID should be read and individually compared to the list of organizations to determine which ones should be returned. Since rules won't do this filtering, it simply simply rejects the query altogether.
What you should probably do instead is simply make one get() for each org ID in the user's claims. It's definitely possible to read custom claims in the client app.

Is it possible to group multiple collections in mongodb

so I'm working with a database that has multiple collections and some of the data overlaps in the collection . In particular I have a collection called app-launches which contains a field called userId and one called users where the _id of a particular object is actually the same as the userId in app-launches. Is it possible to group the two collections together so I can analyze the data? Or maybe match the the userId in app-launches with the _id in users?
There is no definit answer for your question Jeffrey and none of the experts here can tell you to choose which technique over other just by having this information.
After going through various web pages over internet and mongo documentation and understanding the design patterns used in Mongo over a period of time, How I would design it depends on few things which I can try explaining it here in short.
if you have a One-To-One relation then always prefer to choose Embedding over Linking. e.g. User and its address (assuming user has only one address) thus you can utilize the atomicity (without worrying about transactions) as well easily fetch the records without too and fro to bring other information as in the case of Linking (like in DBRef)
If you have One-To-Many relation then you need to consider whether you can do the stuff by using Embedding (prefer this as explained the benefits in point 1). However, embedding would help you if you always want the information altogether e.g. Post/Comments where your requirement is to get the post and all of its comments by postId let say. But think of a situation where you need to get all the comments (and it related posts) which contains some specific tags in comments. in this case you should prefer Linking Because if you go via Embedding route then you would end up getting all the collection of comments for a post and you have to filter the desired comments.
for a Many-To-Many relations I would prefer two separate entities as well another collection for linking them e.g. Product-Category.
-$

How to balance quickness and redundancy in MongoDB data structures?

I am creating a MongoDB database with a users collection (with UserFiles in it) and a posts collection. Each post has tags and sharedFrom fields in it. I eventually plan to have users' search results influenced by what tags they normally post about and from which other users they often share posts. Would it be better to:
make a field in the UserFile document of each user that lists the post IDs made by the user?
make a field in the UserFile that documents that lists all the tags they have used and other users that they have sharedFrom?
make the search function look up the searchers activity that then influences the search results?
something I haven't thought of?