I was writing a rule for my "users" collection that looks like this:
match /users/{userId} {
// Returns true if auth's email is empty and user's
// insensitiveEmail is empty or non-existent
function emailDoesNotExist(resource) {
return request.auth.token.email.size() == 0 &&
(!fieldExists(resource, "insensitiveEmail") || resource.data.insensitiveEmail.size() == 0);
}
// Returns true if auth's email matches user's email
function emailMatches(resource) {
return request.auth.token.email == resource.data.insensitiveEmail;
}
// Returns true if the auth uid matches the resource's uid
function uidMatchesResource(resource) {
return request.auth.uid == resource.data.uid && request.auth.uid == resource.id;
}
allow get: if request.auth != null
&& request.auth.uid == userId
&& uidMatchesResource(resource)
&& (emailDoesNotExist(resource) || emailMatches(resource))
I’m getting [firestore/permission-denied] The caller does not have permission to execute the specified operation.
errors in a case where the user id being requested does not exist in the database.
Is that because I’m trying to do comparisons against resource
and resource
doesn’t exist?
And if that’s so, is there a way I can validate data in returned documents without preventing requests that result in 0 returned documents?
2
Answers
Normally you should not get:
Even if the document doesn’t exist. However, the following rule:
Cannot be evaluated as
true
if the document doesn’t exist and therefore the Firebase servers will reject the operation and you’ll indeed get a "Missing or Insufficient Permissions" when you set a listener on the non-existing document.So in your case, instead of directly checking the UID, you should use
exists
to check if the document exists.To allow your query to access documents that don’t exist, perform a
null
check againstresource
and short-circuit the result before the other checks are resolved.Additionally, your email check can be greatly simplified with the use of
Map#get()
.