-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfirestore.rules
More file actions
169 lines (144 loc) · 5.45 KB
/
firestore.rules
File metadata and controls
169 lines (144 loc) · 5.45 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
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ============================
// Helpers
// ============================
/**
* Verificar si el usuario pertenece a un grupo
* Los grupos se verifican en el documento permission-groups/{groupName}
*/
function userInGroup(groupName) {
let userEmail = request.auth.token.email;
return get(/databases/$(database)/documents/permission-groups/$(groupName)).data.users.hasAny([userEmail]);
}
/**
* Verificar si el usuario tiene un permiso
* Se busca en todos los grupos a los que pertenece
*/
function hasPermission(permissionNeeded) {
let userEmail = request.auth.token.email;
// Definir los permisos de cada grupo
let superadminPerms = [
'users.list', 'users.edit', 'users.delete', 'users.assign-groups',
'votes.delete', 'votes.view',
'applications.view', 'applications.create', 'applications.edit',
'applications.delete', 'applications.vote'
];
let userAdminPerms = [
'users.edit', 'users.assign-groups',
'votes.view',
'applications.view', 'applications.vote'
];
let voteAdminPerms = [
'votes.delete', 'votes.view',
'applications.view'
];
let miembroPerms = [
'applications.view', 'applications.vote'
];
let traineePerms = [
'applications.view'
];
// Verificar si el usuario está en algún grupo que tiene el permiso
return (
(userInGroup('superadmin') && superadminPerms.hasAny([permissionNeeded])) ||
(userInGroup('user-admin') && userAdminPerms.hasAny([permissionNeeded])) ||
(userInGroup('vote-admin') && voteAdminPerms.hasAny([permissionNeeded])) ||
(userInGroup('miembro') && miembroPerms.hasAny([permissionNeeded])) ||
(userInGroup('trainee') && traineePerms.hasAny([permissionNeeded]))
);
}
function userOwnsDocument() {
return request.auth.uid == resource.data.userId;
}
// ============================
// COLECCIÓN: permission-groups
// ============================
match /permission-groups/{groupName} {
// Leer: cualquier usuario autenticado, para listar grupos
allow read: if request.auth != null;
// Escribir: solo superadmin
allow write: if request.auth != null && userInGroup('superadmin');
}
// ============================
// COLECCIÓN: users
// ============================
match /users/{userId} {
function isSelf() {
return request.auth.uid == userId;
}
// Leer user:
// - el propio usuario
// - user-admin
// - superadmin
allow read: if request.auth != null && (
isSelf() ||
userInGroup('user-admin') ||
userInGroup('superadmin')
);
// Crear su propio documento
allow create: if request.auth != null && isSelf();
// Actualizar:
// - Usuario puede actualizar su propio perfil (sin tocar grupos)
// - user-admin/superadmin puede actualizar grupos de otros usuarios
allow update: if request.auth != null && (
// Usuario editando su propio perfil (sin grupos)
(isSelf() && !('grupos' in request.resource.data.diff(resource.data).affectedKeys())) ||
// user-admin/superadmin editando grupos
(hasPermission('users.assign-groups'))
);
// Eliminar usuario: solo superadmin
allow delete: if request.auth != null && userInGroup('superadmin');
}
// ============================
// COLECCIÓN: applications
// ============================
match /applications/{applicationId} {
function isLoggedIn() {
return request.auth != null;
}
// Leer aplicaciones: requiere permiso applications.view
allow read: if isLoggedIn() && hasPermission('applications.view');
// Crear / actualizar / borrar: requiere permiso applications.create/edit/delete
allow create: if isLoggedIn() && hasPermission('applications.create');
allow update: if isLoggedIn() && hasPermission('applications.edit');
allow delete: if isLoggedIn() && hasPermission('applications.delete');
// ============================
// SUB-COLECCIÓN: applications/{applicationId}/votes
// ============================
match /votes/{voteId} {
function isOwnerExisting() {
return request.auth.uid == resource.data.userId;
}
function isOwnerNew() {
return request.auth.uid == request.resource.data.userId;
}
// Leer votos: requiere permiso votes.view
allow read: if isLoggedIn() && hasPermission('votes.view');
// Crear voto: requiere permiso applications.vote
allow create: if isLoggedIn() &&
hasPermission('applications.vote') &&
isOwnerNew();
// Actualizar voto: requiere permiso applications.vote
allow update: if isLoggedIn() &&
hasPermission('applications.vote') &&
isOwnerExisting() &&
isOwnerNew();
// Eliminar voto:
// - dueño del voto
// - o usuario con permiso votes.delete
allow delete: if isLoggedIn() && (
isOwnerExisting() ||
hasPermission('votes.delete')
);
}
}
// ============================
// RUTA DE RESPALDO (Deny All)
// ============================
match /{document=**} {
allow read, write: if false;
}
}
}