-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathfirestore.rules
More file actions
139 lines (109 loc) · 4.68 KB
/
firestore.rules
File metadata and controls
139 lines (109 loc) · 4.68 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
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;
}
// Validate string field with max length
function isValidString(value, maxLen) {
return value is string && value.size() <= maxLen;
}
// Validate array field with max length
function isValidArray(value, maxLen) {
return value is list && value.size() <= maxLen;
}
function isValidUser() {
return isAuthenticated() &&
request.resource.data.uid == request.auth.uid &&
request.resource.data.email == request.auth.token.email &&
isValidString(request.resource.data.displayName, 100) &&
isValidString(request.resource.data.email, 320);
}
function isValidNote() {
let data = request.resource.data;
return isAuthenticated() &&
data.userId == request.auth.uid &&
isValidString(data.title, 500) &&
isValidString(data.content, 500000) && // 500KB limit
isValidArray(data.tags, 50) &&
// Validate booleans
data.isPinned is bool &&
data.isArchived is bool &&
data.isFavorite is bool;
}
function isValidWorkspace() {
let data = request.resource.data;
return isAuthenticated() &&
data.userId == request.auth.uid &&
isValidString(data.name, 100) &&
isValidString(data.description, 500);
}
// Rate limiting helper (basic - checks timestamp)
function notTooFrequent() {
return !exists(/databases/$(database)/documents/rateLimits/$(request.auth.uid)) ||
resource.data.lastWrite < request.time - duration.value(1, 's');
}
// ==================== USER PROFILES ====================
match /users/{userId} {
// Users can only read their own profile
allow read: if isOwner(userId);
// Users can create their own profile with validation
allow create: if isOwner(userId) && isValidUser();
// Users can update their own profile (prevent changing critical fields)
allow update: if isOwner(userId) &&
request.resource.data.uid == resource.data.uid &&
request.resource.data.email == resource.data.email &&
isValidString(request.resource.data.displayName, 100);
// Prevent profile deletion via client
allow delete: if false;
// ==================== USER NOTES ====================
match /notes/{noteId} {
allow read: if isOwner(userId);
allow create: if isOwner(userId) && isValidNote();
allow update: if isOwner(userId) && isValidNote();
allow delete: if isOwner(userId);
}
// ==================== USER WORKSPACES ====================
match /workspaces/{workspaceId} {
allow read: if isOwner(userId);
allow create: if isOwner(userId) && isValidWorkspace();
allow update: if isOwner(userId) && isValidWorkspace();
allow delete: if isOwner(userId);
}
// ==================== USER ACTIVITY LOGS ====================
match /activity/{activityId} {
// Users can only read their own activity
allow read: if isOwner(userId);
// Activity logs can only be created by owner
allow create: if isOwner(userId) &&
request.resource.data.userId == request.auth.uid;
// Activity logs are immutable and cannot be deleted
allow update, delete: if false;
}
}
// ==================== SHARED NOTES ====================
match /shared-notes/{shareId} {
// Anyone can read public shared notes
allow read: if resource.data.isPublic == true;
// Only the owner can create shared notes
allow create: if isAuthenticated() &&
request.resource.data.userId == request.auth.uid &&
isValidString(request.resource.data.title, 500) &&
isValidString(request.resource.data.content, 500000);
// Only the owner can update their shared notes
allow update: if isAuthenticated() &&
resource.data.userId == request.auth.uid;
// Only the owner can delete their shared notes
allow delete: if isAuthenticated() &&
resource.data.userId == request.auth.uid;
}
// ==================== DENY ALL OTHER ACCESS ====================
match /{document=**} {
allow read, write: if false;
}
}
}