Firestore collection group rules - google-cloud-firestore

I have few questions about collection group:
Is there way to execute request for collection group in Firestore simulator?
Can I add additional parameter for collection group rules for example the following rule is used for collection group
match /{prefix=**}/access/{email} {
allow read: if isSignedIn();
}
before access collection i have one more collection with user id, is is possible to add it as parameter to do some validations?

No, there currently is no way to simulate a collection group query in the Firestore console. (Actually there is no querying at all except individual document gets.)
There is no way, using security rules, to know any of the other path elements that come before access in the case you're showing. The prefix wildcard actually will not even contain any data at the time of execution.

Related

Is this firebase security rule redundant?

I have a collection of users, and I have a separate collection of usernames. In my collection usernames I store different usernames as doc_ids. That is, under collection usernames I can have doc_ids as first, second, third, and so on. Under each doc_id I store the following info:
{
ownerId: id,
dateUpdated: someDate
}
When I change some user's username, I execute a batch query, where I first delete the oldUsername doc, and then insert the newUsername doc with the appropriate fields. My question is regarding one of the security rules, related to the usernames collection. Do I need to check, if I already have such username (that is such doc_id). Do I need the following rule:
match /usernames/{username} {
allow create: if !exists(/databases/$(database)/documents/usernames/$(username))
}
I think this rule, is redundant since I am enforcing the uniqueness of collection ids, but I already saw it on a few other posts, so I wanted to check other people's opinions.
Yup, that rule does nothing as the create will only be triggered when the document doesn't exist yet. If the document already exists, its .update will be triggered.
This type of check is common in a .write, but not needed when you're using the more granular .create.

Does a wildcard have to be used for the document segment in a matching path of a Firestore collection group security rule?

db.collectionGroup('private')
.where('members', 'array-contains', userId)
.get()
.then(...)
This query fetches documents successfully if the relevant security rule is set like:
match /{path=**}/private/{document} {
allow read: if request.auth.uid in resource.data.members;
}
However, the similar rule below prevents the same query unexpectedly.
match /{path=**}/private/allowed {
allow read: if request.auth.uid in resource.data.members;
}
In this database,
private subcollections exist only under documents in the rooms collection.
Every private has only a single document with the ID "allowed".
This means /rooms/xxxxxxxx/private/allowed is the only possible path existing, where xxxxxxxx is an auto-assigned document ID.
Therefore specifying the path as /{path=**}/private/allowed looks correct to me.
In fact, "get" queries work in simulations in the playground, so is it a restriction only for collection group queries, or am I doing anything wrong?
FYI, more detailed database structure is described in another question of mine here.
Yes, it is required.
When you perform a collection group query, it's not possible to call out a specific document id in the query (e.g. "allowed"). The query is explicitly asking to consider all of the documents in all of the subcollections of the given name ("private"). Therefore, the rules must allow for those documents to be considered by adding the trailing wildcard.
You can certainly add a filter to the query if you want to get only certain documents with certain field values, but that filter can't be enforced in the rules.

Firestore security rules: check if array contains strings different from user's ID

I know how to check if an array contains a given string (as explained for example here). My requirement however is different: I have a document with an array updatedByHistoryArray written at server side that contains the history of the ids of all users who updated such a document, for example [id1, id2, ..., idn].
I would like to allow a delete operation for this document only if the latter has been updated exclusively by the user who wants to delete it.
So, for example, if a user with id24 wants to delete a document, the updatedByHistoryArray of this document has to be [id24, id24, ..., id24].
Is it possible to implement this requirement in the security rules of Firestore?
It sounds possible. Try using hasOnly() to see if the list field contains only a single user ID.
resource.data.updatedByHistoryArray.hasOnly([request.auth.uid])

firestore security rules get subcollection document

I'm facing insufficient permissions for this firestore security check.
service cloud.firestore
{
match /databases/{database}/documents
{
match /events/{eventID}
{
allow read:
if get(/databases/$(database)/documents/events/$(eventID)/authorizations/$(request.auth.uid)).data.EVENT_READ == true;
}
}
}
the get document is hardcoded in the firestore database and the simulator returns true but for the actual query returns insufficient privileges.
I tested and moved the authorizations subcollection to the same level as users collection and it works. Am i missing out anything?
Additional testing: Reading the document directly does not result in insufficient privileges. I'm testing to see if it's an issue with listing but to my knowledge read should cover both get and list in firestore security rules.
Update: Listing seems to be the issue here. I tried to list the entire collection with only one document and it results in the insufficient privileges.
Works:
this.angularFirestore.collection('events').doc(eventID).valueChanges();
Doesn't work (updated):
this.angularFirestore.collection('events', query => query.where('admins', 'array-contains', auth.uid)).valueChanges()
My firestore database:
/events/event1_id
- field 1: some string
- field 2: some string
- admins: array of uid strings
/authorizations/<uid> #uid for $(request.auth.uid)
- EVENT_READ: true
Update 2: Updated the doesn't work query string which I tried out. It is intriguing that if i move the /authorizations sub collection out to be the same level as /events collections, the query will not fail.
Your first query works because it's accessing the events collection with a specific document. That specific document meets the rules criteria, because you've arranged for the get() to allow it.
Your second query doesn't work because it's attempting to get all of the documents in events collection. Your rule does not specifically allow that. It looks like you expect your rule to filter out the events that aren't allowed access based on the contents an unknown number of other documents. You need to be aware that security rules are not filters. Please click through to the documentation and read that section. The client must only request documents that are known to be readable according to rules. It can't depend on rules to filter documents that are not allowed.
If you want to be able to query for all events that the current user has access to, your current database structure will not work. You will need to put all the relevant information in the events collection itself. This means you should consider something like putting the UID of each user that's allowed to read the event in the document itself, then filter on that field. Or have some other collection that you can query in this way.

Can you predefine allowed fields on documents in Meteor/MongoDB?

I searched everywhere for this but I can't find anything on that matter. If you don't predefine fields in MongoDB, couldn't a user with Insert permission then Insert a document with every kind of field he wants? Via Collection.insert? If I am thinking correctly here, is there a way to restrict this?
You can restrict inserting any kind of fields in these two ways:
Use collection.allow/deny(http://docs.meteor.com/#/full/allow) - the insert callback has a doc parameter, which contains the exact document that user wants to insert - you can check the content of it and deny the insertion if you spot fields that are not allowed.
Use SimpleSchema (https://github.com/aldeed/meteor-simple-schema) and Collection2 (https://github.com/aldeed/meteor-collection2) packages to define the schema and attach it to your collection - it will prevent the insertion if a document has additional/missing fields (or fields of not expected type).
This is my personal preference. Because fieldNames param in
Collections.update(userId, doc, fieldNames) only gives top-level fields in doc. So if you are having nested fields it is very hard to track.
So I don't use collection allow/deny rules. Without allow deny rules Collections.insert/Collections.update does nothing on client. Instead I am using Meteor methods to update/delete documents to collections, so I can decide which exact fields should update/insert.