@edgedev/firebase 2.0.36 → 2.1.37

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/edgeFirebase.ts CHANGED
@@ -349,64 +349,6 @@ export const EdgeFirebase = class {
349
349
  this.unsubscribe.userMeta = metaUnsubscribe;
350
350
  };
351
351
 
352
- private startCollectionPermissionsSync = async (): Promise<void> => {
353
- // TODO: In future get roles from user and only sync those collections
354
- // Perhaps by getting all "first segments" and get all that start with that
355
- const q = this.getQuery('collection-data');
356
- const docs = await getDocs(q);
357
- let items = {}
358
- docs.forEach((doc) => {
359
- const item = doc.data();
360
- item.docId = doc.id;
361
- items[doc.id] = item;
362
- });
363
- this.state.collectionPermissions = items;
364
- if (!this.state.collectionPermissions['-default-']) {
365
- const collectionItem = {
366
- collectionPath: '-default-',
367
- docId: '-default-',
368
- admin: {
369
- assign: true,
370
- read: true,
371
- write: true,
372
- delete: true
373
- },
374
- editor: {
375
- assign: false,
376
- read: true,
377
- write: true,
378
- delete: true
379
- },
380
- writer: {
381
- assign: false,
382
- read: true,
383
- write: true,
384
- delete: false
385
- },
386
- user: {
387
- assign: false,
388
- read: true,
389
- write: false,
390
- delete: false
391
- }
392
- };
393
- await setDoc(
394
- doc(this.db, "collection-data", "-default-"),
395
- collectionItem
396
- );
397
- }
398
- this.stopSnapshot('collection-data');
399
- const unsubscribe = onSnapshot(q, (querySnapshot) => {
400
- items = {};
401
- querySnapshot.forEach((doc) => {
402
- const item = doc.data();
403
- item.docId = doc.id;
404
- items[doc.id] = item;
405
- });
406
- this.state.collectionPermissions = items;
407
- });
408
- this.unsubscribe['collection-data'] = unsubscribe
409
- }
410
352
 
