I'm writing the security rules for my Firestore database, and I got to the point where I'm probably writing too many checks and the authorization automatically fails.
For example the rules for a specific path are
service cloud.firestore {
match /databases/{database}/documents {
match /pending/{userId} {
match /rate/{vendorId}/events/{eventId}/ratings/{rateId} {
allow write: if request.auth.uid == userId
&& exists(/databases/$(database)/documents/vendors/$(vendorId)) // The vendor must exist
&& exists(/databases/$(database)/documents/users/$(userId)/subscriptions/$(vendorId)) // The user must be subscribed to the vendor
&& exists(/databases/$(database)/documents/vendors/$(vendorId)/events/$(eventId)) // The event must exist
&& !exists(/databases/$(database)/documents/vendors/$(vendorId)/events/$(eventId)/ratings/$(userId)) // The user must not have already voted for the event
}
}
}
}
These rules apply when writing to /pending/{userId}/rate/{vendorId}/events/{eventId}/ratings/{rateId}
Removing one or a combination of rules makes everything work again.
I read on the documentation about a limit of 10 developer-defined functions here, but exists and get are listed as service-defined and should not be counted. Even if they were, here I'm only using five.
Is there a more efficient way to check the same fields? How do I calculate how much a single line counts into reaching the 10 functions limit?
Thanks
Firebase PM here: Currently, we limit the number of get()
and exists()
calls in a given rule evaluation to three, which is why you're seeing the behavior fail after adding the fourth. I'll make sure the docs are appropriately updated to include this info.
EDIT (4/2/18): These limits are now documented: https://firebase.google.com/docs/firestore/security/rules-structure#security_rule_limits
EDIT (5/14/18): We increased the limit to 10: https://firebase.google.com/docs/firestore/security/rules-structure#security_rule_limits
get()
and exists()
calls are more computationally expensive than "normal" rule evaluations, and we want to ensure a tight bound on evaluation time, so as to not slow down incoming requests. I'm pretty sure we can increase the number greater than three, but be aware that we'll look up all of these keys/values and it may take a little longer/cost a little more to evaluate each request.
Note that in this specific case, you're able to do this with three calls:
service cloud.firestore {
match /databases/{database}/documents {
match /pending/{userId} {
match /rate/{vendorId}/events/{eventId}/ratings/{rateId} {
// Only allow a document to be created
allow create:
// The user must not have already voted for the event
if request.auth.uid == userId
&& request.auth.uid == rateId
// The vendor must exist
&& exists(/databases/$(database)/documents/vendors/$(vendorId))
// The user must be subscribed to the vendor
&& exists(/databases/$(database)/documents/users/$(userId)/subscriptions/$(vendorId))
// The event must exist
&& exists(/databases/$(database)/documents/vendors/$(vendorId)/events/$(eventId));
// Not necessary unless you want to allow updates
allow update: if ...;
}
}
}
}