@iobroker/db-objects-redis 7.2.3 → 8.0.0-alpha.1-20260921-e5941ca8a

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 (57) hide show
  1. package/build/cjs/index.d.ts +1 -1
  2. package/build/cjs/index.js +5 -3
  3. package/build/cjs/index.js.map +2 -2
  4. package/build/cjs/lib/objects/constants.js.map +2 -2
  5. package/build/cjs/lib/objects/interview.js +2 -0
  6. package/build/cjs/lib/objects/interview.js.map +1 -1
  7. package/build/cjs/lib/objects/lua-v3/custom.lua +23 -1
  8. package/build/cjs/lib/objects/lua-v3/filter.lua +23 -1
  9. package/build/cjs/lib/objects/lua-v3/programs.lua +23 -1
  10. package/build/cjs/lib/objects/lua-v3/script.lua +23 -1
  11. package/build/cjs/lib/objects/lua-v3/variables.lua +23 -1
  12. package/build/cjs/lib/objects/lua-v4/custom.lua +23 -1
  13. package/build/cjs/lib/objects/lua-v4/filter.lua +23 -1
  14. package/build/cjs/lib/objects/lua-v4/programs.lua +23 -1
  15. package/build/cjs/lib/objects/lua-v4/script.lua +23 -1
  16. package/build/cjs/lib/objects/lua-v4/variables.lua +23 -1
  17. package/build/cjs/lib/objects/lua-v4-no-sets/custom.lua +23 -1
  18. package/build/cjs/lib/objects/lua-v4-no-sets/filter.lua +23 -1
  19. package/build/cjs/lib/objects/lua-v4-no-sets/programs.lua +23 -1
  20. package/build/cjs/lib/objects/lua-v4-no-sets/script.lua +23 -1
  21. package/build/cjs/lib/objects/lua-v4-no-sets/variables.lua +23 -1
  22. package/build/cjs/lib/objects/objectsInRedisClient.d.ts +961 -125
  23. package/build/cjs/lib/objects/objectsInRedisClient.js +1092 -776
  24. package/build/cjs/lib/objects/objectsInRedisClient.js.map +3 -3
  25. package/build/cjs/lib/objects/objectsUtils.d.ts +139 -17
  26. package/build/cjs/lib/objects/objectsUtils.js +212 -192
  27. package/build/cjs/lib/objects/objectsUtils.js.map +3 -3
  28. package/build/esm/index.d.ts +1 -1
  29. package/build/esm/index.d.ts.map +1 -1
  30. package/build/esm/index.js.map +1 -1
  31. package/build/esm/lib/objects/constants.d.ts.map +1 -1
  32. package/build/esm/lib/objects/constants.js.map +1 -1
  33. package/build/esm/lib/objects/lua-v3/custom.lua +23 -1
  34. package/build/esm/lib/objects/lua-v3/filter.lua +23 -1
  35. package/build/esm/lib/objects/lua-v3/programs.lua +23 -1
  36. package/build/esm/lib/objects/lua-v3/script.lua +23 -1
  37. package/build/esm/lib/objects/lua-v3/variables.lua +23 -1
  38. package/build/esm/lib/objects/lua-v4/custom.lua +23 -1
  39. package/build/esm/lib/objects/lua-v4/filter.lua +23 -1
  40. package/build/esm/lib/objects/lua-v4/programs.lua +23 -1
  41. package/build/esm/lib/objects/lua-v4/script.lua +23 -1
  42. package/build/esm/lib/objects/lua-v4/variables.lua +23 -1
  43. package/build/esm/lib/objects/lua-v4-no-sets/custom.lua +23 -1
  44. package/build/esm/lib/objects/lua-v4-no-sets/filter.lua +23 -1
  45. package/build/esm/lib/objects/lua-v4-no-sets/programs.lua +23 -1
  46. package/build/esm/lib/objects/lua-v4-no-sets/script.lua +23 -1
  47. package/build/esm/lib/objects/lua-v4-no-sets/variables.lua +23 -1
  48. package/build/esm/lib/objects/objectsInRedisClient.d.ts +961 -125
  49. package/build/esm/lib/objects/objectsInRedisClient.d.ts.map +1 -1
  50. package/build/esm/lib/objects/objectsInRedisClient.js +880 -571
  51. package/build/esm/lib/objects/objectsInRedisClient.js.map +1 -1
  52. package/build/esm/lib/objects/objectsUtils.d.ts +139 -17
  53. package/build/esm/lib/objects/objectsUtils.d.ts.map +1 -1
  54. package/build/esm/lib/objects/objectsUtils.js +273 -221
  55. package/build/esm/lib/objects/objectsUtils.js.map +1 -1
  56. package/build/tsconfig.build.tsbuildinfo +1 -1
  57. package/package.json +9 -9
