-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathfirestore.rules
More file actions
82 lines (71 loc) · 3.15 KB
/
Copy pathfirestore.rules
File metadata and controls
82 lines (71 loc) · 3.15 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
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Helper functions
function isAuthenticated() {
return request.auth != null;
}
function isOwner(userId) {
return request.auth.uid == userId;
}
function isValidInterviewData() {
return request.resource.data.keys().hasAll(['userId', 'role', 'type', 'questions', 'createdAt']) &&
request.resource.data.userId == request.auth.uid &&
request.resource.data.role is string &&
request.resource.data.type in ['technical', 'behavioral', 'mixed'] &&
request.resource.data.questions is list &&
request.resource.data.questions.size() > 0 &&
request.resource.data.questions.size() <= 20;
}
function isValidFeedbackData() {
return request.resource.data.keys().hasAll(['interviewId', 'userId', 'totalScore', 'createdAt']) &&
request.resource.data.userId == request.auth.uid &&
request.resource.data.totalScore is number &&
request.resource.data.totalScore >= 0 &&
request.resource.data.totalScore <= 10;
}
function isValidUserUpdate() {
return request.resource.data.diff(resource.data).affectedKeys().hasOnly(['credits', 'lastActive', 'profileImage']);
}
// Users collection
match /users/{userId} {
allow read, write: if isAuthenticated() && isOwner(userId);
allow update: if isAuthenticated() && isOwner(userId) && isValidUserUpdate();
}
// Interviews collection
match /interviews/{interviewId} {
allow read: if isAuthenticated() && isOwner(resource.data.userId);
allow create: if isAuthenticated() && isValidInterviewData();
allow update: if isAuthenticated() && isOwner(resource.data.userId) &&
// Only allow updating specific fields
request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['status', 'responses', 'updatedAt', 'completedAt', 'feedbackId', 'finalScore']);
allow delete: if isAuthenticated() && isOwner(resource.data.userId);
}
// Feedback collection
match /feedback/{feedbackId} {
allow read: if isAuthenticated() && isOwner(resource.data.userId);
allow create: if isAuthenticated() && isValidFeedbackData();
allow update: if false; // Feedback should be immutable once created
allow delete: if isAuthenticated() && isOwner(resource.data.userId);
}
// Interview templates (public read, admin write)
match /interview_templates/{templateId} {
allow read: if true; // Public read access for templates
allow write: if false; // Only admin can write (handled server-side)
}
// User analytics (aggregated data)
match /analytics/{userId} {
allow read: if isAuthenticated() && isOwner(userId);
allow write: if false; // Only server-side writes
}
// System collections (admin only)
match /system/{document=**} {
allow read, write: if false; // Only server-side access
}
// Default deny all other documents
match /{document=**} {
allow read, write: if false;
}
}
}