You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
114 lines
4.7 KiB
Plaintext
114 lines
4.7 KiB
Plaintext
rules_version = '2';
|
|
|
|
service cloud.firestore {
|
|
match /databases/{database}/documents {
|
|
|
|
// ===============================================================
|
|
// Helper Functions
|
|
// ===============================================================
|
|
function isAuthenticated() {
|
|
return request.auth != null;
|
|
}
|
|
|
|
function isOwner(userId) {
|
|
return isAuthenticated() && request.auth.uid == userId;
|
|
}
|
|
|
|
function isDocOwner() {
|
|
return isAuthenticated() && request.auth.uid == resource.data.authorUid;
|
|
}
|
|
|
|
function uidUnchanged() {
|
|
return !('authorUid' in request.resource.data) ||
|
|
request.resource.data.authorUid == request.auth.uid;
|
|
}
|
|
|
|
function uidNotModified() {
|
|
return !('authorUid' in request.resource.data) ||
|
|
request.resource.data.authorUid == resource.data.authorUid;
|
|
}
|
|
|
|
function hasRequiredFields(fields) {
|
|
return request.resource.data.keys().hasAll(fields);
|
|
}
|
|
|
|
function isValidUrl(url) {
|
|
return url is string &&
|
|
(url.matches("^https://.*") || url.matches("^http://.*"));
|
|
}
|
|
|
|
function isValidNewsItem(item) {
|
|
return item is map &&
|
|
item.keys().hasAll(['title', 'url', 'summary']) &&
|
|
item.title is string && item.title.size() > 0 && item.title.size() < 500 &&
|
|
item.url is string && item.url.size() < 1000 &&
|
|
item.summary is string && item.summary.size() > 0 && item.summary.size() < 2000;
|
|
}
|
|
|
|
function isValidNewsArray(arr) {
|
|
return arr is list && arr.size() <= 50; // Max 50 items per section
|
|
}
|
|
|
|
function isValidSynthesis(data) {
|
|
return hasRequiredFields(['week', 'createdAt', 'authorUid']) &&
|
|
data.week is string && data.week.size() > 0 && data.week.size() < 20 &&
|
|
data.createdAt is timestamp &&
|
|
data.authorUid is string && data.authorUid.size() > 0 &&
|
|
(!('majorAnnouncements' in data) || isValidNewsArray(data.majorAnnouncements)) &&
|
|
(!('financialSector' in data) || isValidNewsArray(data.financialSector)) &&
|
|
(!('otherEnterprises' in data) || isValidNewsArray(data.otherEnterprises)) &&
|
|
(!('publicSector' in data) || isValidNewsArray(data.publicSector)) &&
|
|
(!('generalPublic' in data) || isValidNewsArray(data.generalPublic)) &&
|
|
(!('sections' in data) || data.sections is list);
|
|
}
|
|
|
|
function isValidSettings(data) {
|
|
return hasRequiredFields(['theme', 'maxAgeDays', 'categories', 'maxItemsPerCategory']) &&
|
|
data.theme is string && data.theme.size() > 0 && data.theme.size() < 200 &&
|
|
data.maxAgeDays is number && data.maxAgeDays > 0 && data.maxAgeDays <= 365 &&
|
|
data.categories is list && data.categories.size() > 0 && data.categories.size() <= 20 &&
|
|
data.maxItemsPerCategory is number && data.maxItemsPerCategory > 0 && data.maxItemsPerCategory <= 50;
|
|
}
|
|
|
|
function isValidSource(data) {
|
|
return hasRequiredFields(['title', 'url', 'authorUid', 'createdAt']) &&
|
|
data.title is string && data.title.size() > 0 && data.title.size() < 200 &&
|
|
data.url is string && isValidUrl(data.url) && data.url.size() < 1000 &&
|
|
data.authorUid is string && data.authorUid.size() > 0 &&
|
|
data.createdAt is timestamp;
|
|
}
|
|
|
|
match /syntheses/{synthesisId} {
|
|
allow read: if isAuthenticated() && isDocOwner();
|
|
allow create: if isAuthenticated() &&
|
|
isValidSynthesis(request.resource.data) &&
|
|
uidUnchanged();
|
|
allow update: if isAuthenticated() &&
|
|
isDocOwner() &&
|
|
isValidSynthesis(request.resource.data) &&
|
|
uidNotModified() &&
|
|
request.resource.data.createdAt == resource.data.createdAt;
|
|
allow delete: if isAuthenticated() && isDocOwner();
|
|
}
|
|
|
|
match /sources/{sourceId} {
|
|
allow read: if isAuthenticated() && isDocOwner();
|
|
allow create: if isAuthenticated() &&
|
|
isValidSource(request.resource.data) &&
|
|
uidUnchanged();
|
|
allow update: if isAuthenticated() &&
|
|
isDocOwner() &&
|
|
isValidSource(request.resource.data) &&
|
|
uidNotModified() &&
|
|
request.resource.data.createdAt == resource.data.createdAt;
|
|
allow delete: if isAuthenticated() && isDocOwner();
|
|
}
|
|
|
|
match /settings/{userId} {
|
|
allow read: if isAuthenticated() && request.auth.uid == userId;
|
|
allow write: if isAuthenticated() && request.auth.uid == userId &&
|
|
isValidSettings(request.resource.data);
|
|
}
|
|
}
|
|
}
|