@edgedev/firebase 1.6.3 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +465 -20
  2. package/edgeFirebase.ts +366 -232
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -9,7 +9,8 @@ A Vue 3 / Nuxt 3 Plugin or Nuxt 3 global composable for firebase authentication
9
9
  **[Firestore Basic Document Interactions](#firestore-Basic-document-interactions)**
10
10
  **[Firestore Snapshot Listeners](#firestore-snapshot-listeners)**
11
11
  **[Firestore Static Collection Data](#firestore-static-collection-data)**
12
- **[Await and response](#await-and-responses)**
12
+ **[Await and response](#responses)**
13
+ **[Firestore Rules](#firestore-rules)**
13
14
 
14
15
  # Installation
15
16
 
@@ -104,17 +105,140 @@ const edgeFirebase = inject("edgeFirebase");
104
105
  </script>
105
106
  ```
106
107
 
108
+ ### Firebase Trigger function.
109
+
110
+ User mangement requires setting up in this firestore trigger function and helper functions in your firebase functions:
111
+
112
+ ```javascript
113
+ const functions = require('firebase-functions')
114
+ const admin = require('firebase-admin')
115
+ admin.initializeApp()
116
+ const db = admin.firestore()
117
+
118
+ // START @edge/firebase functions
119
+ exports.updateUser = functions.firestore.document('staged-users/{docId}').onUpdate((change, context) => {
120
+ const eventId = context.eventId
121
+ const eventRef = db.collection('events').doc(eventId)
122
+ const stagedDocId = context.params.docId
123
+ let newData = change.after.data()
124
+ const oldData = change.before.data()
125
+ return shouldProcess(eventRef).then((process) => {
126
+ if (process) {
127
+ // Note: we can trust on newData.uid because we are checking in rules that it matches the auth.uid
128
+ if (newData.userId) {
129
+ const userRef = db.collection('users').doc(newData.userId)
130
+ setUser(userRef, newData, oldData, stagedDocId).then(() => {
131
+ return markProcessed(eventRef)
132
+ })
133
+ }
134
+ else {
135
+ if (newData.templateUserId) {
136
+ newData.isTemplate = false
137
+ const templateUserId = newData.templateUserId
138
+ newData.meta = newData.templateMeta
139
+ delete newData.templateMeta
140
+ delete newData.templateUserId
141
+ if (Object.prototype.hasOwnProperty.call(newData, 'subCreate') && Object.values(newData.subCreate).length > 0) {
142
+ const subCreate = newData.subCreate
143
+ delete newData.subCreate
144
+ db.collection(subCreate.rootPath).add({ [subCreate.dynamicDocumentField]: newData.dynamicDocumentFieldValue }).then((addedDoc) => {
145
+ db.collection(subCreate.rootPath).doc(addedDoc.id).update({ docId: addedDoc.id }).then(() => {
146
+ delete newData.dynamicDocumentFieldValue
147
+ const newRole = { [`${subCreate.rootPath}-${addedDoc.id}`]: { collectionPath: `${subCreate.rootPath}-${addedDoc.id}`, role: subCreate.role } }
148
+ if (Object.prototype.hasOwnProperty.call(newData, 'collectionPaths')) {
149
+ newData.collectionPaths.push(`${subCreate.rootPath}-${addedDoc.id}`)
150
+ }
151
+ else {
152
+ newData.collectionPaths = [`${subCreate.rootPath}-${addedDoc.id}`]
153
+ }
154
+ const newRoles = { ...newData.roles, ...newRole }
155
+ newData = { ...newData, roles: newRoles }
156
+ const stagedUserRef = db.collection('staged-users').doc(templateUserId)
157
+ return stagedUserRef.set({ ...newData, userId: templateUserId }).then(() => {
158
+ const userRef = db.collection('users').doc(templateUserId)
159
+ setUser(userRef, newData, oldData, templateUserId).then(() => {
160
+ return markProcessed(eventRef)
161
+ })
162
+ })
163
+ })
164
+ })
165
+ }
166
+ else {
167
+ const stagedUserRef = db.collection('staged-users').doc(templateUserId)
168
+ return stagedUserRef.set({ ...newData, userId: templateUserId }).then(() => {
169
+ const userRef = db.collection('users').doc(templateUserId)
170
+ setUser(userRef, newData, oldData, templateUserId).then(() => {
171
+ return markProcessed(eventRef)
172
+ })
173
+ })
174
+ }
175
+ }
176
+ }
177
+ return markProcessed(eventRef)
178
+ }
179
+ })
180
+ })
181
+
182
+ function setUser(userRef, newData, oldData, stagedDocId) {
183
+ // IT's OK If "users" doesn't match exactly matched "staged-users" because this is only preventing
184
+ // writing from outside the @edgdev/firebase functions, so discrepancies will be rare since
185
+ // the package will prevent before it gets this far.
186
+ return userRef.get().then((user) => {
187
+ let userUpdate = { meta: newData.meta, stagedDocId }
188
+
189
+ if (Object.prototype.hasOwnProperty.call(newData, 'roles')) {
190
+ userUpdate = { ...userUpdate, roles: newData.roles }
191
+ }
192
+ if (Object.prototype.hasOwnProperty.call(newData, 'specialPermissions')) {
193
+ userUpdate = { ...userUpdate, specialPermissions: newData.specialPermissions }
194
+ }
195
+
196
+ if (!oldData.userId) {
197
+ userUpdate = { ...userUpdate, userId: newData.uid }
198
+ }
199
+ console.log(userUpdate)
200
+ if (!user.exists) {
201
+ return userRef.set(userUpdate)
202
+ }
203
+ else {
204
+ return userRef.update(userUpdate)
205
+ }
206
+ })
207
+ }
208
+
209
+ function shouldProcess(eventRef) {
210
+ return eventRef.get().then((eventDoc) => {
211
+ return !eventDoc.exists || !eventDoc.data().processed
212
+ })
213
+ }
214
+
215
+ function markProcessed(eventRef) {
216
+ return eventRef.set({ processed: true }).then(() => {
217
+ return null
218
+ })
219
+ }
220
+
221
+ // END @edge/firebase functions
222
+ ```
223
+
224
+ ### To make sure your project is secure, install the firestore rules document at the end this documentation.
225
+
107
226
  # User Management and Collection Permissions
108
227
 
109
228
  ### Adding a User
110
229
 
111
- Users must be added before they can register with a login and password (the first user in the project will need to be added manual, see the section below "Root permissions and first user"). When adding a user you can pass role and/or special permissions and user meta data. For more explanations on role and special permssions, see below.
230
+ Users or "Template Users" must be added before someone can register with a login and password (the first user in the project will need to be added manual, see the section below "Root permissions and first user"). When adding a user you can pass role and/or special permissions and user meta data. For more explanations on role and special permssions, see below.
231
+
232
+ Adding a user creates a document for them in the collection "staged-users". The docId of this documment is the used as a registration code and must be passed when using "registerUser" using the "registrationCode" variable.
233
+
234
+ The collection "staged-users" is a staging zone for all modifications and severs to sanitize the actual users in the "users" collection. Once a user is registered their staged-user is linked to their "users" user. Genreally speaking the users in the "users" collection should not be modified. In fact, if you adopt the firestore rules shown in this document direct modification of users in the "users" collection is not allowed. All user related functions in this package (editing of meta, setting rules and special permssions, listing of users) are done on the "staged-users" collection.
235
+
236
+ To bypass adding users and allow "self registration". You can add a user that is a "Template User". By setting the field "template" = true. For a template user you can also set up dynamic document generation and assigning of the registered user to that document with a specified role, but setting "subCreate". Then when registering the user you can pass a "dynamicDocumentFieldValue" variable. In the example below, if on registration you passed: dynamicDocumentFieldValue = "My New Organization", a document would be created under myItems that would look like this: {name: "My New Organization"}. The user would also be assigned as an admin to that newly created document. If your project is going to be completly self registration, you can can create a "Template User" and hard code that registation id into your registation process.
112
237
 
113
238
  How to add a user:
114
239
 
115
240
  ```javascript
116
- edgeFirebase.setUser({
117
- email: "user@edgemarketingdesign.com",
241
+ edgeFirebase.addUser({
118
242
  roles: [
119
243
  {
120
244
  collectionPath: "myItems/subitems/things",
@@ -128,9 +252,35 @@ edgeFirebase.setUser({
128
252
  }
129
253
  ],
130
254
  meta: { firstName: "John", lastName: "Doe", age: 28 } // This is just an example of meta, it can contain any fields and any number of fields.
255
+ template: true, // Optional - Only true if setting up template for self registation
256
+ subCreate: {
257
+ rootPath: 'myItems',
258
+ role: 'admin',
259
+ dynamicDocumentField: 'name',
260
+ documentStructure: {
261
+ name: '',
262
+ },
263
+ }
131
264
  });
132
265
  ```
133
266
 
267
+ ```typescript
268
+ interface newUser {
269
+ roles: role[];
270
+ specialPermissions: specialPermission[];
271
+ meta: object;
272
+ isTemplate?: boolean;
273
+ subCreate?: {
274
+ rootPath: string, // This must be a collection path (odd number of segments) since a document will be created and assigned to ther user here.
275
+ role: string, // must be admin, editor, writer, user
276
+ dynamicDocumentField: string, // This is the field in the document that will be set by the value of "dynamicDocumentFieldValue" passed during registration, like "name"
277
+ documentStructure: {
278
+ [key: string]: any
279
+ }
280
+ };
281
+ }
282
+ ```
283
+
134
284
 
135
285
 
136
286
  ### Register User
@@ -145,9 +295,21 @@ After someoene has been added as a user they will need to "self register" to beg
145
295
  firstName: "John",
146
296
  lastName: "Doe"
147
297
  } // This is just an example of meta, it can contain any fields and any number of fields.
298
+ registrationCode: (document id) // This is the document id of either an added user or a template user, when using a template you can simply hardcode the registrationCode of the remplate to allow self registration.
299
+ dynamicDocumentFieldValue: "" // Optional - See explaintion above about self registration and dynamic collectionPath for user roles.
148
300
  });
149
301
  ```
150
302
 
303
+ ```typescript
304
+ interface userRegister {
305
+ email: string;
306
+ password: string;
307
+ meta: object;
308
+ registrationCode: string;
309
+ dynamicDocumentFieldValue?: string;
310
+ }
311
+ ```
312
+
151
313
 
152
314
 
153
315
  ### Explanation of permissions
@@ -198,7 +360,7 @@ How to assign a user a role for a collection:
198
360
 
199
361
  ```javascript
200
362
  edgeFirebase.storeUserRoles(
201
- "user@edgemarketingdesign.com",
363
+ docId, //Document ID of user in staged-users collection.
202
364
  "myItems/subitems/things",
203
365
  "admin"
204
366
  );
@@ -208,7 +370,7 @@ Remove a role from a user for a collection:
208
370
 
209
371
  ```javascript
210
372
  edgeFirebase.removeUserRoles(
211
- "user@edgemarketingdesign.com",
373
+ docId, //Document ID of user in staged-users collection.
212
374
  "myItems/subitems/things"
213
375
  );
214
376
  ```
@@ -228,7 +390,7 @@ If you want to give a user a unique set of permissions for a collection that doe
228
390
 
229
391
  ```javascript
230
392
  edgeFirebase.storeUserSpecialPermissions(
231
- "user@edgemarketingdesign.com",
393
+ docId, //Document ID of user in staged-users collection.
232
394
  "myItems/subitems/things",
233
395
  {
234
396
  assign: false,
@@ -243,7 +405,7 @@ Remove user special permissions:
243
405
 
244
406
  ```javascript
245
407
  edgeFirebase.removeUserSpecialPermissions(
246
- "user@edgemarketingdesign.com",
408
+ docId, //Document ID of user in staged-users collection.
247
409
  "myItems/subitems/things"
248
410
  );
249
411
  ```
@@ -255,14 +417,14 @@ Remove user special permissions:
255
417
  The remove user function doesn't actually delete the user completely from the system but instead removes all roles and special permissions that the user running the function has assign access for. In this way the user is "removed" as far as the "assigning user" is concerned but the user will remain a user for collections that the "assign user" doesn't have access to.
256
418
 
257
419
  ```javascript
258
- edgeFirebase.removeUser("user@edgemarketingdesign.com");
420
+ edgeFirebase.removeUser(docId);
259
421
  ```
260
422
 
261
423
 
262
424
 
263
425
  ### Users Snapshot Data
264
426
 
265
- This will create a reactive object (state.users) that contains the members of the collection passed to the snapshot if the user running the function has assign access for, it will be a listed index by email/user id.
427
+ This will create a reactive object (state.users) that contains the members of the collection passed to the snapshot if the user running the function has assign access for, it will be a listed index by docId.
266
428
 
267
429
  ```javascript
268
430
  edgeFirebase.startUsersSnapshot("myItems");
@@ -277,27 +439,19 @@ edgeFirebase.stopUsersSnapshot();
277
439
  <template>
278
440
  <div>
279
441
  <div v-for="user in edgeFirebase.state.users" :key="item">
280
- {{ user.email }}
442
+ {{ user.meta.name }}
281
443
  </div>
282
444
  </div>
283
445
  </template>
284
446
  ```
285
447
 
286
- ```typescript
287
- interface usersByEmail {
288
- [email: string]: [user];
289
- }
290
- ```
291
-
292
448
  ```typescript
293
449
  interface user {
294
- email: string;
450
+ docId: string;
295
451
  roles: role[];
296
452
  specialPermissions: specialPermission[];
297
453
  userId: string;
298
- docId: string;
299
454
  uid: string;
300
- last_updated: Date;
301
455
  }
302
456
  ```
303
457
 
@@ -594,6 +748,7 @@ Most functions will return a response that can be used.
594
748
 
595
749
  ```javascript
596
750
  const response = edgeFirebase.startSnapshot("things");
751
+ const response = await edgeFirebase.storeDoc("myItems", {name: "John Doe"});
597
752
  ```
598
753
 
599
754
  reponse:
@@ -606,6 +761,296 @@ interface actionResponse {
606
761
  }
607
762
  ```
608
763
 
764
+ # Firestore Rules
765
+
766
+ ```javascript
767
+ rules_version = '2';
768
+ service cloud.firestore {
769
+
770
+ match /databases/{database}/documents/events/{event} {
771
+ allow read: if false;
772
+ allow create: if false;
773
+ allow update: if false;
774
+ allow delete: if false;
775
+ }
776
+
777
+ match /databases/{database}/documents/rule-helpers/{helper} {
778
+ allow read: if false;
779
+ allow create: if request.auth.uid == request.resource.data.uid;
780
+ allow update: if request.auth.uid == request.resource.data.uid;
781
+ allow delete: if false;
782
+ }
783
+
784
+ match /databases/{database}/documents/users/{user} {
785
+ function readSelf() {
786
+ return resource == null ||
787
+ (
788
+ "userId" in resource.data &&
789
+ resource.data.userId == request.auth.uid
790
+ );
791
+ }
792
+
793
+ allow read: if readSelf();
794
+ allow create: if false;
795
+ allow update: if false;
796
+ allow delete: if false;
797
+ }
798
+
799
+ match /databases/{database}/documents/collection-data/{collectionPath} {
800
+ // TODO: these rules need tested.
801
+ function getRolePermission(role, collection, permissionCheck) {
802
+ let pathCollectionPermissions = get(/databases/$(database)/documents/collection-data/$(collection)).data;
803
+ let defaultPermissions = get(/databases/$(database)/documents/collection-data/-default-).data;
804
+ return (role in pathCollectionPermissions && pathCollectionPermissions[role][permissionCheck]) ||
805
+ (role in defaultPermissions && defaultPermissions[role][permissionCheck]);
806
+ }
807
+ function canAssign() {
808
+ let user = get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
809
+ let ruleHelper = get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data['edge-assignment-helper'];
810
+ return collectionPath.matches("^" + ruleHelper[collectionPath].permissionCheckPath + ".*$") &&
811
+ (
812
+ "specialPermissions" in user &&
813
+ ruleHelper[collectionPath].permissionCheckPath in user.specialPermissions &&
814
+ "assign" in user.specialPermissions[ruleHelper[collectionPath].permissionCheckPath] &&
815
+ user.specialPermissions[ruleHelper[collectionPath].permissionCheckPath]["assign"]
816
+ ) ||
817
+ (
818
+ "roles" in user &&
819
+ ruleHelper[collectionPath].permissionCheckPath in user.roles &&
820
+ "role" in user.roles[ruleHelper[collectionPath].permissionCheckPath] &&
821
+ getRolePermission(user.roles[ruleHelper[collectionPath].permissionCheckPath].role, collectionPath, "assign")
822
+ );
823
+ }
824
+ allow read: if request.auth != null; // All signed in users can read collection-data
825
+ allow create: if canAssign();
826
+ allow update: if canAssign();
827
+ allow delete: if canAssign();
828
+ }
829
+
830
+ match /databases/{database}/documents/staged-users/{user} {
831
+
832
+ function canUpdate() {
833
+ let user = get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
834
+ let ruleHelper = get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data;
835
+
836
+ return (
837
+ resource == null ||
838
+ request.resource.data.userId == resource.data.userId ||
839
+ (
840
+ resource.data.userId == "" &&
841
+ (
842
+ request.resource.data.userId == request.auth.uid ||
843
+ request.resource.data.templateUserId == request.auth.uid
844
+ )
845
+ )
846
+ ) &&
847
+ "edge-assignment-helper" in ruleHelper &&
848
+ permissionUpdatesCheck(user, ruleHelper, "roles") &&
849
+ permissionUpdatesCheck(user, ruleHelper, "specialPermssions") &&
850
+ request.auth.uid == request.resource.data.uid;
851
+ }
852
+
853
+
854
+ function permissionUpdatesCheck(user, ruleHelper, permissionType) {
855
+ return !(permissionType in request.resource.data) ||
856
+ (
857
+ resource.data.userId == request.auth.uid &&
858
+ request.resource.data[permissionType].keys().hasOnly(resource.data[permissionType].keys())
859
+ ) ||
860
+ (
861
+ resource.data.userId != request.auth.uid &&
862
+ permissionCheck(permissionType, user, ruleHelper)
863
+ );
864
+ }
865
+ function permissionCheck(permissionType, user, ruleHelper) {
866
+ let lastPathUpdated = ruleHelper["edge-assignment-helper"].fullPath;
867
+ let permissionCheckPath = ruleHelper["edge-assignment-helper"].permissionCheckPath;
868
+ return request.resource.data[permissionType].diff(resource.data[permissionType]).affectedKeys().size() == 0 ||
869
+ (
870
+ request.resource.data[permissionType].diff(resource.data[permissionType]).affectedKeys().size() == 1 &&
871
+ request.resource.data[permissionType].diff(resource.data[permissionType]).affectedKeys() == [lastPathUpdated].toSet() &&
872
+ (
873
+ permissionCheckPath == "-" ||
874
+ lastPathUpdated.matches("^" + permissionCheckPath + ".*$")
875
+ ) &&
876
+ (
877
+ (
878
+ "roles" in user &&
879
+ getRolePermission(user.roles[permissionCheckPath].role, permissionCheckPath, "assign")
880
+ ) ||
881
+ (
882
+ "specialPermissions" in user &&
883
+ permissionCheckPath in user.specialPermissions &&
884
+ "assign" in user.specialPermissions[permissionCheckPath] &&
885
+ user.specialPermissions[permissionCheckPath]["assign"]
886
+ )
887
+ )
888
+ );
889
+ }
890
+
891
+ function canAssign(user, ruleHelper) {
892
+ return request.auth != null &&
893
+ "edge-assignment-helper" in ruleHelper &&
894
+ (
895
+ (
896
+ "roles" in user &&
897
+ ruleHelper["edge-assignment-helper"].permissionCheckPath in user.roles &&
898
+ getRolePermission(user.roles[ruleHelper["edge-assignment-helper"].permissionCheckPath].role, ruleHelper["edge-assignment-helper"].permissionCheckPath, 'assign')
899
+ ) ||
900
+ (
901
+ "specialPermissions" in user &&
902
+ ruleHelper["edge-assignment-helper"].permissionCheckPath in user.specialPermissions &&
903
+ "assign" in user.specialPermissions[ruleHelper["edge-assignment-helper"].permissionCheckPath] &&
904
+ user.specialPermissions[ruleHelper["edge-assignment-helper"].permissionCheckPath]["assign"]
905
+ )
906
+ )
907
+ }
908
+
909
+ function canAssignSubCreatePath(user, ruleHelper) {
910
+ let permissionCheckPath = ruleHelper["edge-assignment-helper"].permissionCheckPath;
911
+ return (
912
+ !("subCreate" in request.resource.data) ||
913
+ (
914
+ "subCreate" in request.resource.data &&
915
+ request.resource.data.subCreate.keys().size() == 0
916
+ )
917
+ )||
918
+ (
919
+ permissionCheckPath == "-" ||
920
+ request.resource.data.subCreate.rootPath.matches("^" + permissionCheckPath + ".*$")
921
+ ) &&
922
+ (
923
+ (
924
+ "roles" in user &&
925
+ permissionCheckPath in user.roles &&
926
+ getRolePermission(user.roles[permissionCheckPath].role, permissionCheckPath, "assign")
927
+ ) ||
928
+ (
929
+ "specialPermissions" in user &&
930
+ permissionCheckPath in user.specialPermissions &&
931
+ "assign" in user.specialPermissions[permissionCheckPath] &&
932
+ user.specialPermissions[permissionCheckPath]["assign"]
933
+ )
934
+ )
935
+
936
+ }
937
+
938
+ function canList() {
939
+ let user = get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
940
+ let ruleHelper = get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data;
941
+ return canAssign(user, ruleHelper);
942
+ }
943
+
944
+ function canCreate() {
945
+ let user = get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
946
+ let ruleHelper = get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data;
947
+ return noPermissionData() && canAssign(user, ruleHelper) && canAssignSubCreatePath(user, ruleHelper);
948
+ }
949
+
950
+ function noPermissionData() {
951
+ return request.resource.data.roles.size() == 0 && request.resource.data.specialPermissions.size() == 0;
952
+ }
953
+
954
+ function getRolePermission(role, collection, permissionCheck) {
955
+ let pathCollectionPermissions = get(/databases/$(database)/documents/collection-data/$(collection)).data;
956
+ let defaultPermissions = get(/databases/$(database)/documents/collection-data/-default-).data;
957
+ return (role in pathCollectionPermissions && pathCollectionPermissions[role][permissionCheck]) ||
958
+ (role in defaultPermissions && defaultPermissions[role][permissionCheck]);
959
+ }
960
+
961
+ function canGet () {
962
+ return resource == null ||
963
+ ("userId" in resource.data && resource.data.userId == "") ||
964
+ ("userId" in resource.data && resource.data.userId == request.auth.uid) ||
965
+ canAssign(get(/databases/$(database)/documents/users/$(request.auth.uid)).data, get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data);
966
+ }
967
+ allow get: if canGet();
968
+ allow list: if canList();
969
+ allow create: if canCreate();
970
+ allow update: if canUpdate();
971
+ allow delete: if false // TODO if isTemplate is true... can delete... otherwise users never deleted just removed from collection paths
972
+ }
973
+
974
+ match /databases/{database}/documents/{seg1} {
975
+ function getRolePermission(role, collection, permissionCheck) {
976
+ let pathCollectionPermissions = get(/databases/$(database)/documents/collection-data/$(collection)).data;
977
+ let defaultPermissions = get(/databases/$(database)/documents/collection-data/-default-).data;
978
+ return (role in pathCollectionPermissions && pathCollectionPermissions[role][permissionCheck]) ||
979
+ (role in defaultPermissions && defaultPermissions[role][permissionCheck]);
980
+ }
981
+ function checkPermission(collectionPath, permissionCheck) {
982
+ let user = get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
983
+ let skipPaths = ["collection-data", "users", "staged-users", "events", "rule-helpers"];
984
+ let ruleHelper = get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data;
985
+ return !(collectionPath in skipPaths) &&
986
+ request.auth != null &&
987
+ collectionPath in ruleHelper &&
988
+ "permissionCheckPath" in ruleHelper[collectionPath] &&
989
+ (
990
+ ruleHelper[collectionPath].permissionCheckPath == "-" ||
991
+ collectionPath.matches("^" + ruleHelper[collectionPath].permissionCheckPath + ".*$")
992
+ ) &&
993
+ (
994
+ (
995
+ "roles" in user &&
996
+ ruleHelper[collectionPath].permissionCheckPath in user.roles &&
997
+ getRolePermission(user.roles[ruleHelper[collectionPath].permissionCheckPath].role, ruleHelper[collectionPath].permissionCheckPath, permissionCheck)
998
+ ) ||
999
+ (
1000
+ "specialPermissions" in user &&
1001
+ ruleHelper[collectionPath].permissionCheckPath in user.specialPermissions &&
1002
+ permissionCheck in user.specialPermissions[ruleHelper[collectionPath].permissionCheckPath] &&
1003
+ user.specialPermissions[ruleHelper[collectionPath].permissionCheckPath][permissionCheck]
1004
+ )
1005
+ );
1006
+ }
1007
+ match /{seg2} {
1008
+ allow get: if checkPermission(seg1 + "-" + seg2, "read");
1009
+ allow list: if checkPermission(seg1, "read");
1010
+ allow create: if checkPermission(seg1, "write");
1011
+ allow update: if checkPermission(seg1, "write");
1012
+ allow delete: if checkPermission(seg1, "delete");
1013
+ match /{seg3} {
1014
+ allow get: if checkPermission(seg1 + "-" + seg2 + "-" + seg3, "read");
1015
+ allow list: if checkPermission(seg1 + "-" + seg2, "read");
1016
+ allow create: if checkPermission(seg1 + "-" + seg2, "write");
1017
+ allow update: if checkPermission(seg1 + "-" + seg2, "write");
1018
+ allow delete: if checkPermission(seg1 + "-" + seg2, "delete");
1019
+ match /{seg4} {
1020
+ allow get: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4, "read");
1021
+ allow list: if checkPermission(seg1 + "-" + seg2 + "-" + seg3, "read");
1022
+ allow create: if checkPermission(seg1 + "-" + seg2 + "-" + seg3, "write");
1023
+ allow update: if checkPermission(seg1 + "-" + seg2 + "-" + seg3, "write");
1024
+ allow delete: if checkPermission(seg1 + "-" + seg2 + "-" + seg3, "delete");
1025
+
1026
+ match /{seg5} {
1027
+ allow get: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5, "read");
1028
+ allow list: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4, "read");
1029
+ allow create: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4, "write");
1030
+ allow update: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4, "write");
1031
+ allow delete: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4, "delete");
1032
+ match /{seg6} {
1033
+ allow get: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5 + "-" + seg6, "read");
1034
+ allow list: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5, "read");
1035
+ allow create: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5, "write");
1036
+ allow update: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5, "write");
1037
+ allow delete: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5, "delete");
1038
+ match /{seg7} {
1039
+ allow get: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5 + "-" + seg6 + "-" + seg7, "read");
1040
+ allow list: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5 + "-" + seg6, "read");
1041
+ allow create: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5 + "-" + seg6, "write");
1042
+ allow update: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5 + "-" + seg6, "write");
1043
+ allow delete: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5 + "-" + seg6, "delete");
1044
+ }
1045
+ }
1046
+ }
1047
+ }
1048
+ }
1049
+ }
1050
+ }
1051
+ }
1052
+ ```
1053
+
609
1054
 
610
1055
 
611
1056
  ## License