@@ -1,16 +1,17 @@
1
1
  /**
2
2
  * Common functions between client and server
3
3
  *
4
- * Copyright 2013-2024 bluefox <dogafox@gmail.com>
4
+ * Copyright 2013-2026 bluefox <dogafox@gmail.com>
5
5
  *
6
6
  * MIT License
7
7
  *
8
8
  */
9
9
  import path from 'node:path';
10
10
  import deepClone from 'deep-clone';
11
+ import mime from 'mime-types';
11
12
  import { tools } from '@iobroker/db-base';
12
13
  import * as CONSTS from '../../lib/objects/constants.js';
13
- import mime from 'mime-types';
14
+ import { SYSTEM_ADMIN_GROUP, SYSTEM_ADMIN_USER } from '../../lib/objects/constants.js';
14
15
  export * as CONSTS from '../../lib/objects/constants.js';
15
16
  export const ERRORS = CONSTS.ERRORS;
16
17
  export const REG_CHECK_ID = CONSTS.REG_CHECK_ID;
@@ -88,31 +89,36 @@ export function getMimeType(ext, isTextData) {
88
89
  }
89
90
  return { mimeType: isTextData ? 'text/plain' : 'application/octet-stream', isBinary: !isTextData };
90
91
  }
91
- export function checkFile(fileOptions, options, flag, defaultNewAcl) {
92
- if (typeof fileOptions.acl !== 'object') {
93
- fileOptions = {};
94
- fileOptions.mimeType = deepClone(fileOptions);
95
- fileOptions.acl = {
96
- owner: (defaultNewAcl && defaultNewAcl.owner) || CONSTS.SYSTEM_ADMIN_USER,
97
- ownerGroup: (defaultNewAcl && defaultNewAcl.ownerGroup) || CONSTS.SYSTEM_ADMIN_GROUP,
98
- permissions: (defaultNewAcl && defaultNewAcl.file) ||
99
- CONSTS.ACCESS_USER_RW | CONSTS.ACCESS_GROUP_READ | CONSTS.ACCESS_EVERY_READ, // '0644'
100
- };
101
- }
92
+ /**
93
+ * Check if the given options have the required rights on a file according to its ACL
94
+ *
95
+ * @param fileOptions The stored file options including its ACL
96
+ * @param fileOptions.mimeType The mime type of the file
97
+ * @param fileOptions.notExists Whether the file does not exist yet
98
+ * @param fileOptions.acl The access control list stored for the file
99
+ * @param userContext The current request options including the user and group
100
+ * @param flag The access flag(s) to check for
101
+ * @param defaultNewAcl The default ACL to use if the file has none yet
102
+ */
103
+ export function checkFile(fileOptions, userContext, flag, defaultNewAcl) {
104
+ // convert string to structure
105
+ fileOptions ||= {};
106
+ fileOptions.acl ||= {
107
+ owner: defaultNewAcl?.owner || CONSTS.SYSTEM_ADMIN_USER,
108
+ ownerGroup: defaultNewAcl?.ownerGroup || CONSTS.SYSTEM_ADMIN_GROUP,
109
+ permissions: defaultNewAcl?.file || CONSTS.ACCESS_USER_RW | CONSTS.ACCESS_GROUP_READ | CONSTS.ACCESS_EVERY_READ, // '0644'
110
+ };
102
111
  // Set default owner group
103
- fileOptions.acl.ownerGroup =
104
- fileOptions.acl.ownerGroup || (defaultNewAcl && defaultNewAcl.ownerGroup) || CONSTS.SYSTEM_ADMIN_GROUP;
105
- fileOptions.acl.owner = fileOptions.acl.owner || (defaultNewAcl && defaultNewAcl.owner) || CONSTS.SYSTEM_ADMIN_USER;
106
- fileOptions.acl.permissions =
107
- fileOptions.acl.permissions ||
108
- (defaultNewAcl && defaultNewAcl.file) ||
109
- CONSTS.ACCESS_USER_RW | CONSTS.ACCESS_GROUP_READ | CONSTS.ACCESS_EVERY_READ; // '0644'
110
- if (options.user !== CONSTS.SYSTEM_ADMIN_USER &&
111
- options.groups.indexOf(CONSTS.SYSTEM_ADMIN_GROUP) === -1 &&
112
+ fileOptions.acl.owner ||= defaultNewAcl?.owner || CONSTS.SYSTEM_ADMIN_USER;
113
+ fileOptions.acl.ownerGroup ||= defaultNewAcl?.ownerGroup || CONSTS.SYSTEM_ADMIN_GROUP;
114
+ fileOptions.acl.permissions ||=
115
+ defaultNewAcl?.file || CONSTS.ACCESS_USER_RW | CONSTS.ACCESS_GROUP_READ | CONSTS.ACCESS_EVERY_READ; // '0644'
116
+ if (userContext.user !== CONSTS.SYSTEM_ADMIN_USER &&
117
+ !userContext.groups.includes(CONSTS.SYSTEM_ADMIN_GROUP) &&
112
118
  fileOptions.acl) {
113
- if (fileOptions.acl.owner !== options.user) {
119
+ if (fileOptions.acl.owner !== userContext.user) {
114
120
  // Check if the user is in the group
115
- if (options.groups.indexOf(fileOptions.acl.ownerGroup) !== -1) {
121
+ if (userContext.groups.includes(fileOptions.acl.ownerGroup)) {
116
122
  // Check group rights
117
123
  if (!(fileOptions.acl.permissions & (flag << 4))) {
118
124
  return false;
@@ -134,40 +140,50 @@ export function checkFile(fileOptions, options, flag, defaultNewAcl) {
134
140
  }
135
141
  return true;
136
142
  }
137
- export function checkFileRights(objects, id, name, options, flag, callback) {
138
- const _options = options || {};
139
- if (!_options.user) {
143
+ /**
144
+ * Check whether the given user is allowed to access a file and call back with the effective options
145
+ *
146
+ * @param objects The objects database client
147
+ * @param id The id of the object owning the file
148
+ * @param name The file name, or null for the whole namespace
149
+ * @param options The current request options including the user
150
+ * @param flag The access flag(s) to check for
151
+ */
152
+ export async function checkFileRightsAsync(objects, id, name, options, flag) {
153
+ let userContext;
154
+ if (!options?.user) {
140
155
  // Before files converted, lets think: if no options it is admin
141
- _options.user = 'system.user.admin';
142
- _options.params = _options;
143
- _options.group = 'system.group.administrator';
144
- }
145
- if (!_options.acl) {
146
- objects.getUserGroup(_options.user, (_user, groups, acl) => {
147
- _options.acl = acl || {};
148
- _options.groups = groups;
149
- _options.group = groups ? groups[0] : null;
150
- checkFileRights(objects, id, name, _options, flag, callback);
151
- });
152
- return;
156
+ userContext = {
157
+ user: SYSTEM_ADMIN_USER,
158
+ group: SYSTEM_ADMIN_GROUP,
159
+ groups: [SYSTEM_ADMIN_GROUP],
160
+ acl: getDefaultAdminRights(),
161
+ };
153
162
  }
154
- // If user may write
155
- if (flag === CONSTS.ACCESS_WRITE && !_options.acl.file.write) {
156
- // write
157
- return tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION, _options);
163
+ else {
164
+ const [, groups, acl] = await getUserGroupAsync(objects, options.user);
165
+ userContext = {
166
+ user: options.user,
167
+ group: groups?.[0] || null,
168
+ groups,
169
+ acl,
170
+ };
171
+ }
172
+ if (flag === CONSTS.ACCESS_WRITE && !userContext.acl.file.write) {
173
+ // If user may write
174
+ throw new Error(ERRORS.ERROR_PERMISSION);
158
175
  }
159
176
  // If user may read
160
- if (flag === CONSTS.ACCESS_READ && !_options.acl.file.read) {
177
+ if (flag === CONSTS.ACCESS_READ && !userContext.acl.file.read) {
161
178
  // read
162
- return tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION, _options);
179
+ throw new Error(ERRORS.ERROR_PERMISSION);
163
180
  }
164
- _options.checked = true;
165
- objects.checkFile(id, name, _options, flag, (err, options, opt) => {
166
- if (err) {
167
- return tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION, options);
168
- }
169
- return tools.maybeCallbackWithError(callback, null, options, opt);
170
- });
181
+ userContext.checked = true;
182
+ const fileOptions = await objects.checkFileAsync(id, name, userContext, flag);
183
+ if (!fileOptions) {
184
+ throw new Error(ERRORS.ERROR_PERMISSION);
185
+ }
186
+ return { fileOptions, userContext };
171
187
  }
172
188
  // For users and groups
173
189
  function getDefaultAdminRights(acl, _isState) {
@@ -208,156 +224,157 @@ function getDefaultAdminRights(acl, _isState) {
208
224
  },
209
225
  };
210
226
  }
227
+ /**
228
+ * Determine the groups and effective ACL of the given user
229
+ *
230
+ * @param objects The objects database client
231
+ * @param user The id of the user to look up
232
+ * @param callback Called with the user, its groups and the effective ACL
233
+ */
211
234
  export function getUserGroup(objects, user, callback) {
235
+ try {
236
+ getUserGroupAsync(objects, user)
237
+ .then(result => tools.maybeCallbackWithError(callback, null, result[0], result[1], result[2]))
238
+ .catch(err => tools.maybeCallbackWithError(callback, err, deepClone(user), [], deepClone(defaultAcl.acl)));
239
+ }
240
+ catch (err) {
241
+ return tools.maybeCallbackWithError(callback, err, deepClone(user), [], deepClone(defaultAcl.acl));
242
+ }
243
+ }
244
+ /**
245
+ * Determine the groups and effective ACL of the given user
246
+ *
247
+ * @param objects The objects database client
248
+ * @param user The id of the user to look up
249
+ */
250
+ export async function getUserGroupAsync(objects, user) {
212
251
  if (!user || typeof user !== 'string' || !user.startsWith(USER_STARTS_WITH)) {
213
252
  console.log(`invalid user name: ${user}`);
214
- return tools.maybeCallbackWithError(callback, `invalid user name: ${user}`, deepClone(user), [], deepClone(defaultAcl.acl));
253
+ throw new Error(`invalid user name: ${user}`);
215
254
  }
216
255
  if (users[user]) {
217
- return tools.maybeCallbackWithError(callback, null, user, users[user].groups, users[user].acl);
256
+ return [user, users[user].groups, users[user].acl];
218
257
  }
219
258
  let error;
259
+ groups = [];
220
260
  // Read all groups
221
- objects.getObjectList({ startkey: 'system.group.', endkey: 'system.group.\u9999' }, { checked: true }, (err, arr) => {
222
- if (err) {
223
- error = err;
261
+ const groupList = await objects.getObjectListAsync({ startkey: 'system.group.', endkey: 'system.group.\u9999' }, { checked: true });
262
+ if (groupList) {
263
+ // Read all groups
264
+ for (let g = 0; g < groupList.rows.length; g++) {
265
+ const val = groupList.rows[g].value;
266
+ if (!val) {
267
+ continue;
268
+ }
269
+ groups[g] = val;
270
+ if (groups[g]._id === CONSTS.SYSTEM_ADMIN_GROUP) {
271
+ groups[g].common.acl = getDefaultAdminRights(groups[g].common.acl);
272
+ }
273
+ }
274
+ }
275
+ users = {};
276
+ const userList = await objects.getObjectListAsync({ startkey: 'system.user.', endkey: 'system.user.\u9999' }, { checked: true });
277
+ if (userList) {
278
+ for (const row of userList.rows) {
279
+ // cannot use here Object.assign, because required deep copy
280
+ users[row.value._id] = deepClone(defaultAcl);
224
281
  }
225
- groups = [];
226
- if (arr) {
227
- // Read all groups
228
- for (const g in arr.rows) {
229
- const val = arr.rows[g].value;
230
- if (!val) {
231
- continue;
282
+ }
283
+ users[CONSTS.SYSTEM_ADMIN_USER] ||= deepClone(defaultAcl);
284
+ users[CONSTS.SYSTEM_ADMIN_USER].acl = getDefaultAdminRights(users[CONSTS.SYSTEM_ADMIN_USER].acl);
285
+ for (let g = 0; g < groups.length; g++) {
286
+ if (!groups[g]?.common?.members) {
287
+ continue;
288
+ }
289
+ for (let m = 0; m < groups[g].common.members.length; m++) {
290
+ const u = groups[g].common.members[m];
291
+ if (!users[u]) {
292
+ error ||= `Unknown user in group "${groups[g]._id.replace('system.group.', '')}": ${u}`;
293
+ continue;
294
+ }
295
+ users[u].groups.push(groups[g]._id);
296
+ if (groups[g].common.acl?.file) {
297
+ if (!users[u].acl?.file) {
298
+ users[u].acl ||= {};
299
+ users[u].acl.file ||= {};
300
+ users[u].acl.file.create = groups[g].common.acl.file.create;
301
+ users[u].acl.file.read = groups[g].common.acl.file.read;
302
+ users[u].acl.file.write = groups[g].common.acl.file.write;
303
+ users[u].acl.file.delete = groups[g].common.acl.file.delete;
304
+ users[u].acl.file.list = groups[g].common.acl.file.list;
232
305
  }
233
- groups[g] = val;
234
- if (groups[g]._id === CONSTS.SYSTEM_ADMIN_GROUP) {
235
- groups[g].common.acl = getDefaultAdminRights(groups[g].common.acl);
306
+ else {
307
+ users[u].acl.file.create ||= groups[g].common.acl.file.create;
308
+ users[u].acl.file.read ||= groups[g].common.acl.file.read;
309
+ users[u].acl.file.write ||= groups[g].common.acl.file.write;
310
+ users[u].acl.file.delete ||= groups[g].common.acl.file.delete;
311
+ users[u].acl.file.list ||= groups[g].common.acl.file.list;
236
312
  }
237
313
  }
238
- }
239
- objects.getObjectList({ startkey: 'system.user.', endkey: 'system.user.\u9999' }, { checked: true }, (err, arr) => {
240
- if (err) {
241
- error = err;
314
+ if (groups[g].common.acl?.object) {
315
+ if (!users[u].acl?.object) {
316
+ users[u].acl ||= {};
317
+ users[u].acl.object ||= {};
318
+ users[u].acl.object.create = groups[g].common.acl.object.create;
319
+ users[u].acl.object.read = groups[g].common.acl.object.read;
320
+ users[u].acl.object.write = groups[g].common.acl.object.write;
321
+ users[u].acl.object.delete = groups[g].common.acl.object.delete;
322
+ users[u].acl.object.list = groups[g].common.acl.object.list;
323
+ }
324
+ else {
325
+ users[u].acl.object.create ||= groups[g].common.acl.object.create;
326
+ users[u].acl.object.read ||= groups[g].common.acl.object.read;
327
+ users[u].acl.object.write ||= groups[g].common.acl.object.write;
328
+ users[u].acl.object.delete ||= groups[g].common.acl.object.delete;
329
+ users[u].acl.object.list ||= groups[g].common.acl.object.list;
330
+ }
242
331
  }
243
- users = {};
244
- if (arr) {
245
- for (const row of arr.rows) {
246
- // cannot use here Object.assign, because required deep copy
247
- users[row.value._id] = deepClone(defaultAcl);
332
+ if (groups[g].common.acl?.users) {
333
+ if (!users[u].acl?.users) {
334
+ users[u].acl ||= {};
335
+ users[u].acl.users ||= {};
336
+ users[u].acl.users.create = groups[g].common.acl.users.create;
337
+ users[u].acl.users.read = groups[g].common.acl.users.read;
338
+ users[u].acl.users.write = groups[g].common.acl.users.write;
339
+ users[u].acl.users.delete = groups[g].common.acl.users.delete;
340
+ users[u].acl.users.list = groups[g].common.acl.users.list;
341
+ }
342
+ else {
343
+ users[u].acl.users.create ||= groups[g].common.acl.users.create;
344
+ users[u].acl.users.read ||= groups[g].common.acl.users.read;
345
+ users[u].acl.users.write ||= groups[g].common.acl.users.write;
346
+ users[u].acl.users.delete ||= groups[g].common.acl.users.delete;
347
+ users[u].acl.users.list ||= groups[g].common.acl.users.list;
248
348
  }
249
349
  }
250
- users[CONSTS.SYSTEM_ADMIN_USER] = users[CONSTS.SYSTEM_ADMIN_USER] || deepClone(defaultAcl);
251
- users[CONSTS.SYSTEM_ADMIN_USER].acl = getDefaultAdminRights(users[CONSTS.SYSTEM_ADMIN_USER].acl);
252
- for (let g = 0; g < groups.length; g++) {
253
- if (!groups[g] || !groups[g].common || !groups[g].common.members) {
254
- continue;
350
+ if (groups[g].common.acl?.state) {
351
+ if (!users[u].acl?.state) {
352
+ users[u].acl ||= {};
353
+ users[u].acl.state ||= {};
354
+ users[u].acl.state.create = groups[g].common.acl.state.create;
355
+ users[u].acl.state.read = groups[g].common.acl.state.read;
356
+ users[u].acl.state.write = groups[g].common.acl.state.write;
357
+ users[u].acl.state.delete = groups[g].common.acl.state.delete;
358
+ users[u].acl.state.list = groups[g].common.acl.state.list;
255
359
  }
256
- for (let m = 0; m < groups[g].common.members.length; m++) {
257
- const u = groups[g].common.members[m];
258
- if (!users[u]) {
259
- error =
260
- error ||
261
- `Unknown user in group "${groups[g]._id.replace('system.group.', '')}": ${u}`;
262
- continue;
263
- }
264
- users[u].groups.push(groups[g]._id);
265
- if (groups[g].common.acl && groups[g].common.acl.file) {
266
- if (!users[u].acl || !users[u].acl.file) {
267
- users[u].acl = users[u].acl || {};
268
- users[u].acl.file = users[u].acl.file || {};
269
- users[u].acl.file.create = groups[g].common.acl.file.create;
270
- users[u].acl.file.read = groups[g].common.acl.file.read;
271
- users[u].acl.file.write = groups[g].common.acl.file.write;
272
- users[u].acl.file.delete = groups[g].common.acl.file.delete;
273
- users[u].acl.file.list = groups[g].common.acl.file.list;
274
- }
275
- else {
276
- users[u].acl.file.create =
277
- users[u].acl.file.create || groups[g].common.acl.file.create;
278
- users[u].acl.file.read = users[u].acl.file.read || groups[g].common.acl.file.read;
279
- users[u].acl.file.write =
280
- users[u].acl.file.write || groups[g].common.acl.file.write;
281
- users[u].acl.file.delete =
282
- users[u].acl.file.delete || groups[g].common.acl.file.delete;
283
- users[u].acl.file.list = users[u].acl.file.list || groups[g].common.acl.file.list;
284
- }
285
- }
286
- if (groups[g].common.acl && groups[g].common.acl.object) {
287
- if (!users[u].acl || !users[u].acl.object) {
288
- users[u].acl = users[u].acl || {};
289
- users[u].acl.object = users[u].acl.object || {};
290
- users[u].acl.object.create = groups[g].common.acl.object.create;
291
- users[u].acl.object.read = groups[g].common.acl.object.read;
292
- users[u].acl.object.write = groups[g].common.acl.object.write;
293
- users[u].acl.object.delete = groups[g].common.acl.object.delete;
294
- users[u].acl.object.list = groups[g].common.acl.object.list;
295
- }
296
- else {
297
- users[u].acl.object.create =
298
- users[u].acl.object.create || groups[g].common.acl.object.create;
299
- users[u].acl.object.read =
300
- users[u].acl.object.read || groups[g].common.acl.object.read;
301
- users[u].acl.object.write =
302
- users[u].acl.object.write || groups[g].common.acl.object.write;
303
- users[u].acl.object.delete =
304
- users[u].acl.object.delete || groups[g].common.acl.object.delete;
305
- users[u].acl.object.list =
306
- users[u].acl.object.list || groups[g].common.acl.object.list;
307
- }
308
- }
309
- if (groups[g].common.acl && groups[g].common.acl.users) {
310
- if (!users[u].acl || !users[u].acl.users) {
311
- users[u].acl = users[u].acl || {};
312
- users[u].acl.users = users[u].acl.users || {};
313
- users[u].acl.users.create = groups[g].common.acl.users.create;
314
- users[u].acl.users.read = groups[g].common.acl.users.read;
315
- users[u].acl.users.write = groups[g].common.acl.users.write;
316
- users[u].acl.users.delete = groups[g].common.acl.users.delete;
317
- users[u].acl.users.list = groups[g].common.acl.users.list;
318
- }
319
- else {
320
- users[u].acl.users.create =
321
- users[u].acl.users.create || groups[g].common.acl.users.create;
322
- users[u].acl.users.read =
323
- users[u].acl.users.read || groups[g].common.acl.users.read;
324
- users[u].acl.users.write =
325
- users[u].acl.users.write || groups[g].common.acl.users.write;
326
- users[u].acl.users.delete =
327
- users[u].acl.users.delete || groups[g].common.acl.users.delete;
328
- users[u].acl.users.list =
329
- users[u].acl.users.list || groups[g].common.acl.users.list;
330
- }
331
- }
332
- if (groups[g].common.acl && groups[g].common.acl.state) {
333
- if (!users[u].acl || !users[u].acl.state) {
334
- users[u].acl = users[u].acl || {};
335
- users[u].acl.state = users[u].acl.state || {};
336
- users[u].acl.state.create = groups[g].common.acl.state?.create;
337
- users[u].acl.state.read = groups[g].common.acl.state?.read;
338
- users[u].acl.state.write = groups[g].common.acl.state?.write;
339
- users[u].acl.state.delete = groups[g].common.acl.state?.delete;
340
- users[u].acl.state.list = groups[g].common.acl.state?.list;
341
- }
342
- else {
343
- users[u].acl.state.create =
344
- users[u].acl.state.create || groups[g].common.acl.state?.create;
345
- users[u].acl.state.read =
346
- users[u].acl.state.read || groups[g].common.acl.state?.read;
347
- users[u].acl.state.write =
348
- users[u].acl.state.write || groups[g].common.acl.state?.write;
349
- users[u].acl.state.delete =
350
- users[u].acl.state.delete || groups[g].common.acl.state?.delete;
351
- users[u].acl.state.list =
352
- users[u].acl.state.list || groups[g].common.acl.state?.list;
353
- }
354
- }
360
+ else {
361
+ users[u].acl.state.create ||= groups[g].common.acl.state.create;
362
+ users[u].acl.state.read ||= groups[g].common.acl.state.read;
363
+ users[u].acl.state.write ||= groups[g].common.acl.state.write;
364
+ users[u].acl.state.delete ||= groups[g].common.acl.state.delete;
365
+ users[u].acl.state.list ||= groups[g].common.acl.state.list;
355
366
  }
356
367
  }
357
- return tools.maybeCallbackWithError(callback, error, user, users[user] ? users[user].groups : [], users[user] ? users[user].acl : deepClone(defaultAcl.acl));
358
- });
359
- });
368
+ }
369
+ }
370
+ return [user, users[user]?.groups || [], users[user]?.acl || deepClone(defaultAcl.acl)];
360
371
  }
372
+ /**
373
+ * Sanitize an object id and file name so they cannot escape the intended directory
374
+ *
375
+ * @param id The object id owning the file
376
+ * @param name The file name to sanitize
377
+ */
361
378
  export function sanitizePath(id, name) {
362
379
  if (!name) {
363
380
  name = '';
@@ -384,6 +401,13 @@ export function sanitizePath(id, name) {
384
401
  } // do not allow absolute paths
385
402
  return { id: id, name: name };
386
403
  }
404
+ /**
405
+ * Check if the given options have the required rights on an object according to its ACL
406
+ *
407
+ * @param obj The object (or file object) to check, or null
408
+ * @param options The current request options including the user and group
409
+ * @param flag The access flag(s) to check for
410
+ */
387
411
  export function checkObject(obj, options, flag) {
388
412
  // read rights of object
389
413
  if (!obj || !('common' in obj) || !obj.acl || flag === CONSTS.ACCESS_LIST) {
@@ -425,60 +449,88 @@ export function checkObject(obj, options, flag) {
425
449
  }
426
450
  return true; // ALL OK
427
451
  }
452
+ /**
453
+ * Check whether the given user is allowed to access an object and call back with the effective options
454
+ *
455
+ * @param objects The objects database client
456
+ * @param id The id of the object, or null
457
+ * @param object The object to check, or null
458
+ * @param options The current request options including the user
459
+ * @param flag The access flag(s) to check for
460
+ * @param callback Called with the effective options once the rights have been checked
461
+ */
428
462
  export function checkObjectRights(objects, id, object, options, flag, callback) {
429
- options = options || {};
430
- if (!options.user) {
463
+ try {
464
+ checkObjectRightsAsync(objects, id, object, options, flag)
465
+ .then(userContext => tools.maybeCallbackWithError(callback, null, userContext))
466
+ .catch(error => tools.maybeCallbackWithError(callback, error));
467
+ }
468
+ catch (error) {
469
+ tools.maybeCallbackWithError(callback, error);
470
+ }
471
+ }
472
+ /**
473
+ * Check whether the given user is allowed to access an object and call back with the effective options
474
+ *
475
+ * @param objects The objects database client
476
+ * @param id The id of the object, or null
477
+ * @param object The object to check, or null
478
+ * @param options The current request options including the user
479
+ * @param flag The access flag(s) to check for
480
+ */
481
+ export async function checkObjectRightsAsync(objects, id, object, options, flag) {
482
+ let userContext;
483
+ options ||= {};
484
+ if (!options?.user) {
431
485
  // Before files converted, lets think: if no options it is admin
432
- options = {
486
+ userContext = {
433
487
  user: CONSTS.SYSTEM_ADMIN_USER,
434
- params: options,
435
488
  group: CONSTS.SYSTEM_ADMIN_GROUP,
436
489
  groups: [CONSTS.SYSTEM_ADMIN_GROUP],
437
490
  acl: getDefaultAdminRights(),
438
491
  };
439
492
  }
440
- if (!options.acl) {
441
- return objects.getUserGroup(options.user, (_user, groups, acl) => {
442
- // TODO: ts needs it because we are doing async call before
443
- options = options || {};
444
- options.acl = acl || {};
445
- options.groups = groups;
446
- options.group = groups ? groups[0] : null;
447
- checkObjectRights(objects, id, object, options, flag, callback);
448
- });
493
+ else {
494
+ const [, groups, acl] = await getUserGroupAsync(objects, options.user);
495
+ userContext = {
496
+ user: options.user,
497
+ groups,
498
+ acl: acl || {},
499
+ group: groups?.[0] || null,
500
+ };
449
501
  }
450
502
  // now options are filled and we can go
451
- if (options.user === CONSTS.SYSTEM_ADMIN_USER ||
452
- options.group === CONSTS.SYSTEM_ADMIN_GROUP ||
453
- (options.groups && options.groups.includes(CONSTS.SYSTEM_ADMIN_GROUP))) {
454
- return tools.maybeCallbackWithError(callback, null, options);
503
+ if (userContext.user === CONSTS.SYSTEM_ADMIN_USER ||
504
+ userContext.group === CONSTS.SYSTEM_ADMIN_GROUP ||
505
+ userContext.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP)) {
506
+ return userContext;
455
507
  }
456
508
  // if user or group objects
457
509
  if (typeof id === 'string' && (id.startsWith(USER_STARTS_WITH) || id.startsWith(GROUP_STARTS_WITH))) {
458
510
  // If user may write
459
- if (flag === CONSTS.ACCESS_WRITE && !options.acl.users.write) {
511
+ if (flag === CONSTS.ACCESS_WRITE && !userContext.acl.users.write) {
460
512
  // write
461
- return tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION, options);
513
+ throw new Error(ERRORS.ERROR_PERMISSION);
462
514
  }
463
515
  // If user may read
464
- if (flag === CONSTS.ACCESS_READ && !options.acl.users.read) {
516
+ if (flag === CONSTS.ACCESS_READ && !userContext.acl.users.read) {
465
517
  // read
466
- return tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION, options);
518
+ throw new Error(ERRORS.ERROR_PERMISSION);
467
519
  }
468
520
  // If user may delete
469
- if (flag === CONSTS.ACCESS_DELETE && !options.acl.users.delete) {
521
+ if (flag === CONSTS.ACCESS_DELETE && !userContext.acl.users.delete) {
470
522
  // delete
471
- return tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION, options);
523
+ throw new Error(ERRORS.ERROR_PERMISSION);
472
524
  }
473
525
  // If user may list
474
- if (flag === CONSTS.ACCESS_LIST && !options.acl.users.list) {
526
+ if (flag === CONSTS.ACCESS_LIST && !userContext.acl.users.list) {
475
527
  // list
476
- return tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION, options);
528
+ throw new Error(ERRORS.ERROR_PERMISSION);
477
529
  }
478
530
  // If user may create
479
- if (flag === CONSTS.ACCESS_CREATE && !options.acl.users.create) {
531
+ if (flag === CONSTS.ACCESS_CREATE && !userContext.acl.users.create) {
480
532
  // create
481
- return tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION, options);
533
+ throw new Error(ERRORS.ERROR_PERMISSION);
482
534
  }
483
535
  // If user may write
484
536
  if (flag === CONSTS.ACCESS_DELETE) {
@@ -487,32 +539,32 @@ export function checkObjectRights(objects, id, object, options, flag, callback)
487
539
  }
488
540
  }
489
541
  // If user may write
490
- if (flag === CONSTS.ACCESS_WRITE && !options.acl.object.write) {
542
+ if (flag === CONSTS.ACCESS_WRITE && !userContext.acl.object.write) {
491
543
  // write
492
- tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION, options);
544
+ throw new Error(ERRORS.ERROR_PERMISSION);
493
545
  }
494
546
  // If user may read
495
- if (flag === CONSTS.ACCESS_READ && !options.acl.object.read) {
547
+ if (flag === CONSTS.ACCESS_READ && !userContext.acl.object.read) {
496
548
  // read
497
- return tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION, options);
549
+ throw new Error(ERRORS.ERROR_PERMISSION);
498
550
  }
499
551
  // If user may delete
500
- if (flag === CONSTS.ACCESS_DELETE && !options.acl.object.delete) {
552
+ if (flag === CONSTS.ACCESS_DELETE && !userContext.acl.object.delete) {
501
553
  // delete
502
- return tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION, options);
554
+ throw new Error(ERRORS.ERROR_PERMISSION);
503
555
  }
504
556
  // If user may list
505
- if (flag === CONSTS.ACCESS_LIST && !options.acl.object.list) {
557
+ if (flag === CONSTS.ACCESS_LIST && !userContext.acl.object.list) {
506
558
  // list
507
- return tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION, options);
559
+ throw new Error(ERRORS.ERROR_PERMISSION);
508
560
  }
509
561
  if (flag === CONSTS.ACCESS_DELETE) {
510
562
  // write
511
563
  flag = CONSTS.ACCESS_WRITE;
512
564
  }
513
- if (id && !checkObject(object, options, flag)) {
514
- return tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION, options);
565
+ if (id && !checkObject(object, userContext, flag)) {
566
+ throw new Error(ERRORS.ERROR_PERMISSION);
515
567
  }
516
- return tools.maybeCallbackWithError(callback, null, options);
568
+ return userContext;
517
569
  }
518
570
  //# sourceMappingURL=objectsUtils.js.map