-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfirestore.rules
More file actions
179 lines (166 loc) · 7.76 KB
/
firestore.rules
File metadata and controls
179 lines (166 loc) · 7.76 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
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Helper function to check if user is authenticated
function isAuthenticated() {
return request.auth != null;
}
// Helper function to check if the user is accessing their own document
function isOwner(userId) {
return isAuthenticated() && request.auth.uid == userId;
}
// Users collection
match /users/{userId} {
allow read: if isAuthenticated();
allow create: if isAuthenticated() && (
request.auth.uid == userId ||
(request.resource.data.isDemo == true && request.resource.data.name is string)
);
allow update: if isOwner(userId);
}
// Friends subcollection
match /users/{userId}/friends/{friendId} {
allow read: if isOwner(userId);
// Allow create for any authenticated user (for group member linking)
// The app logic ensures users can only add friends through valid flows
allow create: if isAuthenticated();
allow update, delete: if isOwner(userId);
}
// Groups collection - simplified rules
match /groups/{groupId} {
// Allow read for all authenticated users
allow read: if isAuthenticated();
// Allow create if authenticated
allow create: if isAuthenticated()
&& request.auth.uid in request.resource.data.members
&& request.auth.uid == request.resource.data.createdBy;
// Allow update if user is a member (existing or new)
allow update: if isAuthenticated() && (
request.auth.uid in resource.data.members ||
request.auth.uid in request.resource.data.members
);
// Allow delete only by creator
allow delete: if isAuthenticated() && request.auth.uid == resource.data.createdBy;
}
// Expenses collection - simplified rules
match /expenses/{expenseId} {
// Allow read if user is involved or is a group member
allow read: if isAuthenticated() && (
request.auth.uid in resource.data.involvedUsers ||
(resource.data.groupId != null &&
request.auth.uid in get(/databases/$(database)/documents/groups/$(resource.data.groupId)).data.members)
);
// Allow create if user is involved and is the payer
allow create: if isAuthenticated()
&& request.auth.uid in request.resource.data.involvedUsers
&& request.auth.uid == request.resource.data.paidBy;
// Allow update if user is involved
allow update: if isAuthenticated()
&& request.auth.uid in resource.data.involvedUsers
&& request.auth.uid in request.resource.data.involvedUsers;
// Allow delete if user is involved or group creator
allow delete: if isAuthenticated() && (
request.auth.uid in resource.data.involvedUsers ||
(resource.data.groupId != null &&
request.auth.uid == get(/databases/$(database)/documents/groups/$(resource.data.groupId)).data.createdBy)
);
}
// Settlements collection
match /settlements/{settlementId} {
allow read: if isAuthenticated() && (
request.auth.uid == resource.data.fromUserId ||
request.auth.uid == resource.data.toUserId ||
(resource.data.groupId != null &&
request.auth.uid == get(/databases/$(database)/documents/groups/$(resource.data.groupId)).data.createdBy)
);
allow create: if isAuthenticated()
&& (request.auth.uid == request.resource.data.fromUserId || request.auth.uid == request.resource.data.toUserId);
allow update, delete: if isAuthenticated() && (
request.auth.uid == resource.data.fromUserId ||
request.auth.uid == resource.data.toUserId ||
(resource.data.groupId != null &&
request.auth.uid == get(/databases/$(database)/documents/groups/$(resource.data.groupId)).data.createdBy)
);
}
// Activities collection
match /activities/{activityId} {
allow read: if isAuthenticated() && request.auth.uid in resource.data.involvedUsers;
allow create: if isAuthenticated()
&& request.auth.uid in request.resource.data.involvedUsers
&& request.auth.uid == request.resource.data.userId;
allow update, delete: if isAuthenticated() && request.auth.uid == resource.data.userId;
}
// Invitations collection (legacy email-based)
match /invitations/{invitationId} {
allow create: if isAuthenticated()
&& request.auth.uid == request.resource.data.fromUserId
&& request.resource.data.toEmail is string
&& request.resource.data.friendName is string;
allow read: if isAuthenticated();
allow delete: if isAuthenticated()
&& (request.auth.uid == resource.data.fromUserId || request.auth.token.email == resource.data.toEmail);
}
// Invite tokens collection (link-based)
match /inviteTokens/{token} {
// Anyone can read (to validate token before login)
allow read: if true;
// Only authenticated users can create their own tokens
allow create: if isAuthenticated()
&& request.auth.uid == request.resource.data.userId
&& request.resource.data.createdAt is string
&& request.resource.data.expiresAt is string;
// Only the token creator or the user who used it can update
allow update: if isAuthenticated()
&& (request.auth.uid == resource.data.userId || request.auth.uid == request.resource.data.usedBy)
&& request.resource.data.usedBy is string
&& request.resource.data.usedAt is string;
allow delete: if false; // Tokens are never deleted
}
// Restaurants collection - for group ordering feature
match /restaurants/{restaurantId} {
// Anyone authenticated can read restaurants
allow read: if isAuthenticated();
// Anyone authenticated can create restaurants
allow create: if isAuthenticated();
// Only creator can update/delete
allow update, delete: if isAuthenticated() && request.auth.uid == resource.data.createdBy;
}
// Ordering Sessions collection - for group ordering
match /orderingSessions/{sessionId} {
// Allow read if:
// - user is a participant or the creator, OR
// - session is active (allows friends to join via shared link)
allow read: if isAuthenticated() && (
request.auth.uid in resource.data.participants ||
request.auth.uid == resource.data.createdBy ||
resource.data.status == 'active'
);
// Allow create if authenticated
allow create: if isAuthenticated();
// Allow update if user is already a participant or the creator
// OR if they're joining the session (adding self to participants)
allow update: if isAuthenticated() && (
request.auth.uid in resource.data.participants ||
request.auth.uid == resource.data.createdBy ||
(request.auth.uid in request.resource.data.participants &&
resource.data.status == 'active')
);
// Only creator can delete
allow delete: if isAuthenticated() && request.auth.uid == resource.data.createdBy;
}
// Cart Items collection - for group ordering
match /cartItems/{cartItemId} {
// Allow read if user owns the item or is a participant in the session
allow read: if isAuthenticated() && (
request.auth.uid == resource.data.userId ||
request.auth.uid in get(/databases/$(database)/documents/orderingSessions/$(resource.data.sessionId)).data.participants
);
// Allow create if user owns the item
allow create: if isAuthenticated() && request.auth.uid == request.resource.data.userId;
// Allow update if user owns the item
allow update: if isAuthenticated() && request.auth.uid == resource.data.userId;
// Allow delete if user owns the item
allow delete: if isAuthenticated() && request.auth.uid == resource.data.userId;
}
}
}