File size: 3,178 Bytes
fd4dc0d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Default deny
match /{document=**} {
allow read, write: if false;
}
// Helper functions
function isSignedIn() {
return request.auth != null;
}
function isOwner(userId) {
return isSignedIn() && request.auth.uid == userId;
}
function isValidId(id) {
return id is string && id.size() <= 128 && id.matches('^[a-zA-Z0-9_\\-]+$');
}
function incoming() {
return request.resource.data;
}
function existing() {
return resource.data;
}
// Helper for checking project collaboration
function isProjectCollaborator(projectId) {
let project = get(/databases/$(database)/documents/projects/$(projectId)).data;
return isOwner(project.creatorId) || (isSignedIn() && ('collaborators' in project) && (project.collaborators is list) && (request.auth.uid in project.collaborators));
}
// --- Users ---
match /users/{userId} {
// Allow reading user profiles if signed in (needed for collaborator lookup and sharing)
allow read: if isSignedIn();
allow create: if isOwner(userId);
allow update: if isOwner(userId) && (!('email' in incoming()) || incoming().email == existing().email);
}
// --- Projects ---
match /projects/{projectId} {
function canReadProject() {
return isOwner(resource.data.creatorId) || (request.auth.uid in resource.data.collaborators);
}
function canUpdateProject() {
// Owners can update everything. Collaborators can mostly update nothing except status/etc if we had any.
// For now, let's allow collaborators to update if they are in the list.
return isOwner(resource.data.creatorId) || (request.auth.uid in resource.data.collaborators);
}
allow read: if canReadProject();
allow create: if isSignedIn() && incoming().creatorId == request.auth.uid;
allow update: if canUpdateProject();
allow delete: if isOwner(resource.data.creatorId);
}
// --- Notes ---
match /notes/{noteId} {
function canReadNote() {
return isOwner(resource.data.userId) || isProjectCollaborator(resource.data.projectId);
}
function canWriteNote() {
let projectId = request.method == 'create' ? incoming().projectId : existing().projectId;
return isOwner(request.auth.uid) || isProjectCollaborator(projectId);
}
allow read: if canReadNote();
allow create: if isSignedIn() && incoming().userId == request.auth.uid && isProjectCollaborator(incoming().projectId);
allow update: if isSignedIn() && isProjectCollaborator(existing().projectId);
allow delete: if isSignedIn() && (isOwner(resource.data.userId) || isProjectCollaborator(resource.data.projectId));
}
// --- Categories ---
match /categories/{categoryId} {
allow read: if isOwner(resource.data.userId);
allow create: if isSignedIn() && incoming().userId == request.auth.uid;
allow update: if isOwner(resource.data.userId);
allow delete: if isOwner(resource.data.userId);
}
}
}
|