Reputation: 590
I developed this first Firestore application not really recognizing I'd not architected the permissions model, in fact I bolted it on later. (I am wide open to feedback on permissions best practices and/or how better to implement permissions rules, if that is the root of the problem.)
As a data model I have top level 'event' documents with 'note' documents in a collection below an event, and (after the fact) I created permission rules to allow editing notes if the user created the event. (See below.)
Now, when testing the code (on iOS) the application creates an event document, then soon afterwards attempts to add a snapshot listener to the notes sub-collection. It is receiving a "Missing or insufficient permissions" error (which, of course, it didn't when permissions were not enforced.)
This event create is done in a view controller listing all events, and then the snapshot listener query occurs in a pushed view controller (after a segue.) If I pop the child view controller then push it again, it works.
Confusingly, it also does not happen every time, it is intermittent. I wonder if there is some race condition occurring between permissions and the newly created event document. (If I add a brief interval before retry it works correctly that very next attempt.)
Firebase Firestore version 0.13.4, Swift 4, Xcode 10.0
(Edit: The effectively same code does not fail on Android, and also works when offline.)
The pertinent permissions are here:
// Access granted... via ownership
function accessGranted() {
return resource.data.userId == request.auth.uid;
}
function accessGrantedTo(container,rootId) {
return get(/databases/$(database)/documents/$(container)/$(rootId)).data.userId == request.auth.uid;
}
// Events...
match /events/{event} {
allow read, update, delete: if accessGranted();
allow create: if request.auth.uid != null;
}
match /events/{event}/notes/{note} {
allow read, update, delete: if accessGrantedTo("events",event);
allow create: if accessGrantedTo("events",event);
}
Upvotes: 2
Views: 521
Reputation: 590
I explored this topic with Google Firebase Firestore support [4-6103000026011] over the last month. After a number of back and forth conversations about "the object doesn't yet exist, wait for callback, etc.” it was finally elevated to engineering and the final resolution is "works as designed". Personally, I consider non deterministic behavior (works sometimes, fails sometimes) based on device speed a race condition. That said, maybe my scenario is not supported, so I am documenting here in case it helps others.
As I see it ... Firestore might be locally complete after the method call returns from a storage perspective, but its rules implementation is not. This all worked fine until I added the permissions rules, then things became non deterministic.
Every object created / listened to here is created by the same client in that client locally, yet fails for permissions reasons but works "after a moment/retry".
Bear in mind that maybe this is a tough scenario, I may well have created the problem through my requirements and choice of rules. Note:
Long story short I added a seriously bugly “retry (a limited number of times) if listening to something the code just created and it gets a permissions failure error” … and the code seems to succeed on the second try. Bugly, and I’m not proud, but “working”.
BTW: I did create a standalone example to reproduce this. It is iOS (although Android fails also, and fails most reliably on the automated testing virtual machines Android provides.)
Code: Standalone iOS code - Github Repository
Rules: Rule Set - Github Gist
Upvotes: 2