@edgedev/firebase 1.8.11 → 1.9.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.
package/README.md CHANGED
@@ -239,6 +239,63 @@ Calling this will generate a Microsoft Sign In Popup and register the user using
239
239
  );
240
240
  ```
241
241
 
242
+ ### Inviting an Existing User to Register with a New Organization or Member
243
+
244
+ To invite an existing user to register with a new organization or member's data and get the corresponding roles, use the `edgeFirebase.currentUserRegister(userRegister)` method.
245
+
246
+ ```javascript
247
+ const userRegisterData = {
248
+ registrationCode: "12345",
249
+ dynamicDocumentFieldValue: "fieldName",
250
+ };
251
+
252
+ const response = await edgeFirebase.currentUserRegister(userRegisterData);
253
+ ```
254
+
255
+ #### Parameters
256
+
257
+ - `userRegister` (object): An object containing the user registration data. It must include a `registrationCode` property provided by the inviting organization or member. It can also include a `dynamicDocumentFieldValue` property, which is a single string representing the name of an additional data field for registration.
258
+
259
+ ```typescript
260
+ interface userRegister {
261
+ registrationCode: string;
262
+ dynamicDocumentFieldValue?: string;
263
+ }
264
+ ```
265
+
266
+ #### Returns
267
+
268
+ The method returns a Promise that resolves to an `actionResponse` object:
269
+
270
+ ```typescript
271
+ interface actionResponse {
272
+ success: boolean;
273
+ message: string;
274
+ meta: {};
275
+ }
276
+ ```
277
+
278
+ Example usage:
279
+
280
+ ```javascript
281
+ <script setup>
282
+ async function inviteExistingUser() {
283
+ const userRegisterData = {
284
+ registrationCode: "12345",
285
+ dynamicDocumentFieldValue: "fieldName",
286
+ };
287
+
288
+ const response = await edgeFirebase.currentUserRegister(userRegisterData);
289
+ if (response.success) {
290
+ console.log("Existing user invited and registered successfully");
291
+ } else {
292
+ console.error("Error inviting and registering existing user:", response.message);
293
+ }
294
+ }
295
+ </script>
296
+ ```
297
+
298
+
242
299
  ### Explanation of permissions
243
300
 
244
301
  - **assign: boolean** - When a user has this permission for a collection they can assign other users to the collection and change permissions for that collection. For a user to be able run setUser, storeCollectionPermisions, storeUserRoles, removeUserRoles, storeUserSpecialPermissions, or removeUserSpecialPermissions, they must have assign access to any of the collection paths passed into those functions.
package/edgeFirebase.ts CHANGED
@@ -126,6 +126,7 @@ interface newUser {
126
126
  }
127
127
 
128
128
  interface userRegister {
129
+ uid?: string;
129
130
  email?: string;
130
131
  password?: string;
131
132
  meta: object;
@@ -233,7 +234,7 @@ export const EdgeFirebase = class {
233
234
 
234
235
  private functions = null;
235
236
 
236
- public runFunction = async (functionName: string, data: { [key: string]: unknown }) => {
237
+ public runFunction = async (functionName: string, data: Record<string, unknown>) => {
237
238
  data.uid = this.user.uid;
238
239
  const callable = httpsCallable(this.functions, functionName);
239
240
  return await callable(data);
@@ -462,6 +463,32 @@ export const EdgeFirebase = class {
462
463
  }
463
464
  }
464
465
 
466
+ public currentUserRegister = async (userRegister: userRegister): Promise<actionResponse> => {
467
+ if (!Object.prototype.hasOwnProperty.call(userRegister, 'registrationCode') || userRegister.registrationCode === "") {
468
+ return this.sendResponse({
469
+ success: false,
470
+ message: "Registration code is required.",
471
+ meta: {}
472
+ });
473
+ }
474
+ // userRegister.uid = this.user.uid;
475
+ const result = await this.runFunction("currentUserRegister", userRegister as unknown as Record<string, unknown>);
476
+ const resultData = result.data as {success: boolean, message: string};
477
+ if (resultData.success) {
478
+ return this.sendResponse({
479
+ success: true,
480
+ message: "",
481
+ meta: {}
482
+ });
483
+ } else {
484
+ return this.sendResponse({
485
+ success: false,
486
+ message: resultData.message,
487
+ meta: {}
488
+ });
489
+ }
490
+ }
491
+
465
492
  public registerUser = async (
466
493
  userRegister: userRegister,
467
494
  authProvider: authProviders = "email",
@@ -632,7 +659,7 @@ export const EdgeFirebase = class {
632
659
 
633
660
  public removeUser = async (docId: string): Promise<actionResponse> => {
634
661
  const removedFrom = [];
635
- const userRef = doc(this.db, "users", docId);
662
+ const userRef = doc(this.db, "staged-users", docId);
636
663
  const userSnap = await getDoc(userRef);
637
664
  if (userSnap.data().roles) {
638
665
  for (const collectionPath in userSnap.data().roles) {
@@ -653,7 +680,7 @@ export const EdgeFirebase = class {
653
680
  collectionPath.replaceAll("-", "/")
654
681
  );
655
682
  if (canAssign) {
656
- this.removeUserSpecialPermissions(
683
+ await this.removeUserSpecialPermissions(
657
684
  docId,
658
685
  collectionPath.replaceAll("-", "/")
659
686
  );
@@ -662,10 +689,11 @@ export const EdgeFirebase = class {
662
689
  }
663
690
  }
664
691
  if (removedFrom.length > 0) {
692
+ const response = await this.runFunction("removeNonRegisteredUser", {uid: this.user.uid, docId});
665
693
  return this.sendResponse({
666
694
  success: true,
667
695
  message: "",
668
- meta: {}
696
+ meta: {response}
669
697
  });
670
698
  } else {
671
699
  return this.sendResponse({
@@ -892,8 +920,8 @@ export const EdgeFirebase = class {
892
920
  };
893
921
 
894
922
  private generateUserMeta = async (userMeta: newUser): Promise<actionResponse> => {
895
- const roles: role[] = userMeta.roles;
896
- const specialPermissions: specialPermission[] = userMeta.specialPermissions;
923
+ const roles: role[] = userMeta.roles || [];
924
+ const specialPermissions: specialPermission[] = userMeta.specialPermissions || [];
897
925
  delete userMeta.roles;
898
926
  delete userMeta.specialPermissions;
899
927
 
@@ -1411,6 +1439,7 @@ export const EdgeFirebase = class {
1411
1439
  }
1412
1440
  if (canAssign) {
1413
1441
  await updateDoc(doc(this.db, "staged-users/" + docId), {
1442
+ uid: this.user.uid,
1414
1443
  collectionPaths: arrayRemove(collectionPath.replaceAll("/", "-")),
1415
1444
  ["roles." + collectionPath.replaceAll("/", "-")]: deleteField()
1416
1445
  });
@@ -1440,6 +1469,7 @@ export const EdgeFirebase = class {
1440
1469
  }
1441
1470
  if (canAssign) {
1442
1471
  await updateDoc(doc(this.db, "staged-users/" + docId), {
1472
+ uid: this.user.uid,
1443
1473
  collectionPaths: arrayRemove(collectionPath.replaceAll("/", "-")),
1444
1474
  ["specialPermissions." + collectionPath.replaceAll("/", "-")]:
1445
1475
  deleteField()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgedev/firebase",
3
- "version": "1.8.11",
3
+ "version": "1.9.1",
4
4
  "description": "Vue 3 / Nuxt 3 Plugin or Nuxt 3 plugin for firebase authentication and firestore.",
5
5
  "main": "index.ts",
6
6
  "scripts": {
@@ -68,20 +68,27 @@ service cloud.firestore {
68
68
  let ruleHelper = get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data;
69
69
 
70
70
  return (
71
- resource == null ||
72
- request.resource.data.userId == resource.data.userId ||
71
+ request.auth.uid == request.resource.data.uid &&
73
72
  (
74
- resource.data.userId == "" &&
75
73
  (
76
- request.resource.data.userId == request.auth.uid ||
77
- request.resource.data.templateUserId == request.auth.uid
74
+ (
75
+ request.resource.data.userId == resource.data.userId ||
76
+ resource.data.userId == ""
77
+ ) &&
78
+ (
79
+ request.resource.data.userId == request.auth.uid ||
80
+ request.resource.data.templateUserId == request.auth.uid
81
+ )
82
+ ) ||
83
+ (
84
+ request.resource.data.userId == resource.data.userId &&
85
+ "edge-assignment-helper" in ruleHelper &&
86
+ permissionUpdatesCheck(user, ruleHelper, "roles") &&
87
+ permissionUpdatesCheck(user, ruleHelper, "specialPermssions")
78
88
  )
79
89
  )
80
- ) &&
81
- "edge-assignment-helper" in ruleHelper &&
82
- permissionUpdatesCheck(user, ruleHelper, "roles") &&
83
- permissionUpdatesCheck(user, ruleHelper, "specialPermssions") &&
84
- request.auth.uid == request.resource.data.uid;
90
+ );
91
+
85
92
  }
86
93
 
87
94
 
package/src/functions.js CHANGED
@@ -1,4 +1,89 @@
1
1
  // START @edge/firebase functions
2
+ exports.removeNonRegisteredUser = functions.https.onCall(async (data, context) => {
3
+ if (data.uid === context.auth.uid) {
4
+ const stagedUser = await db.collection('staged-users').doc(data.docId).get()
5
+ if (stagedUser.exists) {
6
+ const stagedUserData = stagedUser.data()
7
+
8
+ const rolesExist = stagedUserData.roles && Object.keys(stagedUserData.roles).length !== 0
9
+ const specialPermissionsExist = stagedUserData.specialPermissions && Object.keys(stagedUserData.specialPermissions).length !== 0
10
+ const userIdExistsAndNotBlank = stagedUserData.userId && stagedUserData.userId !== ''
11
+
12
+ if (!rolesExist && !specialPermissionsExist && !userIdExistsAndNotBlank) {
13
+ await db.collection('staged-users').doc(data.docId).delete()
14
+ return { success: true, message: '' }
15
+ }
16
+ else {
17
+ let message = ''
18
+ if (rolesExist && specialPermissionsExist) {
19
+ message = 'Cannot delete because the non-registered user still has roles and special permissions assigned.'
20
+ }
21
+ else if (rolesExist) {
22
+ message = 'Cannot delete because the non-registered user still has roles assigned.'
23
+ }
24
+ else if (specialPermissionsExist) {
25
+ message = 'Cannot delete because the non-registered user still has special permissions assigned.'
26
+ }
27
+ else if (userIdExistsAndNotBlank) {
28
+ message = 'Cannot delete because the user is registered.'
29
+ }
30
+ return { success: false, message }
31
+ }
32
+ }
33
+ }
34
+ return { success: false, message: 'Non-registered user not found.' }
35
+ })
36
+
37
+ exports.currentUserRegister = functions.https.onCall(async (data, context) => {
38
+ if (data.uid === context.auth.uid) {
39
+ const stagedUser = await db.collection('staged-users').doc(data.registrationCode).get()
40
+ if (!stagedUser.exists) {
41
+ return { success: false, message: 'Registration code not found.' }
42
+ }
43
+ else {
44
+ const stagedUserData = await stagedUser.data()
45
+ let process = false
46
+ if (stagedUserData.isTemplate) {
47
+ process = true
48
+ }
49
+ if (!stagedUserData.isTemplate && stagedUserData.userId === '') {
50
+ process = true
51
+ }
52
+ if (!process) {
53
+ return { success: false, message: 'Registration code not valid.' }
54
+ }
55
+ const newRoles = stagedUserData.roles || {}
56
+ const currentUser = await db.collection('users').doc(data.uid).get()
57
+ const currentUserData = await currentUser.data()
58
+ const currentRoles = currentUserData.roles || {}
59
+ const currentUserCollectionPaths = currentUserData.collectionPaths || []
60
+ let newRole = {}
61
+ if (stagedUserData.subCreate && Object.keys(stagedUserData.subCreate).length !== 0 && stagedUserData.isTemplate) {
62
+ if (!data.dynamicDocumentFieldValue) {
63
+ return { success: false, message: 'Dynamic document field value is required.' }
64
+ }
65
+ const rootPath = stagedUserData.subCreate.rootPath
66
+ const newDoc = stagedUserData.subCreate.documentStructure
67
+ newDoc[stagedUserData.subCreate.dynamicDocumentField] = data.dynamicDocumentFieldValue
68
+ const addedDoc = await db.collection(rootPath).add(newDoc)
69
+ await db.collection(rootPath).doc(addedDoc.id).update({ docId: addedDoc.id })
70
+ newRole = { [`${rootPath}-${addedDoc.id}`]: { collectionPath: `${rootPath}-${addedDoc.id}`, role: stagedUserData.subCreate.role } }
71
+ }
72
+ const combinedRoles = { ...currentRoles, ...newRoles, ...newRole }
73
+ Object.values(combinedRoles).forEach((role) => {
74
+ if (!currentUserCollectionPaths.includes(role.collectionPath)) {
75
+ currentUserCollectionPaths.push(role.collectionPath)
76
+ }
77
+ })
78
+ await db.collection('staged-users').doc(currentUserData.stagedDocId).update({ roles: combinedRoles, collectionPaths: currentUserCollectionPaths })
79
+ if (!stagedUserData.isTemplate) {
80
+ await db.collection('staged-users').doc(data.registrationCode).delete()
81
+ }
82
+ return { success: true, message: '' }
83
+ }
84
+ }
85
+ })
86
+
2
87
  exports.updateUser = functions.firestore.document('staged-users/{docId}').onUpdate((change, context) => {
3
88
  const eventId = context.eventId
4
89
  const eventRef = db.collection('events').doc(eventId)