Update Firestore security rules & indexes

This commit is contained in:
2021-09-01 17:31:17 +01:00
parent 913c3f529a
commit ece083e2d9
2 changed files with 126 additions and 25 deletions

87
firestore.indexes.json Normal file
View File

@@ -0,0 +1,87 @@
{
"indexes": [
{
"collectionGroup": "sets",
"queryScope": "COLLECTION",
"fields": [
{
"fieldPath": "owner",
"order": "ASCENDING"
},
{
"fieldPath": "title",
"order": "ASCENDING"
}
]
},
{
"collectionGroup": "sets",
"queryScope": "COLLECTION",
"fields": [
{
"fieldPath": "public",
"order": "ASCENDING"
},
{
"fieldPath": "owner",
"order": "ASCENDING"
},
{
"fieldPath": "title",
"order": "ASCENDING"
}
]
},
{
"collectionGroup": "progress",
"queryScope": "COLLECTION",
"fields": [
{
"fieldPath": "uid",
"order": "ASCENDING"
},
{
"fieldPath": "start_time",
"order": "DESCENDING"
}
]
},
{
"collectionGroup": "progress",
"queryScope": "COLLECTION",
"fields": [
{
"fieldPath": "duration",
"order": "ASCENDING"
},
{
"fieldPath": "uid",
"order": "ASCENDING"
},
{
"fieldPath": "start_time",
"order": "DESCENDING"
}
]
},
{
"collectionGroup": "progress",
"queryScope": "COLLECTION",
"fields": [
{
"fieldPath": "setIds",
"order": "ASCENDING"
},
{
"fieldPath": "uid",
"order": "ASCENDING"
},
{
"fieldPath": "start_time",
"order": "ASCENDING"
}
]
}
],
"fieldOverrides": []
}

View File

