-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfirestore.rules
More file actions
192 lines (160 loc) · 6.88 KB
/
Copy pathfirestore.rules
File metadata and controls
192 lines (160 loc) · 6.88 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
rules_version = '2';
service cloud.firestore {
// Get the current user's document
function getCurrentUser() {
return get(/databases/$(database)/documents/users/$(request.auth.uid));
}
// Get the current user's role, supporting both read and write operations
function getUserRole() {
// For writes that include a role field, use the new role being written
return request.resource != null && request.resource.data.role is string
? request.resource.data.role
: request.auth != null && exists(/databases/$(database)/documents/users/$(request.auth.uid))
? get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role
: null;
}
// Check if a given role is super admin
function hasSuperAdminRole(role) {
return role == 'super_admin';
}
// Check if a given role is admin or super admin
function hasAdminRole(role) {
return ['admin', 'super_admin'].hasAny([role]);
}
// Check if a given role is agent, admin, or super admin
function hasAgentRole(role) {
return ['agent', 'admin', 'super_admin'].hasAny([role]);
}
// Check if current user is super admin
function isSuperAdmin() {
return hasSuperAdminRole(getUserRole());
}
// Check if current user is admin or super admin
function isAdmin() {
return hasAdminRole(getUserRole());
}
// Check if current user is agent, admin, or super admin
function isAgent() {
return hasAgentRole(getUserRole());
}
// Validate role transitions when updating user roles
function isValidRoleTransition(oldRole, newRole) {
// No role change is always valid
return oldRole == newRole || (
// Only super admins can assign super_admin role
(newRole == 'super_admin' && hasSuperAdminRole(getUserRole())) ||
// Only admins and super admins can assign admin role
(newRole == 'admin' && hasAdminRole(getUserRole())) ||
// Only admins and super admins can assign agent role
(newRole == 'agent' && hasAdminRole(getUserRole())) ||
// Only super admins can modify other super admins
(oldRole == 'super_admin' && hasSuperAdminRole(getUserRole())) ||
// Only admins and super admins can modify other admins
(oldRole == 'admin' && hasAdminRole(getUserRole()))
);
}
// Check if user has admin or agent privileges
function isAdminOrAgent() {
return isAdmin() || isAgent();
}
function isAuthenticated() {
return request.auth != null;
}
function isOwner(userId) {
return request.auth != null && request.auth.uid == userId;
}
// Validate order update fields
function isValidOrderUpdate() {
// Only allow specific fields to be updated
let allowedFields = ['status', 'deliveryNotes', 'deliveryDate'];
let isValid = request.resource.data.diff(resource.data).affectedKeys().hasOnly(allowedFields);
// Status transition validation
let statusValid = request.resource.data.status is string && resource.data.status is string
? (
// Define valid statuses inline
['pending', 'processing', 'shipped', 'delivered', 'cancelled'].hasAny([request.resource.data.status]) &&
// Status transition rules
(
// Can always stay in same status
request.resource.data.status == resource.data.status ||
// Can move from pending to processing
(resource.data.status == 'pending' && request.resource.data.status == 'processing') ||
// Can move from processing to shipped
(resource.data.status == 'processing' && request.resource.data.status == 'shipped') ||
// Can move from shipped to delivered
(resource.data.status == 'shipped' && request.resource.data.status == 'delivered') ||
// Can cancel from any state except delivered
(resource.data.status != 'delivered' && request.resource.data.status == 'cancelled')
)
)
: true; // If no status change, consider it valid
// Delivery date validation
let dateValid = request.resource.data.deliveryDate is timestamp
? request.resource.data.deliveryDate >= request.time
: true; // If no date change, consider it valid
return isValid && statusValid && dateValid;
}
// Validate user profile updates
function isValidProfileUpdate() {
// Only allow specific fields to be updated
let allowedFields = ['displayName', 'phoneNumber', 'photoURL'];
// Check if only allowed fields are being updated
return request.resource.data.diff(resource.data).affectedKeys().hasOnly(allowedFields);
}
match /databases/{database}/documents {
// Super admins have full access to all collections
match /{document=**} {
allow read, write: if isSuperAdmin();
}
// Customers collection rules
match /customers/{customerId} {
// Allow read access to authenticated users
allow read: if isAuthenticated();
// Allow create/update/delete for admin/super_admin
allow create, update, delete: if isAdminOrAgent();
// Additional validation for customer documents
allow create: if (
request.resource.data.name is string &&
request.resource.data.name.size() > 0 &&
request.resource.data.email is string &&
request.resource.data.email.matches('^[^@]+@[^@]+\\.[^@]+$') &&
request.resource.data.phone is string &&
request.resource.data.phone.size() >= 10
);
// Allow updates to all fields except id and timestamps
allow update: if (
!('id' in request.resource.data) &&
!('createdAt' in request.resource.data) &&
!('updatedAt' in request.resource.data)
);
}
match /vehicles/{vehicleId} {
allow read: if isAuthenticated() && (isSuperAdmin() || isAdmin() ||
(isAgent() && resource.data.driverId == request.auth.uid));
allow create: if isAuthenticated() && isAdmin();
allow update: if isAuthenticated() &&
(isAdmin() ||
(resource.data.driverId == request.auth.uid &&
request.resource.data.diff(resource.data).affectedKeys().hasOnly(['lastKnownLocation'])));
allow delete: if isSuperAdmin();
}
match /users/{userId} {
allow read: if request.auth != null;
allow create, update, delete: if isAdmin();
}
match /products/{productId} {
allow read: if isAuthenticated();
allow create, update, delete: if isAuthenticated();
}
match /categories/{categoryId} {
allow read: if isAuthenticated();
allow create, update, delete: if isAdmin();
}
match /orders/{orderId} {
allow read: if isOwner(resource.data.userId) || isAdmin();
allow create: if isAuthenticated() && request.resource.data.userId == request.auth.uid;
allow update: if isOwner(resource.data.userId) || isAdmin();
allow delete: if isAdmin();
}
}
}