-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfirestore.rules
More file actions
209 lines (162 loc) · 9.98 KB
/
firestore.rules
File metadata and controls
209 lines (162 loc) · 9.98 KB
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ═══════════════════════════════════════════════════════════
// HELPER FUNCTIONS
// ═══════════════════════════════════════════════════════════
// Check if user is authenticated
function isAuthenticated() {
return request.auth != null;
}
// Check if the user owns this document
function isOwner(userId) {
return isAuthenticated() && request.auth.uid == userId;
}
// Check if user has admin role (set via custom claims)
function isAdmin() {
return isAuthenticated() && request.auth.token.admin == true;
}
// ═══════════════════════════════════════════════════════════
// USERS COLLECTION
// ═══════════════════════════════════════════════════════════
match /users/{userId} {
// Anyone authenticated can read any user (for public profiles)
allow read: if isAuthenticated();
// Only the owner can write to their own document
allow create: if isOwner(userId);
allow update: if isOwner(userId);
allow delete: if isOwner(userId) || isAdmin();
}
// ═══════════════════════════════════════════════════════════
// CONTRIBUTOR PROFILES COLLECTION
// ═══════════════════════════════════════════════════════════
match /contributorProfiles/{userId} {
// Anyone authenticated can read (for matching/network)
allow read: if isAuthenticated();
// Only owner can write
allow create, update: if isOwner(userId);
allow delete: if isOwner(userId) || isAdmin();
}
// ═══════════════════════════════════════════════════════════
// INITIATOR PROFILES COLLECTION
// ═══════════════════════════════════════════════════════════
match /initiatorProfiles/{userId} {
// Anyone authenticated can read
allow read: if isAuthenticated();
// Only owner can write
allow create, update: if isOwner(userId);
allow delete: if isOwner(userId) || isAdmin();
}
// ═══════════════════════════════════════════════════════════
// SKILLS COLLECTION (Read-only, admin managed)
// ═══════════════════════════════════════════════════════════
match /skills/{skillId} {
// Anyone can read skills
allow read: if true;
// Only admin can write (via Admin SDK)
allow write: if isAdmin();
}
// ═══════════════════════════════════════════════════════════
// MISSIONS COLLECTION
// ═══════════════════════════════════════════════════════════
match /missions/{missionId} {
// Public missions: anyone can read
// Private missions: only initiator and assigned contributors
allow read: if resource.data.isPublic == true ||
isOwner(resource.data.initiatorId) ||
isAuthenticated();
// Only authenticated users can create
allow create: if isAuthenticated();
// Only the initiator can update/delete
allow update, delete: if isOwner(resource.data.initiatorId) || isAdmin();
// Milestones subcollection
match /milestones/{milestoneId} {
allow read: if isAuthenticated();
allow write: if isOwner(get(/databases/$(database)/documents/missions/$(missionId)).data.initiatorId);
}
// Applications subcollection
match /applications/{applicationId} {
// Initiator can read all, contributor can read their own
allow read: if isOwner(get(/databases/$(database)/documents/missions/$(missionId)).data.initiatorId) ||
isOwner(resource.data.contributorId);
// Contributors can create applications
allow create: if isAuthenticated() && isOwner(request.resource.data.contributorId);
// Contributors can update their own (withdraw)
allow update: if isOwner(resource.data.contributorId);
// Initiator can update (accept/reject)
allow update: if isOwner(get(/databases/$(database)/documents/missions/$(missionId)).data.initiatorId);
}
// Assignments subcollection
match /assignments/{assignmentId} {
allow read: if isAuthenticated();
allow write: if isOwner(get(/databases/$(database)/documents/missions/$(missionId)).data.initiatorId) ||
isAdmin();
}
}
// ═══════════════════════════════════════════════════════════
// NOTIFICATIONS COLLECTION
// ═══════════════════════════════════════════════════════════
match /notifications/{notificationId} {
// Users can only read their own notifications
allow read: if isOwner(resource.data.userId);
// Users can update (mark as read) their own
allow update: if isOwner(resource.data.userId);
// Users can delete their own
allow delete: if isOwner(resource.data.userId);
// Only server can create (via Admin SDK)
allow create: if false;
}
// ═══════════════════════════════════════════════════════════
// CONVERSATIONS & MESSAGES
// ═══════════════════════════════════════════════════════════
match /conversations/{conversationId} {
// Only participants can read
allow read: if isAuthenticated() &&
request.auth.uid in resource.data.participants;
// Authenticated users can create conversations
allow create: if isAuthenticated() &&
request.auth.uid in request.resource.data.participants;
// Only participants can update
allow update: if isAuthenticated() &&
request.auth.uid in resource.data.participants;
// Messages subcollection
match /messages/{messageId} {
allow read: if isAuthenticated() &&
request.auth.uid in get(/databases/$(database)/documents/conversations/$(conversationId)).data.participants;
allow create: if isAuthenticated() &&
request.auth.uid in get(/databases/$(database)/documents/conversations/$(conversationId)).data.participants &&
isOwner(request.resource.data.senderId);
// Only sender can edit/delete their own messages
allow update, delete: if isOwner(resource.data.senderId);
}
}
// ═══════════════════════════════════════════════════════════
// ESCROW TRANSACTIONS (Admin SDK only)
// ═══════════════════════════════════════════════════════════
match /escrowTransactions/{transactionId} {
// Users can read transactions they're involved in
allow read: if isOwner(resource.data.initiatorId) ||
isOwner(resource.data.contributorId);
// Only server can write (via Admin SDK)
allow write: if false;
}
// ═══════════════════════════════════════════════════════════
// PROOF TASKS (Admin managed)
// ═══════════════════════════════════════════════════════════
match /proofTasks/{taskId} {
// Only active tasks are readable by contributors
allow read: if isAuthenticated() && resource.data.isActive == true;
// Only admin can manage
allow write: if isAdmin();
// Submissions subcollection
match /submissions/{submissionId} {
// Only the contributor can read their submission
allow read: if isOwner(resource.data.contributorId);
// Contributors can create their own submission
allow create: if isAuthenticated() && isOwner(request.resource.data.contributorId);
// Only admin can update (for grading)
allow update: if isAdmin();
}
}
}
}