@@ -18,8 +18,9 @@ service cloud.firestore {
return get(/databases/$(database)/documents/sets/$(setId)).data.owner == request.auth.uid; return get(/databases/$(database)/documents/sets/$(setId)).data.owner == request.auth.uid;
} }
function isPublicSet(setId) { function isSetOwnerOrIsPublic(setId) {
return get(/databases/$(database)/documents/sets/$(setId)).data.public; let data = get(/databases/$(database)/documents/sets/$(setId)).data;
return data.public || data.owner == request.auth.uid;
} }
function verifyCreateFields(fields) { function verifyCreateFields(fields) {
@@ -43,8 +44,8 @@ service cloud.firestore {
return getRequestField(field, "") is string; return getRequestField(field, "") is string;
} }
function verifyNullType(field) { function verifyEmptyArrayType(field) {
return getRequestField(field, null) == null; return getRequestField(field, []) == [];
} }
match /users/{userId} { match /users/{userId} {
@@ -54,7 +55,8 @@ service cloud.firestore {
function verifyThemeValue() { function verifyThemeValue() {
let requestField = getRequestField("theme", "default"); let requestField = getRequestField("theme", "default");
return requestField == "default"; let themes = ["default", "red", "maroon", "green", "light-blue", "pink", "yellow", "orange"];
return requestField in themes;
} }
function verifyFieldTypes() { function verifyFieldTypes() {
@@ -74,9 +76,9 @@ service cloud.firestore {
match /groups/{groupId} { match /groups/{groupId} {
function verifyGroupFieldTypes() { function verifyGroupFieldTypes() {
return getRequestField("role", "") == "member" || return getRequestField("role", "member") == "member" ||
getRequestField("role", "") == "contributor" || getRequestField("role", "contributor") == "contributor" ||
getRequestField("role", "") == "owner"; getRequestField("role", "owner") == "owner";
} }
function getPossibleGroupFields() { function getPossibleGroupFields() {
@@ -88,7 +90,14 @@ service cloud.firestore {
allow read, delete: if isSignedIn() && (isSignedInUser() || getGroupRole(groupId) == "owner" || isAdmin()); // is current user's data or is owner of group or is admin allow read, delete: if isSignedIn() && (isSignedInUser() || getGroupRole(groupId) == "owner" || isAdmin()); // is current user's data or is owner of group or is admin
allow create: if isSignedIn() && isSignedInUser() && (getRequestField("role", "") == "member" || (isAdmin() && verifyGroupFieldTypes())) && verifyCreateFields(getPossibleGroupFields()); allow create: if isSignedIn() && isSignedInUser() && (getRequestField("role", "") == "member" || (isAdmin() && verifyGroupFieldTypes())) && verifyCreateFields(getPossibleGroupFields());
allow update: if isSignedIn() && ((getGroupRole(groupId) == "owner" || isAdmin()) && verifyGroupFieldTypes()) && verifyUpdateFields(getPossibleGroupFields()); allow update: if isSignedIn() &&
(getGroupRole(groupId) == "owner" || isAdmin()) &&
verifyGroupFieldTypes() &&
!(isSignedInUser()) && verifyUpdateFields(getPossibleGroupFields());
allow update: if isSignedIn() &&
isAdmin() &&
isSignedInUser() &&
getRequestField("role", "") == "owner";
} }
} }
@@ -117,11 +126,16 @@ service cloud.firestore {
match /sets/{setId} { match /sets/{setId} {
function verifyPublicField() {
return ((resource == null || resource.data == null || resource.data.groups == [] || resource.data.group == null) && verifyBoolType("public")) ||
(resource.data.groups != [] && resource.data.groups is list && getRequestField("public", true) == true);
}
function verifyFieldTypes() { function verifyFieldTypes() {
return verifyBoolType("public") && return verifyPublicField() &&
verifyStringType("title") && verifyStringType("title") &&
verifyStringType("owner") && verifyStringType("owner") &&
verifyNullType("groups"); verifyEmptyArrayType("groups");
} }
function getPossibleFields() { function getPossibleFields() {
@@ -142,20 +156,16 @@ service cloud.firestore {
return [[], fields[0]]; return [[], fields[0]];
} }
allow read, delete: if isSignedIn() && request.auth.uid == resource.data.owner; allow read: if isSignedIn() && (request.auth.uid == resource.data.owner || resource.data.public == true);
allow read: if isSignedIn() && resource.data.public;
allow create: if isSignedIn() && request.auth.uid == request.resource.data.owner && verifyCreateFields(getPossibleCreateFields()) && verifyFieldTypes(); allow create: if isSignedIn() && request.auth.uid == request.resource.data.owner && verifyCreateFields(getPossibleCreateFields()) && verifyFieldTypes();
allow update: if isSignedIn() && request.auth.uid == resource.data.owner && verifyUpdateFields(getPossibleUpdateFields()) && verifyFieldTypes(); allow update: if isSignedIn() && request.auth.uid == resource.data.owner && verifyUpdateFields(getPossibleUpdateFields()) && verifyFieldTypes();
allow delete: if isSignedIn() && request.auth.uid == resource.data.owner && (resource == null || resource.data == null || resource.data.groups == []);
match /vocab/{vocabId} { match /vocab/{vocabId} {
function verifySoundValue() {
return getRequestField("sound", vocabId) == vocabId;
}
function verifyVocabFieldTypes() { function verifyVocabFieldTypes() {
return verifyStringType("term") && return verifyStringType("term") &&
verifyStringType("definition") && verifyStringType("definition") &&
verifySoundValue(); verifyBoolType("sound");
} }
function getPossibleFields() { function getPossibleFields() {
@@ -176,10 +186,10 @@ service cloud.firestore {
return [[], fields[0]]; return [[], fields[0]];
} }
allow read, delete: if isSignedIn() && isSetOwner(setId); allow read: if isSignedIn() && isSetOwnerOrIsPublic(setId);
allow read: if isSignedIn() && isPublicSet(setId);
allow create: if isSignedIn() && isSetOwner(setId) && verifyCreateFields(getPossibleCreateFields()) && verifyVocabFieldTypes(); allow create: if isSignedIn() && isSetOwner(setId) && verifyCreateFields(getPossibleCreateFields()) && verifyVocabFieldTypes();
allow update: if isSignedIn() && isSetOwner(setId) && verifyUpdateFields(getPossibleUpdateFields()) && verifyVocabFieldTypes(); allow update: if isSignedIn() && isSetOwner(setId) && verifyUpdateFields(getPossibleUpdateFields()) && verifyVocabFieldTypes();
allow delete: if isSignedIn() && isSetOwner(setId);
} }
} }
@@ -196,16 +206,20 @@ service cloud.firestore {
return get(/databases/$(database)/documents/progress/$(progressId)).data.progress < get(/databases/$(database)/documents/progress/$(progressId)).data.questions.size(); return get(/databases/$(database)/documents/progress/$(progressId)).data.progress < get(/databases/$(database)/documents/progress/$(progressId)).data.questions.size();
} }
allow read: if isSignedIn() && isProgressUser(); allow read: if isSignedIn() && resource.data.uid == request.auth.uid;
allow delete: if isSignedIn() && isProgressUser() && isNotComplete(); allow delete: if isSignedIn() && resource.data.uid == request.auth.uid && isNotComplete();
match /terms/{vocabId} { match /terms/{vocabId} {
allow read: if isSignedIn() && isProgressUser() && !(isLanguageSwitched()); allow read: if isSignedIn() && isProgressUser() && (!(isLanguageSwitched()) || !(isNotComplete()));
} }
match /definitions/{vocabId} { match /definitions/{vocabId} {
allow read: if isSignedIn() && isProgressUser() && isLanguageSwitched(); allow read: if isSignedIn() && isProgressUser() && (isLanguageSwitched() || !(isNotComplete()));
} }
}
match /join_codes/{joinCode} {
allow get: if isSignedIn();
} }
} }
} }