@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 +36 -98
- package/package.json +1 -1
- package/src/config.js +37 -0
- package/src/edgeFirebase.js +2 -38
- package/src/firestore.rules +30 -21
- package/src/storage.rules +5 -0
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.
|
|
1083
|
-
|
|
1084
|
-
|
|
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.
|
|
1100
|
-
|
|
1101
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
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
|
+
|
package/src/edgeFirebase.js
CHANGED
|
@@ -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 '
|
|
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',
|
package/src/firestore.rules
CHANGED
|
@@ -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,
|
|
60
|
-
let
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
220
|
-
let
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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,
|
|
241
|
-
let
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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,
|
|
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);
|