I am working on securing my Firestore database and am having trouble with a collection wildcard. I have a root-level collection called study
, I want to allow reads only if the status
field is set to "published".
My current security rules are as follows
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
...
function isStudyPublished(studyId) {
return get(/databases/$(database)/documents/study/$(studyId)).data.status == "published";
}
...
// Match for participant permissioning
match /study/{studyId}/{document=**} {
// Deny all requests to create, update, or delete the study
allow create, update, delete: if false
// Allow the requestor to read the study if it is published
allow read: if isStudyPublished(studyId)
}
...
}
}
This security rule works great when requesting a single document, but not when doing a collection-level query. I get the error message: Null value error. for 'list'
Null value error. for 'list'
. I understand that Firestore rules do not act as a filter, however, I believe my query should only be requesting documents where the status
field equals "published".
Here are my two queries:
// successfully retrieves a single document
export const getStudy: GetStudy = (studyId) => {
return new Promise((resolve, reject) => {
getDoc(doc(firestore, COLLECTION_KEYS.STUDY, studyId))
.then((doc) => {
resolve(dataToStudy(doc.id, doc.data()));
})
.catch((error) => reject(error));
});
};
// fails to retrieve multiple documents
export const getStudies: GetStudies = (cursor) => {
const studies: StudyDoc[] = [];
return new Promise((resolve, reject) => {
let q = query(collection(firestore, COLLECTION_KEYS.STUDY), where("status", "==", "published"), orderBy(FIELD_KEYS.UPDATED));
if (cursor) q = query(q, startAfter(cursor));
getDocs(q)
.then((snapshot) => {
snapshot.forEach((doc) => {
studies.push(dataToStudy(doc.id, doc.data()));
});
return resolve([studies, snapshot.docs[snapshot.docs.length - 1]]);
})
.catch((error) => {
return reject(error);
});
});
};
My initial thought is that my collection query was requesting documents that would fail the security rules, but after staring at the query I don't think that's true...
As a test, I changed up my security rules to test what would pass using the help of the debug function . I ended up realizing that the check fails every time I reference the studyId
variable. As an example the following match statement fails.
match /study/{studyId}/{document=**} {
// Deny all requests to create, update, or delete the study
allow create, update, delete: if false
// Allow the requestor to read the study if it is published
allow read: if debug(debug(true) && (debug(studyId) != null));
}
When looking at the firestore-debug.log
, if I run the query that requests a single document I see the following logged:
bool_value: true
string_value: "rMxuVTJ0O15qPjMbpEU1"
bool_value: true
however, when running the collection query the following is logged:
bool_value: true
I've never run into the document id wildcard throwing an error like this. I can't determine if this is an error with my match statement or my query, but any help is appreciated.
I'm not sure what your use-case really is but you don't have to use get()
when trying to read data from the document being accessed or read. You should use resource.data
instead. See Firestore Rule below:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function isStudyPublished() {
return resource.data.status == "published";
}
// Match for participant permissioning
match /study/{studyId}/{document=**} {
// Deny all requests to create, update, or delete the study
allow create, update, delete: if false
// Allow the requestor to read the study if it is published
allow read: if isStudyPublished();
}
}
}
UPDATE:
When accessing your subcollection, your query should be matched with your security rules. You should have the document ID of the root collection to access your subcollection. Eg:
// Document-ID refers to study's document
let q = query(collection(db, "study", <DOCUMENT-ID>, <SUBCOLLECTION-NAME>), where(<FIELDNAME>, "==", <STRING>));
Security rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /study/{studyId} {
// Deny all requests to create, update, or delete the study
allow create, update, delete: if false
// Do all restrictions here for the study collection.
function isStudyPublished() {
return get(/databases/$(database)/documents/study/$(studyId)).data.status == "published";
}
// Rules for accessing subcollection of study collection.
match /subcollection-name/{docId} {
// Allow read if the document referenced from the study collection is published.
allow read: if isStudyPublished();
// Do all restrictions here for study's subcollection.
}
}
}
}
The sample rule above will only allow reads to the subcollection documents if the referenced document from the study
collection is tagged as published.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.