411
353
  private ruleHelperReset = async (): Promise<void> => {
412
354
  const initRoleHelper = { uid: this.user.uid, 'edge-assignment-helper': {permissionType: "roles"} }
@@ -417,7 +359,6 @@ export const EdgeFirebase = class {
417
359
  private startUserMetaSync = async (docSnap): Promise<void> => {
418
360
  // Took this out because if another client is logged in, it breaks the other client
419
361
  // await this.ruleHelperReset();
420
- await this.startCollectionPermissionsSync()
421
362
  await this.initUserMetaPermissions(docSnap);
422
363
  this.user.loggedIn = true;
423
364
  this.user.loggingIn = false;
@@ -1060,6 +1001,8 @@ export const EdgeFirebase = class {
1060
1001
  this.state.ruleHelpers[ruleKey] = ruleCheck;
1061
1002
  }
1062
1003
 
1004
+
1005
+
1063
1006
  public permissionCheckOnly = (action: action, collectionPath: string): boolean => {
1064
1007
  const collection = collectionPath.replaceAll("-", "/").split("/");
1065
1008
  let index = collection.length;
@@ -1079,10 +1022,12 @@ export const EdgeFirebase = class {
1079
1022
  );
1080
1023
 
1081
1024
  if (role) {
1082
- permissionData = this.getCollectionPermissions(
1083
- permissionCheck,
1084
- role.role
1085
- );
1025
+ permissionData = this.state.permissions[role.role] || {
1026
+ read: false,
1027
+ write: false,
1028
+ delete: false,
1029
+ assign: false
1030
+ };
1086
1031
  }
1087
1032
  const specialPermission = this.user.specialPermissions.find(
1088
1033
  (r) => r.collectionPath === permissionCheck
@@ -1096,10 +1041,12 @@ export const EdgeFirebase = class {
1096
1041
  if (!permissionData[action]) {
1097
1042
  const rootRole = this.user.roles.find((r) => r.collectionPath === "-");
1098
1043
  if (rootRole) {
1099
- permissionData = this.getCollectionPermissions(
1100
- "-",
1101
- rootRole.role
1102
- );
1044
+ permissionData = this.state.permissions[rootRole.role] || {
1045
+ read: false,
1046
+ write: false,
1047
+ delete: false,
1048
+ assign: false
1049
+ };
1103
1050
  }
1104
1051
  const rootSpecialPermission = this.user.specialPermissions.find(
1105
1052
  (r) => r.collectionPath === "-"
@@ -1122,32 +1069,6 @@ export const EdgeFirebase = class {
1122
1069
  return check;
1123
1070
  };
1124
1071
 
1125
- private getCollectionPermissions = (
1126
- collectionPath: string,
1127
- role: string
1128
- ): permissions => {
1129
- if (Object.prototype.hasOwnProperty.call(this.state.collectionPermissions, collectionPath)) {
1130
- if (Object.prototype.hasOwnProperty.call(this.state.collectionPermissions[collectionPath], role)) {
1131
- const permissionData = this.state.collectionPermissions[collectionPath][role];
1132
- return {
1133
- read: permissionData.read,
1134
- write: permissionData.write,
1135
- delete: permissionData.delete,
1136
- assign: permissionData.assign
1137
- };
1138
- }
1139
- }
1140
- if (Object.prototype.hasOwnProperty.call(this.state.collectionPermissions, '-default-')) {
1141
- return this.state.collectionPermissions['-default-'][role];
1142
- }
1143
- return {
1144
- read: false,
1145
- write: false,
1146
- delete: false,
1147
- assign: false
1148
- };
1149
- };
1150
-
1151
1072
  private generateUserMeta = async (userMeta: newUser): Promise<actionResponse> => {
1152
1073
  const roles: role[] = userMeta.roles || [];
1153
1074
  const specialPermissions: specialPermission[] = userMeta.specialPermissions || [];
@@ -1277,7 +1198,12 @@ export const EdgeFirebase = class {
1277
1198
  });
1278
1199
 
1279
1200
  public state = reactive({
1280
- collectionPermissions: {},
1201
+ permissions: {
1202
+ 'admin': {'assign': true, 'delete': true, 'read': true, 'write': true},
1203
+ 'editor': {'assign': false, 'delete': true, 'read': true, 'write': true},
1204
+ 'user': {'assign': false, 'delete': false, 'read': true, 'write': false},
1205
+ 'writer': {'assign': false, 'delete': false, 'read': true, 'write': true}
1206
+ },
1281
1207
  users: {},
1282
1208
  registrationCode: "",
1283
1209
  registrationMeta: {},
@@ -2127,7 +2053,7 @@ export const EdgeFirebase = class {
2127
2053
 
2128
2054
 
2129
2055
  // File functions
2130
- public uploadFile = async (filePath: string, file: Blob): Promise<actionResponse> => {
2056
+ public uploadFile = async (filePath: string, file: Blob, isPublic: boolean): Promise<actionResponse> => {
2131
2057
 
2132
2058
  // Validate if file is provided
2133
2059
  if (!file) {
@@ -2139,7 +2065,11 @@ export const EdgeFirebase = class {
2139
2065
  }
2140
2066
 
2141
2067
  // Check if the user has write permission to the filePath
2142
- const hasWritePermission = await this.permissionCheck("write", filePath);
2068
+ let hasWritePermission = await this.permissionCheck("write", filePath);
2069
+ if (isPublic) {
2070
+ hasWritePermission = true;
2071
+ filePath = "public";
2072
+ }
2143
2073
  if (!hasWritePermission) {
2144
2074
  return this.sendResponse({
2145
2075
  success: false,
@@ -2149,13 +2079,21 @@ export const EdgeFirebase = class {
2149
2079
  }
2150
2080
 
2151
2081
  try {
2152
- const tempFilePath = `${filePath.replaceAll('/', '-')}` + '/' + file.name;
2082
+ let randomId = ''
2083
+ if (isPublic) {
2084
+ randomId = Math.random().toString(36).substring(2, 6) + '-'
2085
+ }
2086
+ const tempFilePath = `${filePath.replaceAll('/', '-')}` + '/' + randomId + file.name;
2153
2087
  const fileRef = ref(this.storage, tempFilePath);
2088
+ if (isPublic) {
2089
+ await uploadBytes(fileRef, file, { contentType: file.type, cacheControl: 'public, max-age=31536000' });
2090
+ } else {
2154
2091
  await uploadBytes(fileRef, file);
2092
+ }
2155
2093
  return this.sendResponse({
2156
2094
  success: true,
2157
2095
  message: "File uploaded successfully.",
2158
- meta: {}
2096
+ meta: {file: tempFilePath}
2159
2097
  });
2160
2098
  } catch (error) {
2161
2099
  return this.sendResponse({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgedev/firebase",
3
- "version": "2.0.36",
3
+ "version": "2.1.37",
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": {
package/src/config.js CHANGED
@@ -26,6 +26,41 @@ const { getFirestore } = require('firebase-admin/firestore')
26
26
  const twilio = require('twilio')
27
27
  const db = getFirestore()
28
28
 
29
+ // The permissionCheck function
30
+
31
+ const permissions = {
32
+ 'admin': {'assign': true, 'delete': true, 'read': true, 'write': true},
33
+ 'editor': {'assign': false, 'delete': true, 'read': true, 'write': true},
34
+ 'user': {'assign': false, 'delete': false, 'read': true, 'write': false},
35
+ 'writer': {'assign': false, 'delete': false, 'read': true, 'write': true}
36
+ };
37
+
38
+ const permissionCheck = async (userId, action, originalFilePath) => {
39
+ // Fetch user document
40
+ const collectionPath = originalFilePath.replace(/\//g, '-')
41
+ const userDoc = await db.collection('users').doc(userId).get()
42
+ if (!userDoc.exists) {
43
+ console.log('No such user!')
44
+ return false // Or handle as needed
45
+ }
46
+ const userData = userDoc.data()
47
+
48
+ // Fetch roles from user data
49
+ const roles = Object.values(userData.roles || {})
50
+
51
+ for (const role of roles) {
52
+ // Check if the role's collectionPath is a prefix of the collectionPath
53
+ if (collectionPath.startsWith(role.collectionPath)) {
54
+ // Use permissions object instead of fetching collection data
55
+ const rolePermissions = permissions[role.role];
56
+ if (rolePermissions && rolePermissions[action]) {
57
+ return true;
58
+ }
59
+ }
60
+ }
61
+ return false;
62
+ }
63
+
29
64
  module.exports = {
30
65
  pubsub,
31
66
  onMessagePublished,
@@ -46,5 +81,7 @@ module.exports = {
46
81
  twilio,
47
82
  db,
48
83
  Storage,
84
+ permissionCheck,
49
85
  }
50
86
 
87
+
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-var-requires */
2
2
  /* eslint-disable no-undef */
3
- const { onCall, HttpsError, logger, getFirestore, functions, admin, twilio, db, onSchedule, onDocumentUpdated, pubsub, Storage } = require('./config.js')
3
+ const { onCall, HttpsError, logger, getFirestore, functions, admin, twilio, db, onSchedule, onDocumentUpdated, pubsub, Storage, permissionCheck } = require('./config.js')
4
4
 
5
5
  const authToken = process.env.TWILIO_AUTH_TOKEN
6
6
  const accountSid = process.env.TWILIO_SID
@@ -13,32 +13,6 @@ function formatPhoneNumber(phone) {
13
13
  return `+1${numericPhone}`
14
14
  }
15
15
 
16
- const permissionCheck = async (userId, action, originalFilePath) => {
17
- // Fetch user document
18
- const collectionPath = originalFilePath.replace(/\//g, '-')
19
- const userDoc = await db.collection('users').doc(userId).get()
20
- const userData = userDoc.data()
21
-
22
- // Fetch roles from user data
23
- const roles = Object.values(userData.roles || {})
24
-
25
- for (const role of roles) {
26
- // Check if the role's collectionPath is a prefix of the collectionPath
27
- if (collectionPath.startsWith(role.collectionPath)) {
28
- // Fetch collection data
29
- const collectionDoc = await db.collection('collection-data').doc(role.collectionPath).get()
30
- const collectionData = collectionDoc.exists ? collectionDoc.data() : await db.collection('collection-data').doc('-default-').get().then(doc => doc.data())
31
-
32
- // Check if action is permitted
33
- if (collectionData && collectionData[role.role] && collectionData[role.role][action]) {
34
- return true
35
- }
36
- }
37
- }
38
- // If no permission found, return false
39
- return false
40
- }
41
-
42
16
  exports.topicQueue = onSchedule({ schedule: 'every 1 minutes', timeoutSeconds: 180 }, async (event) => {
43
17
  const queuedTopicsRef = db.collection('topic-queue')
44
18
  const snapshot = await queuedTopicsRef.get()
@@ -156,18 +130,8 @@ exports.verifyPhoneNumber = onCall(async (request) => {
156
130
  })
157
131
 
158
132
  exports.initFirestore = onCall(async (request) => {
159
- // checks to see of the collections 'collection-data' and 'staged-users' exist if not will seed them with data
160
- const collectionData = await db.collection('collection-data').get()
133
+ // checks to see of the collections 'staged-users' exist if not will seed them with data
161
134
  const stagedUsers = await db.collection('staged-users').get()
162
- if (collectionData.empty) {
163
- // create a document with the id of '-' and one called '-default-':
164
- const admin = { assign: true, delete: true, read: true, write: true }
165
- const editor = { assign: false, delete: true, read: true, write: true }
166
- const writer = { assign: false, delete: false, read: true, write: true }
167
- const user = { assign: false, delete: false, read: true, write: false }
168
- await db.collection('collection-data').doc('-').set({ admin, editor, writer, user })
169
- await db.collection('collection-data').doc('-default-').set({ admin, editor, writer, user })
170
- }
171
135
  if (stagedUsers.empty) {
172
136
  const templateUser = {
173
137
  docId: 'organization-registration-template',
@@ -56,11 +56,14 @@ service cloud.firestore {
56
56
 
57
57
  match /databases/{database}/documents/collection-data/{collectionPath} {
58
58
  // TODO: these rules need tested.
59
- function getRolePermission(role, collection, permissionCheck) {
60
- let pathCollectionPermissions = get(/databases/$(database)/documents/collection-data/$(collection)).data;
61
- let defaultPermissions = get(/databases/$(database)/documents/collection-data/-default-).data;
62
- return (role in pathCollectionPermissions && pathCollectionPermissions[role][permissionCheck]) ||
63
- (role in defaultPermissions && defaultPermissions[role][permissionCheck]);
59
+ function getRolePermission(role, permissionCheck) {
60
+ let permissions = {
61
+ 'admin': {'assign': true, 'delete': true, 'read': true, 'write': true},
62
+ 'editor': {'assign': false, 'delete': true, 'read': true, 'write': true},
63
+ 'user': {'assign': false, 'delete': false, 'read': true, 'write': false},
64
+ 'writer': {'assign': false, 'delete': false, 'read': true, 'write': true}
65
+ };
66
+ return permissions[role][permissionCheck];
64
67
  }
65
68
  function canAssign() {
66
69
  let user = get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
@@ -76,7 +79,7 @@ service cloud.firestore {
76
79
  "roles" in user &&
77
80
  ruleHelper[collectionPath].permissionCheckPath in user.roles &&
78
81
  "role" in user.roles[ruleHelper[collectionPath].permissionCheckPath] &&
79
- getRolePermission(user.roles[ruleHelper[collectionPath].permissionCheckPath].role, collectionPath, "assign")
82
+ getRolePermission(user.roles[ruleHelper[collectionPath].permissionCheckPath].role, "assign")
80
83
  );
81
84
  }
82
85
  allow read: if request.auth != null; // All signed in users can read collection-data
@@ -141,7 +144,7 @@ service cloud.firestore {
141
144
  (
142
145
  (
143
146
  "roles" in user &&
144
- getRolePermission(user.roles[permissionCheckPath].role, permissionCheckPath, "assign")
147
+ getRolePermission(user.roles[permissionCheckPath].role, "assign")
145
148
  ) ||
146
149
  (
147
150
  "specialPermissions" in user &&
@@ -160,7 +163,7 @@ service cloud.firestore {
160
163
  (
161
164
  "roles" in user &&
162
165
  ruleHelper["edge-assignment-helper"].permissionCheckPath in user.roles &&
163
- getRolePermission(user.roles[ruleHelper["edge-assignment-helper"].permissionCheckPath].role, ruleHelper["edge-assignment-helper"].permissionCheckPath, 'assign')
166
+ getRolePermission(user.roles[ruleHelper["edge-assignment-helper"].permissionCheckPath].role, 'assign')
164
167
  ) ||
165
168
  (
166
169
  "specialPermissions" in user &&
@@ -188,7 +191,7 @@ service cloud.firestore {
188
191
  (
189
192
  "roles" in user &&
190
193
  permissionCheckPath in user.roles &&
191
- getRolePermission(user.roles[permissionCheckPath].role, permissionCheckPath, "assign")
194
+ getRolePermission(user.roles[permissionCheckPath].role, "assign")
192
195
  ) ||
193
196
  (
194
197
  "specialPermissions" in user &&
@@ -216,12 +219,15 @@ service cloud.firestore {
216
219
  return request.resource.data.roles.size() == 0 && request.resource.data.specialPermissions.size() == 0;
217
220
  }
218
221
 
219
- function getRolePermission(role, collection, permissionCheck) {
220
- let pathCollectionPermissions = get(/databases/$(database)/documents/collection-data/$(collection)).data;
221
- let defaultPermissions = get(/databases/$(database)/documents/collection-data/-default-).data;
222
- return (role in pathCollectionPermissions && pathCollectionPermissions[role][permissionCheck]) ||
223
- (role in defaultPermissions && defaultPermissions[role][permissionCheck]);
224
- }
222
+ function getRolePermission(role, permissionCheck) {
223
+ let permissions = {
224
+ 'admin': {'assign': true, 'delete': true, 'read': true, 'write': true},
225
+ 'editor': {'assign': false, 'delete': true, 'read': true, 'write': true},
226
+ 'user': {'assign': false, 'delete': false, 'read': true, 'write': false},
227
+ 'writer': {'assign': false, 'delete': false, 'read': true, 'write': true}
228
+ };
229
+ return permissions[role][permissionCheck];
230
+ }
225
231
 
226
232
  function canGet () {
227
233
  return resource == null ||
@@ -237,11 +243,14 @@ service cloud.firestore {
237
243
  }
238
244
 
239
245
  match /databases/{database}/documents/{seg1} {
240
- function getRolePermission(role, collection, permissionCheck) {
241
- let pathCollectionPermissions = get(/databases/$(database)/documents/collection-data/$(collection)).data;
242
- let defaultPermissions = get(/databases/$(database)/documents/collection-data/-default-).data;
243
- return (role in pathCollectionPermissions && pathCollectionPermissions[role][permissionCheck]) ||
244
- (role in defaultPermissions && defaultPermissions[role][permissionCheck]);
246
+ function getRolePermission(role, permissionCheck) {
247
+ let permissions = {
248
+ 'admin': {'assign': true, 'delete': true, 'read': true, 'write': true},
249
+ 'editor': {'assign': false, 'delete': true, 'read': true, 'write': true},
250
+ 'user': {'assign': false, 'delete': false, 'read': true, 'write': false},
251
+ 'writer': {'assign': false, 'delete': false, 'read': true, 'write': true}
252
+ };
253
+ return permissions[role][permissionCheck];
245
254
  }
246
255
  function checkPermission(collectionPath, permissionCheck) {
247
256
  let user = get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
@@ -267,7 +276,7 @@ service cloud.firestore {
267
276
  (
268
277
  "roles" in user &&
269
278
  ruleHelper[collectionPath].permissionCheckPath in user.roles &&
270
- getRolePermission(user.roles[ruleHelper[collectionPath].permissionCheckPath].role, ruleHelper[collectionPath].permissionCheckPath, permissionCheck)
279
+ getRolePermission(user.roles[ruleHelper[collectionPath].permissionCheckPath].role, permissionCheck)
271
280
  ) ||
272
281
  (
273
282
  "specialPermissions" in user &&
package/src/storage.rules CHANGED
@@ -36,6 +36,11 @@ service firebase.storage {
36
36
  )
37
37
  );
38
38
  }
39
+ match /public/{fileId} {
40
+ // Public directory with special rules
41
+ allow read: if true; // Unrestricted read access
42
+ allow write, delete: if request.auth != null; // Write and delete require authentication
43
+ }
39
44
  match /{dir}/{fileId} {
40
45
  // General read permission check based on Firestore data
41
46
  allow read: if checkPermission("read", dir);