Receiving Invalid glob match expression for wildcard security rule - google-cloud-firestore

I'm trying to implement the rule outlined here: https://stackoverflow.com/a/56047977/1604072
match /{path=**}/posts/{post} {
allow read: if request.auth.uid != null;
}
This is yielding this error: Invalid glob match expression. Glob matches
are permitted as the last segment in a match declaration path.
Is this the only/proper way to write this now?:
match /{path=**}{
match /collectionItems/{collectionItem} {
allow read: if request.auth.uid != null;
}
}

For future users, just found this bit of documentation. Not sure why I wasn’t able to find it originally:
Secure and query documents based on collection groups
In your security rules, you must explicitly allow collection group
queries by writing a rule for the collection group:
Make sure rules_version = '2'; is the first line of your ruleset. Collection group queries require the new recursive wildcard {name=**} behavior of security rules version 2.
Write a rule for you collection group using match /{path=**}/[COLLECTION_ID]/{doc}.

simply replace /{path=**}/ with /{prefix=**}/ and it should work
In the end it should look like this:
match /{prefix=**}/posts/{post} {
allow read: if request.auth.uid != null;
}

Related

Firestore rules unable to check if a reference exists

Here are my firestore rules:
match /databases/{database}/documents {
match /customers/{customerId} {
allow write: if exists(/databases/$(database)/documents/$(request.resource.data.referred_by));
allow read: if true;
}
}
The following data does not pass this rule(I was able to create the data from client app by removing the exists(/databases/$(database)/documents/$(request.resource.data.referred_by)):
So how do we deal with the reference field so that I can validate whether this reference exists already or not?
EDIT:
The type of the referred_by field is reference.
It seems that its a limitation of firestore at this stage. We are not able to validate whether a reference exists or not.
I further did some digging using the rules playground. The request.resource.data.referred_by has a path child. So I used $(request.resource.data.referred_by.path). Still that did not work because the $(request.resource.data.referred_by.path) resolves to customers%2FB85CSuRJQZWUKt2jgxOy (url-encoded).
I don't know of a way to url decode this in firestore rules.
So at this time I am changing my data models to use referred_by as a 'string' instead of a reference.
I hope this thing is resolved in future by firestore team.

Firestore security rules passing wildcard variable to path

I have the following security rules:
match /collection1/{doc_id} {
allow read: if (get(/databases/$(database)/documents/collection2/$(doc_id)).author ==
request.auth.uid);
}
What I am doing is that I am trying to pass the wildcard variable from the parent path doc_id into the path of get method. The read access of this doc in collection1 depends on the author field of a document with the same id in another collection collection2. I don't believe that the way I am passing doc_id as $(doc_id) is correct, as I get an error of: Property author is undefined on object
I have also tried (doc_id) and \doc_id, but they are syntaxilly wrong. How do I pass a wildcard variable to a path then?
You're missing a data in there, which is needed to get at the fields of the document:
get(/databases/$(database)/documents/collection2/$(doc_id)).data.author

Firestore SecurityRules get () issue

I'm trying to write a security rule for firestore DB and I'm having trouble for some reason. Does the syntax in the following code look wrong to anyone?
match /posts/{postType} {
allow read: if true;
allow write: if get(/databases/$(database)/documents/posts/$(postType)).data.creator_id ==
request.auth.uid;
}
I attached a screenshot of the path and the data structure. The problem I am getting is "get" is returning null.
Data & Path:
error:
You must change the rule.
Instead of:
allow write: if get(/databases/$(database)/documents/posts/$(postType)).data.creator_id == request.auth.uid;
You must write:
allow write: resource.data.creator_id == request.auth.uid;
request.resource.data.creator_id == request.auth.uid was the solution!
You're not using the simulator correctly. When asked to enter the path of the document to get, you must provide a path to an actual document. Do not type the wildcard from the match. The rules will determine the actual value of the wildcard at the time the rule is evaluated. If you enter "/posts/{postType}", you are telling it to literally load a document with the ID "{postType}", which doesn't exist.

Firestore Rules, allow update only certain fields

I have the following database collection '/player_profiles/{userId}'. I want to use the rule that only the request.auth.uid == userId unless they are only updating the field matches which is an array. I tried this, but it denied permissions
match /player_profiles/{userId}{
allow write: if userId == request.auth.uid;
allow update: if (request.resource.data.keys().hasOnly(["matches"]) && request.auth != null);
}
And here is the Flutter code that runs the update to add in the item to the matches array:
await DatabaseProvider()
.db
.collection(PLAYER_COLLECTION)
.document(widget.userProfile.userId)
.updateData({
'matches': FieldValue.arrayUnion([profile.userId])
});
Im new to firestore rules but i thought this would work
Was able to get the behavior I wanted with this:
match /player_profiles/{userId}{
allow write: if request.auth.uid == userId || (request.auth != null && request.resource.data.diff(resource.data).affectedKeys().hasOnly(["matches"]));
}
Looking at #Doug Stevenson's answer though I can add in making sure they only are allowed to put in their own userId into other peoples profiles.
request.resource.data.keys() always contains every field in the document being written, regardless of whether or not it's being updated. You should use the new MapDiff API instead to compare what's being written with what currently exists.
I think you will want to do something like this:
if
(request.resource.data.diff(request.resource.data).affectedKeys().hasOnly(["matches"]) &&
(request.resource.data.matches.toSet().difference(resource.data.matches) == [userId].toSet()) ||
request.auth == null;
I have not tested, but I think you get the idea. You will need to make liberal use of the linked API documentation for security rules to work with maps, sets, and lists effectively.

Firestore security rules - wildcarding Collection Names?

I have a set of Collections whose names all start with ABC and I want to write a single rule that applies to all of them regardless of what follows ABC. Something like:
match /ABC*/{anyid} {
allow read, write;
}
Is this possible? In the Rules Console there are no syntax errors highlighted, but the Simulator won't allow me to access the table with:
GET /ABC123/456
Any ideas?
As far as I know it is not currently possible to match on a partial collection (or document) name. It sounds like an interesting feature request though, so I recommend filing a feature request.
In the meantime, the only thing I can think of is matching all collections, and then testing the path through resource['__name__']:
match /53829635/{document} {
match /{col}/{doc} {
allow read: if resource['__name__'][5].matches('ABC.*')
}
}
The resource['__name__'] expression returns a Path, which can be indexes as an array to get the path segments. It has a form /databases/(default)/documents/collection/document, so the subcollection is at index 5. Since that is just a string, we can use matches on it. In this case I allow reading from any subcollection whose name starts with ABC.
Update: it turns out that you can also simply access the col wildcard, instead of looking up from the path. So this would work the same:
allow read: if col.matches('ABC.*')