-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfirestore.rules
More file actions
158 lines (126 loc) · 5.42 KB
/
Copy pathfirestore.rules
File metadata and controls
158 lines (126 loc) · 5.42 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
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ============================================================
// HELPER FUNCTIONS
// ============================================================
// Check if user is authenticated (including anonymous)
function isAuthenticated() {
return request.auth != null;
}
// Check if user owns this document (uid matches doc id)
function isOwner(userId) {
return isAuthenticated() && request.auth.uid == userId;
}
// Validate required string field
function hasString(field) {
return field in request.resource.data &&
request.resource.data[field] is string &&
request.resource.data[field].size() > 0;
}
// Validate required int field
function hasInt(field) {
return field in request.resource.data &&
request.resource.data[field] is int;
}
// ============================================================
// CHILDREN COLLECTION (Child Profiles)
// ============================================================
match /children/{userId} {
// User can only read/write their own profile
allow read: if isOwner(userId);
// Create: Must be owner, must have required fields
allow create: if isOwner(userId) &&
hasString('name') &&
hasInt('age') &&
request.resource.data.age >= 3 &&
request.resource.data.age <= 12 &&
hasString('preferredLanguage');
// Update: Must be owner
allow update: if isOwner(userId);
// Delete: Not allowed (preserve child data)
allow delete: if false;
// --------------------------------------------------------
// PROGRESS SUBCOLLECTION (Learning Progress Entries)
// --------------------------------------------------------
match /progress/{progressId} {
allow read: if isOwner(userId);
// Create progress entry with validation
allow create: if isOwner(userId) &&
hasString('topic') &&
hasInt('score') &&
hasInt('totalQuestions') &&
request.resource.data.score >= 0 &&
request.resource.data.totalQuestions > 0 &&
request.resource.data.score <= request.resource.data.totalQuestions;
// No updates or deletes to progress (immutable log)
allow update, delete: if false;
}
// --------------------------------------------------------
// STATS SUBCOLLECTION (Aggregated Statistics per Topic)
// --------------------------------------------------------
match /stats/{topic} {
allow read: if isOwner(userId);
// Create/Update stats
allow create, update: if isOwner(userId) &&
hasString('topic') &&
hasInt('totalAttempts') &&
hasInt('totalScore') &&
hasInt('totalPossible') &&
request.resource.data.totalAttempts >= 0 &&
request.resource.data.totalScore >= 0 &&
request.resource.data.totalPossible >= 0;
// No deletes
allow delete: if false;
}
// --------------------------------------------------------
// EVENTS SUBCOLLECTION (Analytics Events)
// --------------------------------------------------------
match /events/{eventId} {
allow read: if isOwner(userId);
// Create events with basic validation
allow create: if isOwner(userId) &&
hasString('name');
// No updates or deletes (immutable log)
allow update, delete: if false;
}
// --------------------------------------------------------
// SETTINGS SUBCOLLECTION (Parent-controlled settings)
// --------------------------------------------------------
match /settings/{settingId} {
// Child can read settings (e.g., YouTube settings)
allow read: if isOwner(userId);
// Only parent can write settings (via linkedChildren)
// For now, allow owner to write (will be restricted via parent link later)
allow write: if isOwner(userId);
}
}
// ============================================================
// PARENTS COLLECTION (for Parent Dashboard)
// ============================================================
match /parents/{parentId} {
// Parent can read/write own profile
allow read, write: if isOwner(parentId);
// Linked children subcollection
match /linkedChildren/{childId} {
allow read: if isOwner(parentId);
allow write: if isOwner(parentId);
}
}
// ============================================================
// GLOBAL CONTENT (read-only for all authenticated users)
// ============================================================
match /content/{document=**} {
allow read: if isAuthenticated();
allow write: if false; // Only admin via console
}
match /stories/{document=**} {
allow read: if isAuthenticated();
allow write: if false;
}
match /games/{document=**} {
allow read: if isAuthenticated();
allow write: if false;
}
}
}