@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
@@ -5,6 +5,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
9
  var __export = (target, all) => {
9
10
  for (var name in all)
10
11
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -32,21 +33,25 @@ __export(objectsInRedisClient_exports, {
32
33
  });
33
34
  module.exports = __toCommonJS(objectsInRedisClient_exports);
34
35
  var __import_meta_url = typeof document === "undefined" ? new (require("url".replace("", ""))).URL("file:" + __filename).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
35
- var import_node = __toESM(require("node.extend"), 1);
36
- var import_ioredis = __toESM(require("ioredis"), 1);
37
- var import_db_base = require("@iobroker/db-base");
38
36
  var import_node_fs = __toESM(require("node:fs"), 1);
39
37
  var import_node_path = __toESM(require("node:path"), 1);
40
38
  var import_node_crypto = __toESM(require("node:crypto"), 1);
41
39
  var import_node_util = require("node:util");
40
+ var url = __toESM(require("node:url"), 1);
42
41
  var import_deep_clone = __toESM(require("deep-clone"), 1);
43
- var utils = __toESM(require("../../lib/objects/objectsUtils.js"), 1);
44
42
  var import_semver = __toESM(require("semver"), 1);
43
+ var import_node = __toESM(require("node.extend"), 1);
44
+ var import_ioredis = __toESM(require("ioredis"), 1);
45
+ var import_db_base = require("@iobroker/db-base");
46
+ var import_objectsUtils = require("../../lib/objects/objectsUtils.js");
47
+ var utils = __toESM(require("../../lib/objects/objectsUtils.js"), 1);
45
48
  var CONSTS = __toESM(require("../../lib/objects/constants.js"), 1);
46
- var url = __toESM(require("node:url"), 1);
47
49
  const thisDir = url.fileURLToPath(new URL(".", __import_meta_url || `file://${__filename}`));
48
50
  const ERRORS = CONSTS.ERRORS;
49
51
  class ObjectsInRedisClient {
52
+ static {
53
+ __name(this, "ObjectsInRedisClient");
54
+ }
50
55
  client;
51
56
  fileNamespace;
52
57
  redisNamespace;
@@ -72,6 +77,9 @@ class ObjectsInRedisClient {
72
77
  noLegacyMultihost;
73
78
  userSubscriptions;
74
79
  systemSubscriptions;
80
+ /**
81
+ * @param settings Settings for the objects client including connection and namespaces
82
+ */
75
83
  constructor(settings) {
76
84
  this.settings = settings || {};
77
85
  this.redisNamespace = `${this.settings.redisNamespace || this.settings.connection?.redisNamespace || "cfg"}.`;
@@ -126,6 +134,9 @@ class ObjectsInRedisClient {
126
134
  throw new Error(`This host does not support protocol version "${protoVersion}"`);
127
135
  }
128
136
  }
137
+ /**
138
+ * Connect to the objects database and set up the change and file subscriptions
139
+ */
129
140
  connectDb() {
130
141
  this.settings.connection = this.settings.connection || {};
131
142
  const onChange = this.settings.change;
@@ -166,7 +177,7 @@ class ObjectsInRedisClient {
166
177
  const defaultPort = Array.isArray(configuredPort) ? null : configuredPort;
167
178
  this.settings.connection.options.sentinels = this.settings.connection.host.map((redisNode, idx) => ({
168
179
  host: redisNode,
169
- // @ts-expect-error ts does not get that if defPort is null we have an array
180
+ // @ts-expect-error ts does not get that if defPort is null, we have an array
170
181
  port: defaultPort === null ? configuredPort[idx] : defaultPort
171
182
  }));
172
183
  this.settings.connection.options.name = this.settings.connection.sentinelName ? this.settings.connection.sentinelName : "mymaster";
@@ -178,10 +189,10 @@ class ObjectsInRedisClient {
178
189
  }
179
190
  this.settings.connection.options.db = this.settings.connection.options.db || 0;
180
191
  this.settings.connection.options.family = this.settings.connection.options.family || 0;
181
- this.settings.connection.options.password = this.settings.connection.options.auth_pass || this.settings.connection.pass || null;
192
+ this.settings.connection.options.password = this.settings.connection.options.auth_pass || this.settings.connection.pass || void 0;
182
193
  this.settings.connection.options.autoResubscribe = false;
183
194
  this.settings.connection.options.connectionName = this.namespace.replace(/\s/g, "");
184
- this.client = new import_ioredis.default(this.settings.connection.options);
195
+ this.client = new import_ioredis.default.Redis(this.settings.connection.options);
185
196
  this.client.on("error", (error) => {
186
197
  if (this.settings.connection.enhancedLogging) {
187
198
  this.log.silly(`${this.namespace} Redis ERROR Objects: (${this.stop}) ${error.message} / ${error.stack}`);
@@ -258,7 +269,7 @@ class ObjectsInRedisClient {
258
269
  if (!this.subSystem && typeof onChange === "function") {
259
270
  initCounter++;
260
271
  this.log.debug(`${this.namespace} Objects create System PubSub Client`);
261
- this.subSystem = new import_ioredis.default(this.settings.connection.options);
272
+ this.subSystem = new import_ioredis.default.Redis(this.settings.connection.options);
262
273
  if (typeof this.settings.primaryHostLost === "function") {
263
274
  try {
264
275
  await this.client.config("SET", "notify-keyspace-events", "Exe");
@@ -303,7 +314,7 @@ class ObjectsInRedisClient {
303
314
  if (id === "system.config" && obj2?.common?.defaultNewAcl && !(0, import_node_util.isDeepStrictEqual)(obj2.common.defaultNewAcl, this.defaultNewAcl)) {
304
315
  this.defaultNewAcl = (0, import_deep_clone.default)(obj2.common.defaultNewAcl);
305
316
  if (this.settings.controller) {
306
- this.setDefaultAcl(this.defaultNewAcl);
317
+ this.setDefaultAcl(this.defaultNewAcl).catch((e) => this.log.error(`${this.namespace} Cannot set default ACL: ${e.message}`));
307
318
  }
308
319
  }
309
320
  onChange(id, obj2);
@@ -379,7 +390,7 @@ class ObjectsInRedisClient {
379
390
  if (!this.sub && (typeof onChangeUser === "function" || typeof onChangeFileUser === "function")) {
380
391
  initCounter++;
381
392
  this.log.debug(`${this.namespace} Objects create User PubSub Client`);
382
- this.sub = new import_ioredis.default(this.settings.connection.options);
393
+ this.sub = new import_ioredis.default.Redis(this.settings.connection.options);
383
394
  this.sub.on("pmessage", (pattern, channel, message) => {
384
395
  setImmediate(() => {
385
396
  this.log.silly(`${this.namespace} Objects user redis pmessage ${pattern}/${channel}:${message}`);
@@ -529,6 +540,9 @@ class ObjectsInRedisClient {
529
540
  }
530
541
  });
531
542
  }
543
+ /**
544
+ * Get the current status of the database
545
+ */
532
546
  getStatus() {
533
547
  return { type: "redis", server: false };
534
548
  }
@@ -536,7 +550,7 @@ class ObjectsInRedisClient {
536
550
  * Checks if given ID is a meta-object, else throws error
537
551
  *
538
552
  * @param id to check
539
- * @throws Error if id is invalid
553
+ * @throws {Error} if id is invalid
540
554
  */
541
555
  async validateMetaObject(id) {
542
556
  if (this.existingMetaObjects[id] === void 0) {
@@ -551,6 +565,11 @@ class ObjectsInRedisClient {
551
565
  throw new Error(`${id} is not an object of type "meta"`);
552
566
  }
553
567
  }
568
+ /**
569
+ * Normalize a file name by collapsing slashes and backslashes into a single forward slash
570
+ *
571
+ * @param name The file name to normalize
572
+ */
554
573
  normalizeFilename(name) {
555
574
  return name ? name.replace(/[/\\]+/g, "/") : name;
556
575
  }
@@ -558,7 +577,7 @@ class ObjectsInRedisClient {
558
577
  /**
559
578
  * Sets a buffer to the Redis DB
560
579
  *
561
- * @param id id of the file
580
+ * @param id ID of the file
562
581
  * @param data content, if string is passed it will be converted to a Buffer
563
582
  */
564
583
  async _setBinaryState(id, data) {
@@ -572,7 +591,7 @@ class ObjectsInRedisClient {
572
591
  await this.client.publish(id, data.byteLength.toString(10));
573
592
  }
574
593
  /**
575
- * get buffer of given id from redis
594
+ * Get a buffer of a given ID from redis
576
595
  *
577
596
  * @param id - id of the data with namespace prefix
578
597
  */
@@ -583,7 +602,7 @@ class ObjectsInRedisClient {
583
602
  return this.client.getBuffer(id);
584
603
  }
585
604
  /**
586
- * deletes binary state of given id from redis db
605
+ * deletes binary state of a given ID from redis db
587
606
  *
588
607
  * @param id - id to delete, with namespace prefix
589
608
  */
@@ -595,6 +614,13 @@ class ObjectsInRedisClient {
595
614
  await this.client.publish(id, "null");
596
615
  }
597
616
  }
617
+ /**
618
+ * Build the internal redis key for a file
619
+ *
620
+ * @param id The ID of the object owning the file
621
+ * @param name The file name
622
+ * @param isMeta Whether to return the key of the meta-entry (true) or the data entry (false)
623
+ */
598
624
  getFileId(id, name, isMeta) {
599
625
  name = this.normalizeFilename(name);
600
626
  if (id.endsWith(".admin")) {
@@ -617,37 +643,58 @@ class ObjectsInRedisClient {
617
643
  name = normalized.name;
618
644
  return `${this.fileNamespace + id}$%$${name}${isMeta !== void 0 ? isMeta ? "$%$meta" : "$%$data" : ""}`;
619
645
  }
620
- async checkFile(id, name, options2, flag, callback) {
621
- const fileId = this.getFileId(id, name, true);
646
+ /**
647
+ * Check whether the current options have the required rights on a file
648
+ *
649
+ * @param id The id of the object owning the file
650
+ * @param name The file name
651
+ * @param userContext The resolved user context used for permission checks
652
+ * @param flag The access flag(s) to check for
653
+ */
654
+ async checkFileAsync(id, name, userContext2, flag) {
655
+ const fileId = this.getFileId(id, name ?? "", true);
622
656
  if (!fileId) {
623
- const fileOptions2 = { notExists: true };
624
- if (utils.checkFile(fileOptions2, options2, flag, this.defaultNewAcl)) {
625
- return import_db_base.tools.maybeCallback(callback, false, options2, fileOptions2);
657
+ if (!utils.checkFile({ notExists: true }, userContext2, flag, this.defaultNewAcl)) {
658
+ return null;
626
659
  }
627
- return import_db_base.tools.maybeCallback(callback, true, options2);
660
+ return { notExists: true };
628
661
  }
629
662
  if (!this.client) {
630
- return import_db_base.tools.maybeCallbackWithRedisError(callback, ERRORS.ERROR_DB_CLOSED, options2);
663
+ throw new Error(ERRORS.ERROR_DB_CLOSED);
631
664
  }
632
- let fileOptions;
665
+ let fileOptionsStr;
633
666
  try {
634
- fileOptions = await this.client.get(fileId);
667
+ fileOptionsStr = await this.client.get(fileId);
635
668
  } catch {
636
669
  }
637
- fileOptions = fileOptions || '{"notExists": true}';
670
+ fileOptionsStr ||= '{"notExists": true}';
671
+ let fileOptions;
638
672
  try {
639
- fileOptions = JSON.parse(fileOptions);
673
+ fileOptions = JSON.parse(fileOptionsStr);
640
674
  } catch {
641
- this.log.error(`${this.namespace} Cannot parse JSON ${id}: ${fileOptions}`);
675
+ this.log.error(`${this.namespace} Cannot parse JSON ${id}: ${fileOptionsStr}`);
642
676
  fileOptions = { notExists: true };
643
677
  }
644
- if (utils.checkFile(fileOptions, options2, flag, this.defaultNewAcl)) {
645
- return import_db_base.tools.maybeCallback(callback, false, options2, fileOptions);
678
+ if (!utils.checkFile(fileOptions, userContext2, flag, this.defaultNewAcl)) {
679
+ return null;
646
680
  }
647
- return import_db_base.tools.maybeCallback(callback, true, options2);
681
+ return fileOptions;
648
682
  }
649
- checkFileRights(id, name, options2, flag, callback) {
650
- return utils.checkFileRights(this, id, name, options2, flag, callback);
683
+ /**
684
+ * Check whether the current user is allowed to access a file
685
+ *
686
+ * @param id The id of the object owning the file
687
+ * @param name The file name, or null for the whole namespace
688
+ * @param options The current request options including the user
689
+ * @param flag The access flag(s) to check for
690
+ * @param callback Called with the effective options once the rights have been checked
691
+ */
692
+ checkFileRights(id, name, options, flag, callback) {
693
+ try {
694
+ utils.checkFileRightsAsync(this, id, name, options, flag).then((result) => callback(null, result.fileOptions, result.userContext)).catch((err) => callback(err));
695
+ } catch (error) {
696
+ callback(error);
697
+ }
651
698
  }
652
699
  async _setDefaultAcl(ids, defaultAcl) {
653
700
  for (const id of ids) {
@@ -662,6 +709,11 @@ class ObjectsInRedisClient {
662
709
  }
663
710
  }
664
711
  }
712
+ /**
713
+ * Set the default ACL applied to new objects and apply it to all existing objects without an ACL
714
+ *
715
+ * @param defaultNewAcl The default ACL to use, or null to use the built-in default
716
+ */
665
717
  async setDefaultAcl(defaultNewAcl) {
666
718
  this.defaultNewAcl = defaultNewAcl || {
667
719
  owner: CONSTS.SYSTEM_ADMIN_USER,
@@ -679,24 +731,32 @@ class ObjectsInRedisClient {
679
731
  this.log.error(`${this.namespace} Could not update default acl: ${e.message}`);
680
732
  }
681
733
  }
734
+ /**
735
+ * Determine the groups and effective ACL of the given user
736
+ *
737
+ * @param user The id of the user to look up
738
+ * @param callback Called with the user, its groups, and the effective ACL
739
+ */
682
740
  getUserGroup(user, callback) {
683
741
  return utils.getUserGroup(this, user, (error, user2, userGroups, userAcl) => {
684
742
  if (error) {
685
743
  this.log.error(`${this.namespace} ${error.stack}`);
686
744
  }
687
- return import_db_base.tools.maybeCallback(callback, user2, userGroups, userAcl);
745
+ if (typeof callback === "function") {
746
+ callback(user2, userGroups, userAcl);
747
+ }
688
748
  });
689
749
  }
690
- async _writeFile(id, name, data, options2, callback, meta) {
750
+ async _writeFile(id, name, data, options, callback, meta) {
691
751
  const matchedExtension = name.match(/\.[^.]+$/);
692
- const ext = matchedExtension ? matchedExtension[0] : "";
752
+ const ext = matchedExtension?.[0] || "";
693
753
  const isTextData = typeof data === "string";
694
754
  const { mimeType, isBinary } = utils.getMimeType(ext, isTextData);
695
755
  const metaID = this.getFileId(id, name, true);
696
756
  if (!this.client) {
697
757
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_DB_CLOSED);
698
758
  }
699
- if (options2.virtualFile) {
759
+ if (options.virtualFile) {
700
760
  meta = {
701
761
  notExists: true,
702
762
  virtualFile: true
@@ -708,28 +768,24 @@ class ObjectsInRedisClient {
708
768
  return import_db_base.tools.maybeCallbackWithRedisError(callback, e);
709
769
  }
710
770
  } else {
711
- if (!meta) {
712
- meta = { createdAt: Date.now() };
713
- }
714
- if (!meta.acl) {
715
- meta.acl = {
716
- owner: options2.user || this.defaultNewAcl && this.defaultNewAcl.owner || CONSTS.SYSTEM_ADMIN_USER,
717
- ownerGroup: options2.group || this.defaultNewAcl && this.defaultNewAcl.ownerGroup || CONSTS.SYSTEM_ADMIN_GROUP,
718
- permissions: options2.mode || this.defaultNewAcl && this.defaultNewAcl.file || 1604
719
- };
720
- }
771
+ meta ||= { createdAt: Date.now() };
772
+ meta.acl ||= {
773
+ owner: options.user || this.defaultNewAcl?.owner || CONSTS.SYSTEM_ADMIN_USER,
774
+ ownerGroup: options.group || this.defaultNewAcl?.ownerGroup || CONSTS.SYSTEM_ADMIN_GROUP,
775
+ permissions: options.mode || this.defaultNewAcl?.file || 1604
776
+ };
721
777
  meta.stats = {
722
- size: data ? data.length : 0
778
+ size: data?.length || 0
723
779
  };
724
780
  if (Object.prototype.hasOwnProperty.call(meta, "notExists")) {
725
781
  delete meta.notExists;
726
782
  }
727
- meta.mimeType = options2.mimeType || mimeType;
783
+ meta.mimeType = options.mimeType || mimeType;
728
784
  meta.binary = isBinary;
729
- meta.acl.ownerGroup = meta.acl.ownerGroup || this.defaultNewAcl && this.defaultNewAcl.ownerGroup || CONSTS.SYSTEM_ADMIN_GROUP;
785
+ meta.acl.ownerGroup ||= this.defaultNewAcl?.ownerGroup || CONSTS.SYSTEM_ADMIN_GROUP;
730
786
  meta.modifiedAt = Date.now();
731
787
  try {
732
- await this._setBinaryState(this.getFileId(id, name, false), data);
788
+ await this._setBinaryState(this.getFileId(id, name, false), data || "");
733
789
  await this.client.set(metaID, JSON.stringify(meta));
734
790
  return import_db_base.tools.maybeCallback(callback);
735
791
  } catch (e) {
@@ -737,19 +793,22 @@ class ObjectsInRedisClient {
737
793
  }
738
794
  }
739
795
  }
740
- async writeFile(id, name, data, options2, callback) {
741
- if (typeof options2 === "function") {
742
- callback = options2;
743
- options2 = null;
744
- }
745
- if (typeof options2 === "string") {
746
- options2 = { mimeType: options2 };
747
- }
748
- if (options2?.acl) {
749
- options2.acl = null;
796
+ /**
797
+ * Write data into a file of an object
798
+ *
799
+ * @param id The id of the object owning the file
800
+ * @param name The file name
801
+ * @param data The data to write
802
+ * @param options The current request options including the user, or the callback
803
+ * @param callback Called once the file has been written
804
+ */
805
+ async writeFile(id, name, data, options, callback) {
806
+ if (typeof options === "function") {
807
+ callback = options;
808
+ options = null;
750
809
  }
751
810
  if (!callback) {
752
- return this.writeFileAsync(id, name, data, options2);
811
+ return this.writeFileAsync(id, name, data, options);
753
812
  }
754
813
  try {
755
814
  await this.validateMetaObject(id);
@@ -766,15 +825,23 @@ class ObjectsInRedisClient {
766
825
  if (data === void 0) {
767
826
  data = null;
768
827
  }
769
- return this.checkFileRights(id, name, options2, CONSTS.ACCESS_WRITE, (err, options3, meta) => {
828
+ return this.checkFileRights(id, name, options, CONSTS.ACCESS_WRITE, (err, meta) => {
770
829
  if (err) {
771
830
  return import_db_base.tools.maybeCallbackWithError(callback, err);
772
831
  }
773
- return this._writeFile(id, name, data, options3, callback, meta);
832
+ return this._writeFile(id, name, data, options || {}, callback, meta);
774
833
  });
775
834
  }
776
- writeFileAsync(id, name, data, options2) {
777
- return new Promise((resolve, reject) => this.writeFile(id, name, data, options2, (err) => err ? reject(err) : resolve()));
835
+ /**
836
+ * Promise-version of writeFile
837
+ *
838
+ * @param id The ID of the object owning the file
839
+ * @param name The file name
840
+ * @param data The data to write
841
+ * @param options The current request options including the user
842
+ */
843
+ writeFileAsync(id, name, data, options) {
844
+ return new Promise((resolve, reject) => this.writeFile(id, name, data, options, (err) => err ? reject(err) : resolve()));
778
845
  }
779
846
  async _readFile(id, name, meta) {
780
847
  if (meta.notExists) {
@@ -783,24 +850,28 @@ class ObjectsInRedisClient {
783
850
  if (!this.client) {
784
851
  throw new Error(ERRORS.ERROR_DB_CLOSED);
785
852
  }
786
- let buffer;
787
- buffer = await this._getBinaryState(this.getFileId(id, name, false));
853
+ let file = await this._getBinaryState(this.getFileId(id, name, false));
788
854
  const mimeType = meta?.mimeType;
789
- if (meta && !meta.binary && buffer) {
790
- buffer = buffer.toString();
855
+ if (meta && !meta.binary && file) {
856
+ file = file.toString();
791
857
  }
792
- return { file: buffer, mimeType };
858
+ return { file, mimeType };
793
859
  }
794
- readFile(id, name, options2, callback) {
795
- if (typeof options2 === "function") {
796
- callback = options2;
797
- options2 = null;
798
- }
799
- if (options2?.acl) {
800
- options2.acl = null;
860
+ /**
861
+ * Read a file of an object
862
+ *
863
+ * @param id The ID of the object owning the file
864
+ * @param name The file name
865
+ * @param options The current request options including the user, or the callback
866
+ * @param callback Called with the file content and mime type
867
+ */
868
+ readFile(id, name, options, callback) {
869
+ if (typeof options === "function") {
870
+ callback = options;
871
+ options = null;
801
872
  }
802
873
  if (!callback) {
803
- return new Promise((resolve, reject) => this.readFile(id, name, options2, (err, res, mimeType) => err ? reject(err) : resolve({ file: res, mimeType })));
874
+ return new Promise((resolve, reject) => this.readFile(id, name, options, (err, res, mimeType) => err ? reject(err) : resolve({ file: res, mimeType })));
804
875
  }
805
876
  if (typeof name !== "string" || !name.length || name === "/") {
806
877
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_NOT_FOUND);
@@ -808,8 +879,8 @@ class ObjectsInRedisClient {
808
879
  if (name.startsWith("/")) {
809
880
  name = name.substring(1);
810
881
  }
811
- options2 = options2 || {};
812
- this.checkFileRights(id, name, options2, CONSTS.ACCESS_READ, async (err, options3, meta) => {
882
+ options ||= {};
883
+ this.checkFileRights(id, name, options, CONSTS.ACCESS_READ, async (err, meta) => {
813
884
  if (err) {
814
885
  return import_db_base.tools.maybeCallbackWithError(callback, err);
815
886
  }
@@ -822,27 +893,21 @@ class ObjectsInRedisClient {
822
893
  });
823
894
  }
824
895
  /**
825
- * Check if given object exists
896
+ * Check if a given object exists
826
897
  *
827
- * @param id id of the object
898
+ * @param id ID of the object
828
899
  * @param options optional user context
829
900
  */
830
- async objectExists(id, options2) {
901
+ async objectExists(id, options) {
831
902
  if (!this.client) {
832
903
  throw new Error(ERRORS.ERROR_DB_CLOSED);
833
904
  }
834
905
  if (!id || typeof id !== "string") {
835
- throw new Error(`invalid id ${JSON.stringify(id)}`);
906
+ throw new Error(`invalid ID ${JSON.stringify(id)}`);
836
907
  }
837
908
  try {
838
909
  await new Promise((resolve, reject) => {
839
- utils.checkObjectRights(this, null, null, options2, CONSTS.ACCESS_LIST, (err) => {
840
- if (err) {
841
- reject(err);
842
- } else {
843
- resolve();
844
- }
845
- });
910
+ utils.checkObjectRights(this, null, null, options, CONSTS.ACCESS_LIST, (err) => err ? reject(err) : resolve());
846
911
  });
847
912
  const exists = await this.client.exists(this.objNamespace + id);
848
913
  return !!exists;
@@ -852,13 +917,13 @@ class ObjectsInRedisClient {
852
917
  }
853
918
  }
854
919
  /**
855
- * Check if given file exists
920
+ * Check if a given file exists
856
921
  *
857
- * @param id id of the namespace
922
+ * @param id ID of the namespace
858
923
  * @param name name of the file
859
924
  * @param options optional user context
860
925
  */
861
- async fileExists(id, name, options2) {
926
+ async fileExists(id, name, options) {
862
927
  if (typeof name !== "string") {
863
928
  name = "";
864
929
  }
@@ -866,15 +931,7 @@ class ObjectsInRedisClient {
866
931
  name = name.substring(1);
867
932
  }
868
933
  try {
869
- await new Promise((resolve, reject) => {
870
- this.checkFileRights(id, name, options2, CONSTS.ACCESS_READ, (err) => {
871
- if (err) {
872
- reject(err);
873
- } else {
874
- resolve();
875
- }
876
- });
877
- });
934
+ await utils.checkFileRightsAsync(this, id, name, options, CONSTS.ACCESS_READ);
878
935
  if (!this.client) {
879
936
  throw new Error(ERRORS.ERROR_DB_CLOSED);
880
937
  }
@@ -886,25 +943,30 @@ class ObjectsInRedisClient {
886
943
  throw new Error(`Cannot check file existence of "${id}": ${e.message}`);
887
944
  }
888
945
  }
889
- async _unlink(id, name, options2, meta) {
946
+ async _unlink(id, name, userContext2, meta) {
890
947
  if (!this.client) {
891
948
  throw new Error(ERRORS.ERROR_DB_CLOSED);
892
949
  }
893
- if (meta && meta.notExists) {
894
- return this._rm(id, name, options2);
950
+ if (meta?.notExists) {
951
+ return this._rm(id, name, userContext2);
895
952
  }
896
953
  const metaID = this.getFileId(id, name, true);
897
954
  const dataID = this.getFileId(id, name, false);
898
955
  await this._delBinaryState(dataID);
899
956
  await this.client.del(metaID);
900
957
  }
901
- unlink(id, name, options2, callback) {
902
- if (typeof options2 === "function") {
903
- callback = options2;
904
- options2 = null;
905
- }
906
- if (options2?.acl) {
907
- options2.acl = null;
958
+ /**
959
+ * Delete a file or directory of an object
960
+ *
961
+ * @param id The id of the object owning the file
962
+ * @param name The file or directory name to delete
963
+ * @param options The current request options including the user, or the callback
964
+ * @param callback Called with the list of removed files
965
+ */
966
+ unlink(id, name, options, callback) {
967
+ if (typeof options === "function") {
968
+ callback = options;
969
+ options = null;
908
970
  }
909
971
  if (typeof name !== "string") {
910
972
  name = "";
@@ -912,31 +974,53 @@ class ObjectsInRedisClient {
912
974
  if (name.startsWith("/")) {
913
975
  name = name.substring(1);
914
976
  }
915
- this.checkFileRights(id, name, options2, CONSTS.ACCESS_DELETE, async (err, options3, meta) => {
977
+ this.checkFileRights(id, name, options, CONSTS.ACCESS_DELETE, async (err, meta, userContext2) => {
916
978
  if (err) {
917
979
  return import_db_base.tools.maybeCallbackWithError(callback, err);
918
980
  }
919
- if (!options3.acl.file.delete) {
981
+ if (!userContext2.acl.file.delete) {
920
982
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION);
921
983
  }
922
984
  try {
923
- const files = await this._unlink(id, name, options3, meta);
985
+ const files = await this._unlink(id, name, userContext2, meta);
924
986
  return import_db_base.tools.maybeCallbackWithError(callback, null, files);
925
987
  } catch (e) {
926
988
  return import_db_base.tools.maybeCallbackWithError(callback, e);
927
989
  }
928
990
  });
929
991
  }
930
- unlinkAsync(id, name, options2) {
931
- return new Promise((resolve, reject) => this.unlink(id, name, options2, (err) => err ? reject(err) : resolve()));
992
+ /**
993
+ * Promise-version of unlink
994
+ *
995
+ * @param id The id of the object owning the file
996
+ * @param name The file or directory name to delete
997
+ * @param options The current request options including the user
998
+ */
999
+ unlinkAsync(id, name, options) {
1000
+ return new Promise((resolve, reject) => this.unlink(id, name, options, (err) => err ? reject(err) : resolve()));
932
1001
  }
933
- delFile(id, name, options2, callback) {
934
- return this.unlink(id, name, options2, callback);
1002
+ /**
1003
+ * Delete a file of an object (alias for {@link unlink})
1004
+ *
1005
+ * @param id The id of the object owning the file
1006
+ * @param name The file name to delete
1007
+ * @param options The current request options including the user
1008
+ * @param callback Called once the file has been deleted
1009
+ */
1010
+ delFile(id, name, options, callback) {
1011
+ return this.unlink(id, name, options, callback);
935
1012
  }
936
- delFileAsync(id, name, options2) {
937
- return this.unlinkAsync(id, name, options2);
1013
+ /**
1014
+ * Promise-version of delFile
1015
+ *
1016
+ * @param id The id of the object owning the file
1017
+ * @param name The file name to delete
1018
+ * @param options The current request options including the user
1019
+ */
1020
+ delFileAsync(id, name, options) {
1021
+ return this.unlinkAsync(id, name, options);
938
1022
  }
939
- async _readDir(id, name, options2, callback) {
1023
+ async _readDir(id, name, userContext2, callback) {
940
1024
  name = this.normalizeFilename(name);
941
1025
  if (!this.client) {
942
1026
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_DB_CLOSED);
@@ -952,15 +1036,15 @@ class ObjectsInRedisClient {
952
1036
  if (!this.client) {
953
1037
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_DB_CLOSED);
954
1038
  }
955
- const result3 = [];
1039
+ const result2 = [];
956
1040
  if (!keys3 || !keys3.length) {
957
- return import_db_base.tools.maybeCallbackWithError(callback, null, result3);
1041
+ return import_db_base.tools.maybeCallbackWithError(callback, null, result2);
958
1042
  }
959
1043
  let lastDir;
960
1044
  keys3.sort().forEach((dir) => {
961
1045
  dir = dir.substring(this.fileNamespaceL, dir.indexOf("$%$"));
962
1046
  if (dir !== lastDir) {
963
- result3.push({
1047
+ result2.push({
964
1048
  file: dir,
965
1049
  stats: {},
966
1050
  isDir: true
@@ -968,7 +1052,7 @@ class ObjectsInRedisClient {
968
1052
  }
969
1053
  lastDir = dir;
970
1054
  });
971
- return import_db_base.tools.maybeCallbackWithError(callback, null, result3);
1055
+ return import_db_base.tools.maybeCallbackWithError(callback, null, result2);
972
1056
  }
973
1057
  try {
974
1058
  await this.validateMetaObject(id);
@@ -1006,12 +1090,12 @@ class ObjectsInRedisClient {
1006
1090
  }
1007
1091
  });
1008
1092
  if (!keys2.length) {
1009
- const result3 = dirs.map((file) => ({
1093
+ const result2 = dirs.map((file) => ({
1010
1094
  file,
1011
1095
  stats: {},
1012
1096
  isDir: true
1013
1097
  }));
1014
- return import_db_base.tools.maybeCallbackWithError(callback, null, result3);
1098
+ return import_db_base.tools.maybeCallbackWithError(callback, null, result2);
1015
1099
  }
1016
1100
  let strObjs;
1017
1101
  try {
@@ -1019,12 +1103,12 @@ class ObjectsInRedisClient {
1019
1103
  } catch (e) {
1020
1104
  return import_db_base.tools.maybeCallbackWithRedisError(callback, e);
1021
1105
  }
1022
- const result2 = [];
1023
- const dontCheck = options2.user === CONSTS.SYSTEM_ADMIN_USER || options2.group !== CONSTS.SYSTEM_ADMIN_GROUP || options2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP);
1106
+ const result = [];
1107
+ const dontCheck = userContext2.user === CONSTS.SYSTEM_ADMIN_USER || userContext2.group === CONSTS.SYSTEM_ADMIN_GROUP || userContext2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP);
1024
1108
  for (let i = 0; i < keys2.length; i++) {
1025
1109
  const file = keys2[i].substring(start + baseName.length, keys2[i].length - end);
1026
1110
  while (dirs.length && dirs[0] < file) {
1027
- result2.push({
1111
+ result.push({
1028
1112
  file: dirs.shift(),
1029
1113
  stats: {},
1030
1114
  isDir: true
@@ -1038,21 +1122,21 @@ class ObjectsInRedisClient {
1038
1122
  this.log.error(`${this.namespace} Cannot parse JSON ${keys2[i]}: ${strObj}`);
1039
1123
  continue;
1040
1124
  }
1041
- if (dontCheck || utils.checkObject(obj, options2, CONSTS.ACCESS_READ)) {
1125
+ if (dontCheck || utils.checkObject(obj, userContext2, CONSTS.ACCESS_READ)) {
1042
1126
  if (!obj || obj.virtualFile) {
1043
1127
  continue;
1044
1128
  }
1045
- obj.acl = obj.acl || {};
1046
- if (options2.user !== CONSTS.SYSTEM_ADMIN_USER && !options2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP)) {
1129
+ obj.acl ||= {};
1130
+ if (userContext2.user !== CONSTS.SYSTEM_ADMIN_USER && !userContext2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP)) {
1047
1131
  obj.acl.read = !!(obj.acl.permissions & CONSTS.ACCESS_EVERY_READ);
1048
1132
  obj.acl.write = !!(obj.acl.permissions & CONSTS.ACCESS_EVERY_WRITE);
1049
1133
  } else {
1050
1134
  obj.acl.read = true;
1051
1135
  obj.acl.write = true;
1052
1136
  }
1053
- result2.push({
1137
+ result.push({
1054
1138
  file,
1055
- stats: obj.stats,
1139
+ stats: obj.stats || {},
1056
1140
  isDir: false,
1057
1141
  acl: obj.acl,
1058
1142
  modifiedAt: obj.modifiedAt,
@@ -1061,21 +1145,26 @@ class ObjectsInRedisClient {
1061
1145
  }
1062
1146
  }
1063
1147
  while (dirs.length) {
1064
- result2.push({
1148
+ result.push({
1065
1149
  file: dirs.shift(),
1066
1150
  stats: {},
1067
1151
  isDir: true
1068
1152
  });
1069
1153
  }
1070
- return import_db_base.tools.maybeCallbackWithError(callback, null, result2);
1154
+ return import_db_base.tools.maybeCallbackWithError(callback, null, result);
1071
1155
  }
1072
- readDir(id, name, options2, callback) {
1073
- if (typeof options2 === "function") {
1074
- callback = options2;
1075
- options2 = null;
1076
- }
1077
- if (options2?.acl) {
1078
- options2.acl = null;
1156
+ /**
1157
+ * List the contents of a directory of an object
1158
+ *
1159
+ * @param id The id of the object owning the files
1160
+ * @param name The directory name to list
1161
+ * @param options The current request options including the user, or the callback
1162
+ * @param callback Called with the directory entries
1163
+ */
1164
+ readDir(id, name, options, callback) {
1165
+ if (typeof options === "function") {
1166
+ callback = options;
1167
+ options = null;
1079
1168
  }
1080
1169
  if (typeof name !== "string") {
1081
1170
  name = "";
@@ -1086,18 +1175,25 @@ class ObjectsInRedisClient {
1086
1175
  if (name.endsWith("/")) {
1087
1176
  name = name.substring(0, name.length - 1);
1088
1177
  }
1089
- this.checkFileRights(id, name, options2, CONSTS.ACCESS_READ, (err, options3) => {
1178
+ this.checkFileRights(id, name, options, CONSTS.ACCESS_READ, (err, options2, userContext2) => {
1090
1179
  if (err) {
1091
1180
  return import_db_base.tools.maybeCallbackWithError(callback, err);
1092
1181
  }
1093
- if (!options3.acl.file.list) {
1182
+ if (!userContext2.acl.file.list) {
1094
1183
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION);
1095
1184
  }
1096
- this._readDir(id, name, options3, callback);
1185
+ return this._readDir(id, name, userContext2, callback);
1097
1186
  });
1098
1187
  }
1099
- readDirAsync(id, name, options2) {
1100
- return new Promise((resolve, reject) => this.readDir(id, name, options2, (err, res) => err ? reject(err) : resolve(res)));
1188
+ /**
1189
+ * Promise-version of readDir
1190
+ *
1191
+ * @param id The id of the object owning the files
1192
+ * @param name The directory name to list
1193
+ * @param options The current request options including the user
1194
+ */
1195
+ readDirAsync(id, name, options) {
1196
+ return new Promise((resolve, reject) => this.readDir(id, name, options, (err, res) => err ? reject(err) : resolve(res)));
1101
1197
  }
1102
1198
  async _renameHelper(keys2, oldBase, newBase, callback) {
1103
1199
  if (!keys2 || !keys2.length) {
@@ -1122,7 +1218,7 @@ class ObjectsInRedisClient {
1122
1218
  }
1123
1219
  return import_db_base.tools.maybeCallback(callback);
1124
1220
  }
1125
- async _rename(id, oldName, newName, options2, callback, meta) {
1221
+ async _rename(id, oldName, newName, userContext2, callback, meta) {
1126
1222
  const oldMetaID = this.getFileId(id, oldName, true);
1127
1223
  const oldDataID = this.getFileId(id, oldName, false);
1128
1224
  const newMetaID = this.getFileId(id, newName, true);
@@ -1167,10 +1263,10 @@ class ObjectsInRedisClient {
1167
1263
  } catch (e) {
1168
1264
  return import_db_base.tools.maybeCallbackWithRedisError(callback, e);
1169
1265
  }
1170
- let result2;
1171
- const dontCheck = options2.user === CONSTS.SYSTEM_ADMIN_USER || options2.group !== CONSTS.SYSTEM_ADMIN_GROUP || options2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP);
1266
+ let result;
1267
+ const dontCheck = userContext2.user === CONSTS.SYSTEM_ADMIN_USER || userContext2.group === CONSTS.SYSTEM_ADMIN_GROUP || userContext2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP);
1172
1268
  if (!dontCheck) {
1173
- result2 = [];
1269
+ result = [];
1174
1270
  for (let i = 0; i < keys2.length; i++) {
1175
1271
  const strObj = strObjs[i];
1176
1272
  let obj;
@@ -1180,14 +1276,14 @@ class ObjectsInRedisClient {
1180
1276
  this.log.error(`${this.namespace} Cannot parse JSON ${keys2[i]}: ${strObj}`);
1181
1277
  continue;
1182
1278
  }
1183
- if (utils.checkObject(obj, options2, CONSTS.ACCESS_READ)) {
1184
- result2.push(keys2[i]);
1279
+ if (utils.checkObject(obj, userContext2, CONSTS.ACCESS_READ)) {
1280
+ result.push(keys2[i]);
1185
1281
  }
1186
1282
  }
1187
1283
  } else {
1188
- result2 = keys2;
1284
+ result = keys2;
1189
1285
  }
1190
- return this._renameHelper(result2, oldBase, newBase, callback);
1286
+ return this._renameHelper(result, oldBase, newBase, callback);
1191
1287
  }
1192
1288
  try {
1193
1289
  await this.client.rename(oldDataID, newDataID);
@@ -1197,13 +1293,19 @@ class ObjectsInRedisClient {
1197
1293
  return import_db_base.tools.maybeCallbackWithRedisError(callback, e);
1198
1294
  }
1199
1295
  }
1200
- rename(id, oldName, newName, options2, callback) {
1201
- if (typeof options2 === "function") {
1202
- callback = options2;
1203
- options2 = null;
1204
- }
1205
- if (options2?.acl) {
1206
- options2.acl = null;
1296
+ /**
1297
+ * Rename a file or directory of an object
1298
+ *
1299
+ * @param id The id of the object owning the file
1300
+ * @param oldName The current file or directory name
1301
+ * @param newName The new file or directory name
1302
+ * @param options The current request options including the user, or the callback
1303
+ * @param callback Called once the file has been renamed
1304
+ */
1305
+ rename(id, oldName, newName, options, callback) {
1306
+ if (typeof options === "function") {
1307
+ callback = options;
1308
+ options = null;
1207
1309
  }
1208
1310
  if (typeof oldName !== "string" || !oldName.length || oldName === "/" || oldName === "//" || typeof newName !== "string" || !newName.length || newName === "/" || newName === "//") {
1209
1311
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_NOT_FOUND);
@@ -1220,20 +1322,29 @@ class ObjectsInRedisClient {
1220
1322
  if (newName.endsWith("/")) {
1221
1323
  newName = newName.substring(0, newName.length - 1);
1222
1324
  }
1223
- this.checkFileRights(id, oldName, options2, CONSTS.ACCESS_WRITE, (err, options3, meta) => {
1325
+ this.checkFileRights(id, oldName, options, CONSTS.ACCESS_WRITE, (err, meta, userContext2) => {
1224
1326
  if (err) {
1225
1327
  return import_db_base.tools.maybeCallbackWithError(callback, err);
1226
1328
  }
1227
- if (!options3.acl.file.write) {
1329
+ if (!userContext2.acl.file.write) {
1228
1330
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION);
1229
1331
  }
1230
- this._rename(id, oldName, newName, options3, callback, meta);
1332
+ return this._rename(id, oldName, newName, userContext2, callback, meta);
1231
1333
  });
1232
1334
  }
1233
- renameAsync(id, oldName, newName, options2) {
1234
- return new Promise((resolve, reject) => this.rename(id, oldName, newName, options2, (err) => err ? reject(err) : resolve()));
1335
+ /**
1336
+ * Promise-version of rename
1337
+ *
1338
+ * @param id The id of the object owning the file
1339
+ * @param oldName The current file or directory name
1340
+ * @param newName The new file or directory name
1341
+ * @param options The current request options including the user
1342
+ * @param options.user The user on whose behalf the operation is performed
1343
+ */
1344
+ renameAsync(id, oldName, newName, options) {
1345
+ return new Promise((resolve, reject) => this.rename(id, oldName, newName, options, (err) => err ? reject(err) : resolve()));
1235
1346
  }
1236
- async _touch(id, name, callback, meta) {
1347
+ async _touch(id, name, meta, callback) {
1237
1348
  const metaID = this.getFileId(id, name, true);
1238
1349
  if (!this.client) {
1239
1350
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_DB_CLOSED);
@@ -1249,13 +1360,18 @@ class ObjectsInRedisClient {
1249
1360
  return import_db_base.tools.maybeCallbackWithRedisError(callback, e);
1250
1361
  }
1251
1362
  }
1252
- touch(id, name, options2, callback) {
1253
- if (typeof options2 === "function") {
1254
- callback = options2;
1255
- options2 = null;
1256
- }
1257
- if (options2?.acl) {
1258
- options2.acl = null;
1363
+ /**
1364
+ * Update the modification time of a file
1365
+ *
1366
+ * @param id The id of the object owning the file
1367
+ * @param name The file name
1368
+ * @param options The current request options including the user, or the callback
1369
+ * @param callback Called once the file has been touched
1370
+ */
1371
+ touch(id, name, options, callback) {
1372
+ if (typeof options === "function") {
1373
+ callback = options;
1374
+ options = null;
1259
1375
  }
1260
1376
  if (typeof name !== "string" || !name.length || name === "/") {
1261
1377
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_NOT_FOUND);
@@ -1263,15 +1379,23 @@ class ObjectsInRedisClient {
1263
1379
  if (name.startsWith("/")) {
1264
1380
  name = name.substring(1);
1265
1381
  }
1266
- this.checkFileRights(id, name, options2, CONSTS.ACCESS_WRITE, (err, options3, meta) => {
1382
+ this.checkFileRights(id, name, options, CONSTS.ACCESS_WRITE, (err, meta) => {
1267
1383
  if (err) {
1268
1384
  return import_db_base.tools.maybeCallbackWithError(callback, err);
1269
1385
  }
1270
- return this._touch(id, name, callback, meta);
1386
+ return this._touch(id, name, meta, callback);
1271
1387
  });
1272
1388
  }
1273
- touchAsync(id, name, options2) {
1274
- return new Promise((resolve, reject) => this.touch(id, name, options2, (err) => err ? reject(err) : resolve()));
1389
+ /**
1390
+ * Promise-version of touch
1391
+ *
1392
+ * @param id The id of the object owning the file
1393
+ * @param name The file name
1394
+ * @param options The current request options including the user
1395
+ * @param options.user The user on whose behalf the operation is performed
1396
+ */
1397
+ touchAsync(id, name, options) {
1398
+ return new Promise((resolve, reject) => this.touch(id, name, options, (err) => err ? reject(err) : resolve()));
1275
1399
  }
1276
1400
  async _rmHelper(keys2) {
1277
1401
  if (!keys2.length) {
@@ -1285,7 +1409,7 @@ class ObjectsInRedisClient {
1285
1409
  await this.client.del(id);
1286
1410
  }
1287
1411
  }
1288
- async _rm(id, name, options2, meta) {
1412
+ async _rm(id, name, userContext2, meta) {
1289
1413
  if (meta && !meta.isDir) {
1290
1414
  const metaID = this.getFileId(id, name, true);
1291
1415
  const dataID = this.getFileId(id, name, false);
@@ -1313,31 +1437,31 @@ class ObjectsInRedisClient {
1313
1437
  if (!keys2.length) {
1314
1438
  throw new Error(ERRORS.ERROR_NOT_FOUND);
1315
1439
  }
1316
- let objs2;
1440
+ let strObjs;
1317
1441
  try {
1318
- objs2 = await this.client.mget(keys2);
1442
+ strObjs = await this.client.mget(keys2);
1319
1443
  } catch {
1320
1444
  }
1321
- let result2;
1322
- const dontCheck = options2.user === CONSTS.SYSTEM_ADMIN_USER || options2.group !== CONSTS.SYSTEM_ADMIN_GROUP || options2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP);
1323
- objs2 = objs2 || [];
1445
+ let result;
1446
+ const dontCheck = userContext2.user === CONSTS.SYSTEM_ADMIN_USER || userContext2.group === CONSTS.SYSTEM_ADMIN_GROUP || userContext2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP);
1447
+ strObjs ||= [];
1324
1448
  if (!dontCheck) {
1325
- result2 = [];
1449
+ result = [];
1326
1450
  for (let i = 0; i < keys2.length; i++) {
1451
+ const strObj = strObjs[i];
1327
1452
  try {
1328
- const strObj = objs2[i];
1329
1453
  const obj = strObj ? JSON.parse(strObj) : null;
1330
- if (utils.checkObject(obj, options2, CONSTS.ACCESS_READ)) {
1331
- result2.push(keys2[i]);
1454
+ if (utils.checkObject(obj, userContext2, CONSTS.ACCESS_READ)) {
1455
+ result.push(keys2[i]);
1332
1456
  }
1333
1457
  } catch {
1334
- this.log.error(`${this.namespace} Cannot parse JSON ${keys2[i]}: ${objs2[i]}`);
1458
+ this.log.error(`${this.namespace} Cannot parse JSON ${keys2[i]}: ${strObj}`);
1335
1459
  }
1336
1460
  }
1337
1461
  } else {
1338
- result2 = keys2;
1462
+ result = keys2;
1339
1463
  }
1340
- const files = result2.map((key) => {
1464
+ const files = result.map((key) => {
1341
1465
  const name2 = key.substring(this.fileNamespaceL + id.length + 3, key.length - 7);
1342
1466
  const pos = name2.lastIndexOf("/");
1343
1467
  if (pos !== -1) {
@@ -1346,47 +1470,68 @@ class ObjectsInRedisClient {
1346
1470
  return { file: id, path: "" };
1347
1471
  });
1348
1472
  try {
1349
- await this._rmHelper(result2);
1473
+ await this._rmHelper(result);
1350
1474
  } catch (e) {
1351
1475
  this.log.error(`${this.namespace} Could not remove files: ${e.message}`);
1352
1476
  }
1353
1477
  return files;
1354
1478
  }
1355
1479
  }
1356
- rm(id, name, options2, callback) {
1357
- if (typeof options2 === "function") {
1358
- callback = options2;
1359
- options2 = null;
1360
- }
1361
- if (options2?.acl) {
1362
- options2.acl = null;
1480
+ /**
1481
+ * Delete a file or directory of an object
1482
+ *
1483
+ * @param id The id of the object owning the file
1484
+ * @param name The file or directory name to delete
1485
+ * @param options The current request options including the user, or the callback
1486
+ * @param callback Called with the list of removed files
1487
+ */
1488
+ rm(id, name, options, callback) {
1489
+ if (typeof options === "function") {
1490
+ callback = options;
1491
+ options = null;
1363
1492
  }
1364
1493
  if (typeof name !== "string") {
1365
1494
  name = "";
1366
1495
  }
1367
- this.checkFileRights(id, null, options2, CONSTS.ACCESS_DELETE, async (err, options3, meta) => {
1496
+ this.checkFileRights(id, null, options, CONSTS.ACCESS_DELETE, async (err, meta, userContext2) => {
1368
1497
  if (err) {
1369
1498
  return import_db_base.tools.maybeCallbackWithError(callback, err);
1370
1499
  }
1371
- if (!options3.acl.file.delete) {
1500
+ if (!userContext2.acl.file.delete) {
1372
1501
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION);
1373
1502
  }
1374
1503
  try {
1375
- const files = await this._rm(id, name, options3, meta?.notExists ? null : meta);
1504
+ const files = await this._rm(id, name, userContext2, meta?.notExists ? null : meta);
1376
1505
  return import_db_base.tools.maybeCallbackWithError(callback, null, files);
1377
1506
  } catch (e) {
1378
1507
  return import_db_base.tools.maybeCallbackWithError(callback, e);
1379
1508
  }
1380
1509
  });
1381
1510
  }
1382
- rmAsync(id, name, options2) {
1383
- return new Promise((resolve, reject) => this.rm(id, name, options2, (err, files) => err ? reject(err) : resolve(files)));
1511
+ /**
1512
+ * Promise-version of rm
1513
+ *
1514
+ * @param id The id of the object owning the file
1515
+ * @param name The file or directory name to delete
1516
+ * @param options The current request options including the user
1517
+ * @param options.user The user on whose behalf the operation is performed
1518
+ */
1519
+ rmAsync(id, name, options) {
1520
+ return new Promise((resolve, reject) => this.rm(id, name, options, (err, files) => err ? reject(err) : resolve(files)));
1384
1521
  }
1385
1522
  // simulate. redis has no dirs
1386
- mkdir(id, dirName, options2, callback) {
1387
- if (typeof options2 === "function") {
1388
- callback = options2;
1389
- options2 = null;
1523
+ /**
1524
+ * Create a directory for an object's files (simulated, as redis has no real directories)
1525
+ *
1526
+ * @param id The id of the object owning the files
1527
+ * @param dirName The directory name to create
1528
+ * @param options The current request options including the user, or the callback
1529
+ * @param callback Called once the directory has been created
1530
+ */
1531
+ mkdir(id, dirName, options, callback) {
1532
+ if (typeof options === "function") {
1533
+ callback = options;
1534
+ options = null;
1390
1535
  }
1391
1536
  if (typeof dirName !== "string") {
1392
1537
  dirName = "";
@@ -1395,22 +1540,29 @@ class ObjectsInRedisClient {
1395
1540
  if (dirName.startsWith("/")) {
1396
1541
  dirName = dirName.substring(1);
1397
1542
  }
1398
- this.checkFileRights(id, dirName, options2, CONSTS.ACCESS_WRITE, (err, options3) => {
1543
+ this.checkFileRights(id, dirName, options, CONSTS.ACCESS_WRITE, (err, meta, userContext2) => {
1399
1544
  if (err) {
1400
1545
  return import_db_base.tools.maybeCallbackWithError(callback, err);
1401
1546
  }
1402
- if (!options3.acl.file.write) {
1547
+ if (!userContext2.acl.file.write) {
1403
1548
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION);
1404
1549
  }
1405
- options3 = { ...options3, virtualFile: true };
1550
+ meta = { ...options, virtualFile: true };
1406
1551
  const realName = dirName + (dirName.endsWith("/") ? "" : "/");
1407
- this.writeFile(id, `${realName}_data.json`, "", options3, callback);
1552
+ return this.writeFile(id, `${realName}_data.json`, "", meta, callback);
1408
1553
  });
1409
1554
  }
1410
- mkdirAsync(id, dirName, options2) {
1411
- return new Promise((resolve, reject) => this.mkdir(id, dirName, options2, (err) => err ? reject(err) : resolve()));
1555
+ /**
1556
+ * Promise-version of mkdir
1557
+ *
1558
+ * @param id The id of the object owning the files
1559
+ * @param dirName The directory name to create
1560
+ * @param options The current request options including the user
1561
+ */
1562
+ mkdirAsync(id, dirName, options) {
1563
+ return new Promise((resolve, reject) => this.mkdir(id, dirName, options, (err) => err ? reject(err) : resolve()));
1412
1564
  }
1413
- async _chownFileHelper(keys2, metas, options2, callback) {
1565
+ async _chownFileHelper(keys2, metas, options, callback) {
1414
1566
  if (!keys2.length) {
1415
1567
  return import_db_base.tools.maybeCallback(callback);
1416
1568
  }
@@ -1419,8 +1571,8 @@ class ObjectsInRedisClient {
1419
1571
  }
1420
1572
  for (const id of keys2) {
1421
1573
  const meta = metas.shift();
1422
- meta.acl.owner = options2.owner;
1423
- meta.acl.ownerGroup = options2.ownerGroup;
1574
+ meta.acl.owner = options.owner;
1575
+ meta.acl.ownerGroup = options.ownerGroup;
1424
1576
  try {
1425
1577
  await this.client.set(id, JSON.stringify(meta));
1426
1578
  } catch (e) {
@@ -1429,7 +1581,7 @@ class ObjectsInRedisClient {
1429
1581
  }
1430
1582
  return import_db_base.tools.maybeCallback(callback);
1431
1583
  }
1432
- async _chownFile(id, name, options2, callback, meta) {
1584
+ async _chownFile(id, name, options, meta, userContext2, callback) {
1433
1585
  if (!meta) {
1434
1586
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_NOT_FOUND);
1435
1587
  }
@@ -1439,8 +1591,8 @@ class ObjectsInRedisClient {
1439
1591
  }
1440
1592
  if (!meta.isDir && !meta.notExists) {
1441
1593
  const metaID = this.getFileId(id, name, true);
1442
- meta.acl.owner = options2.owner;
1443
- meta.acl.ownerGroup = options2.ownerGroup;
1594
+ meta.acl.owner = options.owner;
1595
+ meta.acl.ownerGroup = options.ownerGroup;
1444
1596
  try {
1445
1597
  await this.client.set(metaID, JSON.stringify(meta));
1446
1598
  } catch (e) {
@@ -1486,7 +1638,7 @@ class ObjectsInRedisClient {
1486
1638
  } catch (e) {
1487
1639
  return import_db_base.tools.maybeCallbackWithRedisError(callback, e);
1488
1640
  }
1489
- const dontCheck = options2.user === CONSTS.SYSTEM_ADMIN_USER || options2.group !== CONSTS.SYSTEM_ADMIN_GROUP || options2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP);
1641
+ const dontCheck = userContext2.user === CONSTS.SYSTEM_ADMIN_USER || userContext2.group === CONSTS.SYSTEM_ADMIN_GROUP || userContext2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP);
1490
1642
  const keysFiltered = [];
1491
1643
  const objsFiltered = [];
1492
1644
  const processed = [];
@@ -1501,7 +1653,7 @@ class ObjectsInRedisClient {
1501
1653
  this.log.error(`${this.namespace} Cannot parse JSON ${keys2[i]}: ${metaStr}`);
1502
1654
  continue;
1503
1655
  }
1504
- if (dontCheck || utils.checkObject(meta2, options2, CONSTS.ACCESS_WRITE)) {
1656
+ if (dontCheck || utils.checkObject(meta2, userContext2, CONSTS.ACCESS_WRITE)) {
1505
1657
  if (!meta2 || meta2.virtualFile) {
1506
1658
  continue;
1507
1659
  }
@@ -1521,76 +1673,92 @@ class ObjectsInRedisClient {
1521
1673
  });
1522
1674
  }
1523
1675
  }
1524
- this._chownFileHelper(keysFiltered, objsFiltered, options2, (err) => {
1676
+ return this._chownFileHelper(keysFiltered, objsFiltered, options, (err) => {
1525
1677
  return import_db_base.tools.maybeCallbackWithError(callback, err, processed);
1526
1678
  });
1527
1679
  }
1528
- chownFile(id, name, options2, callback) {
1529
- if (typeof options2 === "function") {
1530
- callback = options2;
1531
- options2 = {};
1532
- }
1533
- options2 = options2 || {};
1534
- if (typeof options2 !== "object") {
1535
- options2 = { owner: options2 };
1536
- }
1680
+ /**
1681
+ * Change the owner and owner group of a file
1682
+ *
1683
+ * @param id The id of the object owning the file
1684
+ * @param name The file name
1685
+ * @param options The current request options including the new owner and the user
1686
+ * @param options.owner The new owner (user id) to assign to the file
1687
+ * @param options.user The user on whose behalf the operation is performed
1688
+ * @param options.ownerGroup The new owner group to assign to the file
1689
+ * @param options.group The new owner group to assign (alias for ownerGroup)
1690
+ * @param callback Called with the processed file(s)
1691
+ */
1692
+ chownFile(id, name, options, callback) {
1537
1693
  if (typeof name !== "string" || !name.length || name === "/") {
1538
1694
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_NOT_FOUND);
1539
1695
  }
1540
1696
  if (name.startsWith("/")) {
1541
1697
  name = name.substring(1);
1542
1698
  }
1543
- if (!options2.ownerGroup && options2.group) {
1544
- options2.ownerGroup = options2.group;
1699
+ if (options?.group && !options.ownerGroup) {
1700
+ options.ownerGroup = options.group;
1545
1701
  }
1546
- if (!options2.owner && options2.user) {
1547
- options2.owner = options2.user;
1702
+ if (options?.user && !options.owner) {
1703
+ options.owner = options.user;
1548
1704
  }
1549
- if (!options2.owner) {
1705
+ if (!options?.owner) {
1550
1706
  this.log.error(`${this.namespace} user is not defined`);
1551
1707
  return import_db_base.tools.maybeCallbackWithError(callback, "invalid parameter");
1552
1708
  }
1553
- if (!options2.ownerGroup) {
1554
- this.getUserGroup(options2.owner, (user, groups) => {
1555
- if (!groups || !groups[0]) {
1556
- return import_db_base.tools.maybeCallbackWithError(callback, `user "${options2.owner}" belongs to no group`);
1709
+ if (!options.ownerGroup) {
1710
+ this.getUserGroup(options.owner, (user, groups) => {
1711
+ if (!groups?.[0]) {
1712
+ return import_db_base.tools.maybeCallbackWithError(callback, `user "${options.owner}" belongs to no group`);
1557
1713
  }
1558
- options2.ownerGroup = groups[0];
1559
- this.chownFile(id, name, options2, callback);
1714
+ options.ownerGroup = groups[0];
1715
+ this.chownFile(id, name, options, callback);
1560
1716
  });
1561
1717
  return;
1562
1718
  }
1563
- this.checkFileRights(id, name, options2, CONSTS.ACCESS_WRITE, (err, options3, meta) => {
1719
+ this.checkFileRights(id, name, options, CONSTS.ACCESS_WRITE, (err, meta, userContext2) => {
1564
1720
  if (err) {
1565
1721
  return import_db_base.tools.maybeCallbackWithError(callback, err);
1566
1722
  }
1567
- if (!options3.acl.file.write) {
1723
+ if (!userContext2.acl.file.write) {
1568
1724
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION);
1569
1725
  }
1570
- return this._chownFile(id, name, options3, callback, meta);
1726
+ return this._chownFile(id, name, options, meta, userContext2, callback);
1571
1727
  });
1572
1728
  }
1573
- chownFileAsync(id, name, options2) {
1574
- return new Promise((resolve, reject) => this.chownFile(id, name, options2, (err, processed) => err ? reject(err) : resolve(processed)));
1729
+ /**
1730
+ * Promise-version of chownFile
1731
+ *
1732
+ * @param id The id of the object owning the file
1733
+ * @param name The file name
1734
+ * @param options The current request options including the new owner and the user
1735
+ * @param options.user The user on whose behalf the operation is performed
1736
+ * @param options.owner The new owner (user id) to assign to the file
1737
+ * @param options.ownerGroup The new owner group to assign to the file
1738
+ */
1739
+ chownFileAsync(id, name, options) {
1740
+ return new Promise((resolve, reject) => this.chownFile(id, name, options, (err, processed) => err ? reject(err) : resolve(processed)));
1575
1741
  }
1576
1742
  /**
1577
1743
  *
1578
1744
  * @param keys Key names to handle
1579
1745
  * @param metas Objects for the keys to handle
1580
1746
  * @param options options
1747
+ * @param options.user The user on whose behalf the operation is performed
1748
+ * @param options.mode The new permission mode to apply
1581
1749
  * @param callback callback function
1582
1750
  */
1583
- async _chmodFileHelper(keys2, metas, options2, callback) {
1751
+ async _chmodFileHelper(keys2, metas, options, callback) {
1584
1752
  if (!keys2 || !keys2.length) {
1585
1753
  return import_db_base.tools.maybeCallback(callback);
1586
1754
  }
1587
1755
  if (!this.client) {
1588
1756
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_DB_CLOSED);
1589
1757
  }
1590
- for (const i in keys2) {
1758
+ for (let i = 0; i < keys2.length; i++) {
1591
1759
  const id = keys2[i];
1592
1760
  const meta = metas[i];
1593
- meta.acl.permissions = options2.mode;
1761
+ meta.acl.permissions = options.mode;
1594
1762
  try {
1595
1763
  await this.client.set(id, JSON.stringify(meta));
1596
1764
  } catch (e) {
@@ -1599,7 +1767,7 @@ class ObjectsInRedisClient {
1599
1767
  }
1600
1768
  return import_db_base.tools.maybeCallback(callback);
1601
1769
  }
1602
- async _chmodFile(id, name, options2, callback, meta) {
1770
+ async _chmodFile(id, name, options, meta, userContext2, callback) {
1603
1771
  if (!meta) {
1604
1772
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_NOT_FOUND);
1605
1773
  }
@@ -1609,7 +1777,7 @@ class ObjectsInRedisClient {
1609
1777
  }
1610
1778
  if (!meta.isDir && !meta.notExists) {
1611
1779
  const metaID = this.getFileId(id, name, true);
1612
- meta.acl.permissions = options2.mode;
1780
+ meta.acl.permissions = options.mode;
1613
1781
  try {
1614
1782
  await this.client.set(metaID, JSON.stringify(meta));
1615
1783
  } catch (e) {
@@ -1655,7 +1823,7 @@ class ObjectsInRedisClient {
1655
1823
  } catch (e) {
1656
1824
  return import_db_base.tools.maybeCallbackWithRedisError(callback, e);
1657
1825
  }
1658
- const dontCheck = options2.user === CONSTS.SYSTEM_ADMIN_USER || options2.group !== CONSTS.SYSTEM_ADMIN_GROUP || options2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP);
1826
+ const dontCheck = userContext2.user === CONSTS.SYSTEM_ADMIN_USER || userContext2.group === CONSTS.SYSTEM_ADMIN_GROUP || userContext2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP);
1659
1827
  const keysFiltered = [];
1660
1828
  const objsFiltered = [];
1661
1829
  const processed = [];
@@ -1670,7 +1838,7 @@ class ObjectsInRedisClient {
1670
1838
  this.log.error(`${this.namespace} Cannot parse JSON ${keys2[i]}: ${strObj}`);
1671
1839
  continue;
1672
1840
  }
1673
- if (dontCheck || utils.checkObject(obj, options2, CONSTS.ACCESS_WRITE)) {
1841
+ if (dontCheck || utils.checkObject(obj, userContext2, CONSTS.ACCESS_WRITE)) {
1674
1842
  if (!obj || obj.virtualFile) {
1675
1843
  continue;
1676
1844
  }
@@ -1690,59 +1858,86 @@ class ObjectsInRedisClient {
1690
1858
  });
1691
1859
  }
1692
1860
  }
1693
- this._chmodFileHelper(keysFiltered, objsFiltered, options2, (err) => import_db_base.tools.maybeCallbackWithError(callback, err, processed));
1861
+ return this._chmodFileHelper(keysFiltered, objsFiltered, options, (err) => import_db_base.tools.maybeCallbackWithError(callback, err, processed));
1694
1862
  }
1695
- chmodFile(id, name, options2, callback) {
1696
- if (typeof options2 === "function") {
1697
- callback = options2;
1698
- options2 = null;
1863
+ /**
1864
+ * Change the file mode (permissions) of a single file
1865
+ *
1866
+ * @param id The id of the object owning the file
1867
+ * @param name The file name
1868
+ * @param options The current request options including the new mode and the user, or the callback
1869
+ * @param options.user The user on whose behalf the operation is performed
1870
+ * @param options.mode The new permission mode to apply
1871
+ * @param callback Called with the processed file
1872
+ */
1873
+ chmodFile(id, name, options, callback) {
1874
+ if (!options || options.mode === void 0) {
1875
+ throw new Error("options.mode is not defined");
1699
1876
  }
1700
- options2 = options2 || {};
1701
1877
  if (typeof name !== "string" || !name.length || name === "/") {
1702
1878
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_NOT_FOUND);
1703
1879
  }
1704
1880
  if (name[0].startsWith("/")) {
1705
1881
  name = name.substring(1);
1706
1882
  }
1707
- if (typeof options2 !== "object") {
1708
- options2 = { mode: options2 };
1883
+ if (typeof options !== "object") {
1884
+ options = { mode: options };
1709
1885
  }
1710
- if (options2.mode === void 0) {
1886
+ if (options.mode === void 0) {
1711
1887
  this.log.error(`${this.namespace} mode is not defined`);
1712
1888
  return import_db_base.tools.maybeCallbackWithError(callback, "invalid parameter");
1713
- } else if (typeof options2.mode === "string") {
1714
- options2.mode = parseInt(options2.mode, 16);
1889
+ } else if (typeof options.mode === "string") {
1890
+ options.mode = parseInt(options.mode, 16);
1715
1891
  }
1716
- this.checkFileRights(id, name, options2, CONSTS.ACCESS_WRITE, (err, options3, meta) => {
1892
+ this.checkFileRights(id, name, options, CONSTS.ACCESS_WRITE, (err, meta, userContext2) => {
1717
1893
  if (err) {
1718
1894
  return import_db_base.tools.maybeCallbackWithError(callback, err);
1719
1895
  }
1720
- if (!options3.acl.file.write) {
1896
+ if (!userContext2.acl.file.write) {
1721
1897
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION);
1722
1898
  }
1723
- return this._chmodFile(id, name, options3, callback, meta);
1899
+ return this._chmodFile(id, name, options, meta, userContext2, callback);
1724
1900
  });
1725
1901
  }
1726
- chmodFileAsync(id, name, options2) {
1727
- return new Promise((resolve, reject) => this.chmodFile(id, name, options2, (err, processed) => err ? reject(err) : resolve(processed)));
1902
+ /**
1903
+ * Promise-version of chmodFile
1904
+ *
1905
+ * @param id The id of the object owning the file
1906
+ * @param name The file name
1907
+ * @param options The current request options including the new mode and the user
1908
+ * @param options.user The user on whose behalf the operation is performed
1909
+ * @param options.mode The new permission mode to apply
1910
+ */
1911
+ chmodFileAsync(id, name, options) {
1912
+ return new Promise((resolve, reject) => this.chmodFile(id, name, options, (err, processed) => err ? reject(err) : resolve(processed)));
1728
1913
  }
1729
- enableFileCache(enabled, options2, callback) {
1730
- if (typeof options2 === "function") {
1731
- callback = options2;
1732
- options2 = null;
1733
- }
1734
- if (options2?.acl) {
1735
- options2.acl = null;
1914
+ /**
1915
+ * Enable or disable the file cache
1916
+ *
1917
+ * @param enabled Whether the file cache should be enabled
1918
+ * @param options The current request options including the user, or the callback
1919
+ * @param callback Called with the resulting cache state
1920
+ */
1921
+ enableFileCache(enabled, options, callback) {
1922
+ if (typeof options === "function") {
1923
+ callback = options;
1924
+ options = null;
1736
1925
  }
1737
- utils.checkObjectRights(this, null, null, options2, CONSTS.ACCESS_WRITE, (err, _options) => {
1926
+ utils.checkObjectRights(this, null, null, options, CONSTS.ACCESS_WRITE, (err) => {
1738
1927
  if (err) {
1739
1928
  return import_db_base.tools.maybeCallbackWithError(callback, err);
1740
1929
  }
1741
1930
  return import_db_base.tools.maybeCallbackWithError(callback, null, false);
1742
1931
  });
1743
1932
  }
1744
- enableFileCacheAsync(enabled, options2) {
1745
- return new Promise((resolve, reject) => this.enableFileCache(enabled, options2, (err, res) => err ? reject(err) : resolve(res)));
1933
+ /**
1934
+ * Promise-version of enableFileCache
1935
+ *
1936
+ * @param enabled Whether the file cache should be enabled
1937
+ * @param options The current request options including the user
1938
+ */
1939
+ enableFileCacheAsync(enabled, options) {
1940
+ return new Promise((resolve, reject) => this.enableFileCache(enabled, options, (err, res) => err ? reject(err) : resolve(res)));
1746
1941
  }
1747
1942
  async _subscribeFile(id, pattern) {
1748
1943
  if (!this.sub) {
@@ -1788,9 +1983,16 @@ class ObjectsInRedisClient {
1788
1983
  }
1789
1984
  }
1790
1985
  }
1791
- subscribeUserFile(id, pattern, options2) {
1986
+ /**
1987
+ * Subscribe a user to file changes of an object
1988
+ *
1989
+ * @param id The id of the object owning the files
1990
+ * @param pattern One or more file name patterns to subscribe to
1991
+ * @param options The current request options including the user
1992
+ */
1993
+ subscribeUserFile(id, pattern, options) {
1792
1994
  return new Promise((resolve, reject) => {
1793
- utils.checkObjectRights(this, null, null, options2, "list", (err) => {
1995
+ utils.checkObjectRights(this, null, null, options, "list", (err) => {
1794
1996
  if (err) {
1795
1997
  reject(err);
1796
1998
  } else {
@@ -1799,9 +2001,16 @@ class ObjectsInRedisClient {
1799
2001
  });
1800
2002
  });
1801
2003
  }
1802
- unsubscribeUserFile(id, pattern, options2) {
2004
+ /**
2005
+ * Unsubscribe a user from file changes of an object
2006
+ *
2007
+ * @param id The id of the object owning the files
2008
+ * @param pattern One or more file name patterns to unsubscribe from
2009
+ * @param options The current request options including the user
2010
+ */
2011
+ unsubscribeUserFile(id, pattern, options) {
1803
2012
  return new Promise((resolve, reject) => {
1804
- utils.checkObjectRights(this, null, null, options2, "list", (err) => {
2013
+ utils.checkObjectRights(this, null, null, options, "list", (err) => {
1805
2014
  if (err) {
1806
2015
  reject(err);
1807
2016
  } else {
@@ -1840,38 +2049,64 @@ class ObjectsInRedisClient {
1840
2049
  });
1841
2050
  }
1842
2051
  }
1843
- subscribeConfig(pattern, options2, callback) {
1844
- utils.checkObjectRights(this, null, null, options2, "list", (err) => {
2052
+ subscribeConfig(pattern, options, callback) {
2053
+ utils.checkObjectRights(this, null, null, options, "list", (err) => {
1845
2054
  if (err) {
1846
2055
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
1847
2056
  }
1848
2057
  return this._subscribe(pattern, false, callback);
1849
2058
  });
1850
2059
  }
1851
- subscribe(pattern, options2, callback) {
1852
- if (typeof options2 === "function") {
1853
- callback = options2;
1854
- options2 = null;
2060
+ /**
2061
+ * Subscribe to object changes matching the given pattern
2062
+ *
2063
+ * @param pattern One or more patterns to subscribe to
2064
+ * @param options The current request options including the user, or the callback
2065
+ * @param callback Called once the subscription is registered
2066
+ */
2067
+ subscribe(pattern, options, callback) {
2068
+ if (typeof options === "function") {
2069
+ callback = options;
2070
+ options = null;
1855
2071
  }
1856
- return this.subscribeConfig(pattern, options2, callback);
2072
+ return this.subscribeConfig(pattern, options, callback);
1857
2073
  }
1858
- subscribeAsync(pattern, options2) {
1859
- return new Promise((resolve, reject) => this.subscribe(pattern, options2, (err) => err ? reject(err) : resolve()));
2074
+ /**
2075
+ * Promise-version of subscribe
2076
+ *
2077
+ * @param pattern One or more patterns to subscribe to
2078
+ * @param options The current request options including the user
2079
+ */
2080
+ subscribeAsync(pattern, options) {
2081
+ return new Promise((resolve, reject) => this.subscribe(pattern, options, (err) => err ? reject(err) : resolve()));
1860
2082
  }
1861
- subscribeUser(pattern, options2, callback) {
1862
- if (typeof options2 === "function") {
1863
- callback = options2;
1864
- options2 = null;
2083
+ /**
2084
+ * Subscribe a user to object changes matching the given pattern
2085
+ *
2086
+ * @param pattern One or more patterns to subscribe to
2087
+ * @param options The current request options including the user, or the callback
2088
+ * @param callback Called once the subscription is registered
2089
+ */
2090
+ subscribeUser(pattern, options, callback) {
2091
+ if (typeof options === "function") {
2092
+ callback = options;
2093
+ options = null;
1865
2094
  }
1866
- utils.checkObjectRights(this, null, null, options2, "list", (err) => {
2095
+ utils.checkObjectRights(this, null, null, options, "list", (err) => {
1867
2096
  if (err) {
1868
2097
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
1869
2098
  }
1870
2099
  return this._subscribe(pattern, true, callback);
1871
2100
  });
1872
2101
  }
1873
- subscribeUserAsync(pattern, options2) {
1874
- return new Promise((resolve, reject) => this.subscribeUser(pattern, options2, (err) => err ? reject(err) : resolve()));
2102
+ /**
2103
+ * Promise-version of subscribeUser
2104
+ *
2105
+ * @param pattern One or more patterns to subscribe to
2106
+ * @param options The current request options including the user
2107
+ */
2108
+ subscribeUserAsync(pattern, options) {
2109
+ return new Promise((resolve, reject) => this.subscribeUser(pattern, options, (err) => err ? reject(err) : resolve()));
1875
2110
  }
1876
2111
  async _unsubscribe(pattern, asUser) {
1877
2112
  const subClient = asUser ? this.sub : this.subSystem;
@@ -1896,8 +2131,8 @@ class ObjectsInRedisClient {
1896
2131
  }
1897
2132
  }
1898
2133
  }
1899
- unsubscribeConfig(pattern, options2, callback) {
1900
- utils.checkObjectRights(this, null, null, options2, "list", async (err) => {
2134
+ unsubscribeConfig(pattern, options, callback) {
2135
+ utils.checkObjectRights(this, null, null, options, "list", async (err) => {
1901
2136
  if (err) {
1902
2137
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
1903
2138
  }
@@ -1909,22 +2144,42 @@ class ObjectsInRedisClient {
1909
2144
  }
1910
2145
  });
1911
2146
  }
1912
- unsubscribe(pattern, options2, callback) {
1913
- if (typeof options2 === "function") {
1914
- callback = options2;
1915
- options2 = null;
2147
+ /**
2148
+ * Unsubscribe from object changes matching the given pattern
2149
+ *
2150
+ * @param pattern One or more patterns to unsubscribe from
2151
+ * @param options The current request options including the user, or the callback
2152
+ * @param callback Called once the subscription is removed
2153
+ */
2154
+ unsubscribe(pattern, options, callback) {
2155
+ if (typeof options === "function") {
2156
+ callback = options;
2157
+ options = null;
1916
2158
  }
1917
- return this.unsubscribeConfig(pattern, options2, callback);
2159
+ return this.unsubscribeConfig(pattern, options, callback);
1918
2160
  }
1919
- unsubscribeAsync(pattern, options2) {
1920
- return new Promise((resolve, reject) => this.unsubscribe(pattern, options2, (err) => err ? reject(err) : resolve()));
2161
+ /**
2162
+ * Promise-version of unsubscribe
2163
+ *
2164
+ * @param pattern One or more patterns to unsubscribe from
2165
+ * @param options The current request options including the user
2166
+ */
2167
+ unsubscribeAsync(pattern, options) {
2168
+ return new Promise((resolve, reject) => this.unsubscribe(pattern, options, (err) => err ? reject(err) : resolve()));
1921
2169
  }
1922
- unsubscribeUser(pattern, options2, callback) {
1923
- if (typeof options2 === "function") {
1924
- callback = options2;
1925
- options2 = null;
2170
+ /**
2171
+ * Unsubscribe a user from object changes matching the given pattern
2172
+ *
2173
+ * @param pattern One or more patterns to unsubscribe from
2174
+ * @param options The current request options including the user, or the callback
2175
+ * @param callback Called once the subscription is removed
2176
+ */
2177
+ unsubscribeUser(pattern, options, callback) {
2178
+ if (typeof options === "function") {
2179
+ callback = options;
2180
+ options = null;
1926
2181
  }
1927
- utils.checkObjectRights(this, null, null, options2, "list", async (err) => {
2182
+ utils.checkObjectRights(this, null, null, options, "list", async (err) => {
1928
2183
  if (err) {
1929
2184
  return import_db_base.tools.maybeCallbackWithError(callback, err);
1930
2185
  }
@@ -1936,8 +2191,14 @@ class ObjectsInRedisClient {
1936
2191
  }
1937
2192
  });
1938
2193
  }
1939
- unsubscribeUserAsync(pattern, options2) {
1940
- return new Promise((resolve, reject) => this.unsubscribeUser(pattern, options2, (err) => err ? reject(err) : resolve()));
2194
+ /**
2195
+ * Promise-version of unsubscribeUser
2196
+ *
2197
+ * @param pattern One or more patterns to unsubscribe from
2198
+ * @param options The current request options including the user
2199
+ */
2200
+ unsubscribeUserAsync(pattern, options) {
2201
+ return new Promise((resolve, reject) => this.unsubscribeUser(pattern, options, (err) => err ? reject(err) : resolve()));
1941
2202
  }
1942
2203
  async _objectHelper(keys2, objs2) {
1943
2204
  if (!keys2.length) {
@@ -1967,8 +2228,8 @@ class ObjectsInRedisClient {
1967
2228
  await this.client.publish(id, message);
1968
2229
  }
1969
2230
  }
1970
- _chownObject(pattern, options2, callback) {
1971
- this.getKeys(pattern, options2, async (err, keys2) => {
2231
+ _chownObject(pattern, options, userContext2, callback) {
2232
+ this.getKeys(pattern, options, async (err, keys2) => {
1972
2233
  if (err) {
1973
2234
  return import_db_base.tools.maybeCallbackWithError(callback, err);
1974
2235
  }
@@ -1995,7 +2256,7 @@ class ObjectsInRedisClient {
1995
2256
  this.log.error(`${this.namespace} Cannot parse JSON ${keys2[i]}: ${strObj}`);
1996
2257
  continue;
1997
2258
  }
1998
- if (!obj || !utils.checkObject(obj, options2, CONSTS.ACCESS_WRITE)) {
2259
+ if (!obj || !utils.checkObject(obj, userContext2, CONSTS.ACCESS_WRITE)) {
1999
2260
  continue;
2000
2261
  }
2001
2262
  if (!obj.acl) {
@@ -2009,8 +2270,8 @@ class ObjectsInRedisClient {
2009
2270
  obj.acl.state = this.defaultNewAcl?.state || CONSTS.ACCESS_USER_RW | CONSTS.ACCESS_GROUP_READ | CONSTS.ACCESS_EVERY_READ;
2010
2271
  }
2011
2272
  }
2012
- obj.acl.owner = options2.owner || obj.acl.owner;
2013
- obj.acl.ownerGroup = options2.ownerGroup || obj.acl.ownerGroup;
2273
+ obj.acl.owner = options.owner || obj.acl.owner;
2274
+ obj.acl.ownerGroup = options.ownerGroup || obj.acl.ownerGroup;
2014
2275
  filteredKeys.push(keys2[i]);
2015
2276
  filteredObjs.push(obj);
2016
2277
  }
@@ -2022,51 +2283,63 @@ class ObjectsInRedisClient {
2022
2283
  return import_db_base.tools.maybeCallbackWithError(callback, null, filteredObjs);
2023
2284
  }, true);
2024
2285
  }
2025
- chownObject(pattern, options2, callback) {
2026
- if (typeof options2 === "function") {
2027
- callback = options2;
2028
- options2 = {};
2029
- }
2030
- options2 = options2 || {};
2031
- options2.acl = null;
2032
- if (typeof options2 !== "object") {
2033
- options2 = { owner: options2 };
2034
- }
2035
- if (!options2.ownerGroup && options2.group) {
2036
- options2.ownerGroup = options2.group;
2286
+ /**
2287
+ * Change the owner and owner group of all objects matching the given pattern
2288
+ *
2289
+ * @param pattern The pattern of object ids whose owner should be changed
2290
+ * @param options The current request options including the new owner and the user, or the callback
2291
+ * @param options.user The user on whose behalf the operation is performed
2292
+ * @param options.owner The new owner (user id) to assign to the objects
2293
+ * @param options.ownerGroup The new owner group to assign to the objects
2294
+ * @param options.group The new owner group to assign (alias for ownerGroup)
2295
+ * @param callback Called with the list of changed objects
2296
+ */
2297
+ chownObject(pattern, options, callback) {
2298
+ if (options?.group && !options.ownerGroup) {
2299
+ options.ownerGroup = options.group;
2037
2300
  }
2038
- if (!options2.owner && options2.user) {
2039
- options2.owner = options2.user;
2301
+ if (options?.user && !options.owner) {
2302
+ options.owner = options.user;
2040
2303
  }
2041
- if (!options2.owner) {
2304
+ if (!options.owner) {
2042
2305
  this.log.error(`${this.namespace} user is not defined`);
2043
2306
  return import_db_base.tools.maybeCallbackWithError(callback, "invalid parameter");
2044
2307
  }
2045
- if (!options2.ownerGroup) {
2046
- this.getUserGroup(options2.owner, (user, groups) => {
2047
- if (!groups || !groups[0]) {
2048
- return import_db_base.tools.maybeCallbackWithError(callback, `user "${options2.owner}" belongs to no group`);
2308
+ if (!options.ownerGroup) {
2309
+ this.getUserGroup(options.owner, (user, groups) => {
2310
+ if (!groups?.[0]) {
2311
+ return import_db_base.tools.maybeCallbackWithError(callback, `user "${options.owner}" belongs to no group`);
2049
2312
  }
2050
- options2.ownerGroup = groups[0];
2051
- this.chownObject(pattern, options2, callback);
2313
+ options.ownerGroup = groups[0];
2314
+ return this.chownObject(pattern, options, callback);
2052
2315
  });
2053
2316
  return;
2054
2317
  }
2055
- utils.checkObjectRights(this, null, null, options2, CONSTS.ACCESS_WRITE, (err, options3) => {
2318
+ utils.checkObjectRights(this, null, null, options, CONSTS.ACCESS_WRITE, (err, userContext2) => {
2056
2319
  if (err) {
2057
2320
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
2058
2321
  }
2059
- if (!options3.acl.object || !options3.acl.object.write) {
2322
+ if (!userContext2.acl.object || !userContext2.acl.object.write) {
2060
2323
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION);
2061
2324
  }
2062
- return this._chownObject(pattern, options3, callback);
2325
+ return this._chownObject(pattern, options, userContext2, callback);
2063
2326
  });
2064
2327
  }
2065
- chownObjectAsync(pattern, options2) {
2066
- return new Promise((resolve, reject) => this.chownObject(pattern, options2, (err, list) => err ? reject(err) : resolve(list)));
2328
+ /**
2329
+ * Promise-version of chownObject
2330
+ *
2331
+ * @param pattern The pattern of object ids whose owner should be changed
2332
+ * @param options The current request options including the new owner and the user
2333
+ * @param options.user The user on whose behalf the operation is performed
2334
+ * @param options.owner The new owner (user id) to assign to the objects
2335
+ * @param options.ownerGroup The new owner group to assign to the objects
2336
+ * @param options.group The new owner group to assign (alias for ownerGroup)
2337
+ */
2338
+ chownObjectAsync(pattern, options) {
2339
+ return new Promise((resolve, reject) => this.chownObject(pattern, options, (err, list) => err ? reject(err) : resolve(list)));
2067
2340
  }
2068
- _chmodObject(pattern, options2, callback) {
2069
- this.getKeys(pattern, options2, async (err, keys2) => {
2341
+ _chmodObject(pattern, options, userContext2, callback) {
2342
+ this.getKeys(pattern, options, async (err, keys2) => {
2070
2343
  if (err) {
2071
2344
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
2072
2345
  }
@@ -2093,7 +2366,7 @@ class ObjectsInRedisClient {
2093
2366
  this.log.error(`${this.namespace} Cannot parse JSON ${keys2[i]}: ${strObj}`);
2094
2367
  continue;
2095
2368
  }
2096
- if (!utils.checkObject(obj, options2, CONSTS.ACCESS_WRITE) || !obj) {
2369
+ if (!utils.checkObject(obj, userContext2, CONSTS.ACCESS_WRITE) || !obj) {
2097
2370
  continue;
2098
2371
  }
2099
2372
  if (!obj.acl) {
@@ -2107,11 +2380,11 @@ class ObjectsInRedisClient {
2107
2380
  obj.acl.state = this.defaultNewAcl?.state || CONSTS.ACCESS_USER_RW | CONSTS.ACCESS_GROUP_READ | CONSTS.ACCESS_EVERY_READ;
2108
2381
  }
2109
2382
  }
2110
- if (options2.object !== void 0) {
2111
- obj.acl.object = options2.object;
2383
+ if (options.object !== void 0) {
2384
+ obj.acl.object = options.object;
2112
2385
  }
2113
- if (options2.state !== void 0 && "state" in obj.acl) {
2114
- obj.acl.state = options2.state;
2386
+ if (options.state !== void 0 && "state" in obj.acl) {
2387
+ obj.acl.state = options.state;
2115
2388
  }
2116
2389
  filteredKeys.push(keys2[i]);
2117
2390
  filteredObjs.push(obj);
@@ -2123,39 +2396,55 @@ class ObjectsInRedisClient {
2123
2396
  }
2124
2397
  }, true);
2125
2398
  }
2126
- chmodObject(pattern, options2, callback) {
2127
- if (typeof options2 === "function") {
2128
- callback = options2;
2129
- options2 = null;
2130
- }
2131
- options2 = options2 || {};
2132
- options2.acl = null;
2133
- if (typeof options2 !== "object") {
2134
- options2 = { object: options2 };
2399
+ /**
2400
+ * Change the file mode (permissions) of all files matching the given pattern
2401
+ *
2402
+ * @param pattern The pattern of object ids whose files should be changed
2403
+ * @param options The current request options including the new mode and the user, or the callback
2404
+ * @param options.mode The new permission mode to apply
2405
+ * @param options.user The user on whose behalf the operation is performed
2406
+ * @param options.object The permission bitmask for object access
2407
+ * @param options.state The permission bitmask for state access
2408
+ * @param callback Called with the list of changed objects
2409
+ */
2410
+ chmodObject(pattern, options, callback) {
2411
+ if (!options) {
2412
+ this.log.error(`${this.namespace} mode is not defined`);
2413
+ return import_db_base.tools.maybeCallbackWithError(callback, "invalid parameter");
2135
2414
  }
2136
- if (options2.mode && !options2.object) {
2137
- options2.object = options2.mode;
2415
+ if (options.mode && !options.object) {
2416
+ options.object = options.mode;
2138
2417
  }
2139
- if (options2.object === void 0) {
2418
+ if (options.object === void 0) {
2140
2419
  this.log.error(`${this.namespace} mode is not defined`);
2141
2420
  return import_db_base.tools.maybeCallbackWithError(callback, "invalid parameter");
2142
- } else if (typeof options2.mode === "string") {
2143
- options2.mode = parseInt(options2.mode, 16);
2421
+ } else if (typeof options.mode === "string") {
2422
+ options.mode = parseInt(options.mode, 16);
2144
2423
  }
2145
- utils.checkObjectRights(this, null, null, options2, CONSTS.ACCESS_WRITE, (err, options3) => {
2424
+ utils.checkObjectRights(this, null, null, options, CONSTS.ACCESS_WRITE, (err, userContext2) => {
2146
2425
  if (err) {
2147
2426
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
2148
2427
  }
2149
- if (!options3.acl.file.write) {
2428
+ if (!userContext2.acl.file.write) {
2150
2429
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION);
2151
2430
  }
2152
- return this._chmodObject(pattern, options3, callback);
2431
+ return this._chmodObject(pattern, options, userContext2, callback);
2153
2432
  });
2154
2433
  }
2155
- chmodObjectAsync(pattern, options2) {
2156
- return new Promise((resolve, reject) => this.chmodObject(pattern, options2, (err, list) => err ? reject(err) : resolve(list)));
2434
+ /**
2435
+ * Promise-version of chmodObject
2436
+ *
2437
+ * @param pattern The pattern of object ids whose files should be changed
2438
+ * @param options The current request options including the new mode and the user
2439
+ * @param options.mode The new permission mode to apply
2440
+ * @param options.user The user on whose behalf the operation is performed
2441
+ * @param options.object The permission bitmask for object access
2442
+ * @param options.state The permission bitmask for state access
2443
+ */
2444
+ chmodObjectAsync(pattern, options) {
2445
+ return new Promise((resolve, reject) => this.chmodObject(pattern, options, (err, list) => err ? reject(err) : resolve(list)));
2157
2446
  }
2158
- async _getObject(id, options2, callback) {
2447
+ async _getObject(id, userContext2, callback) {
2159
2448
  if (!this.client) {
2160
2449
  return import_db_base.tools.maybeCallbackWithRedisError(callback, ERRORS.ERROR_DB_CLOSED);
2161
2450
  }
@@ -2179,43 +2468,48 @@ class ObjectsInRedisClient {
2179
2468
  }
2180
2469
  }
2181
2470
  if (obj) {
2182
- if (utils.checkObject(obj, options2, CONSTS.ACCESS_READ)) {
2471
+ if (utils.checkObject(obj, userContext2, CONSTS.ACCESS_READ)) {
2183
2472
  return import_db_base.tools.maybeCallbackWithError(callback, null, obj);
2184
2473
  }
2185
2474
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION);
2186
2475
  }
2187
2476
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err, obj);
2188
2477
  }
2189
- getObject(id, options2, callback) {
2190
- if (typeof options2 === "function") {
2191
- callback = options2;
2192
- options2 = null;
2478
+ /**
2479
+ * Get a single object by its id
2480
+ *
2481
+ * @param id The id of the object to read
2482
+ * @param options The current request options including the user, or the callback
2483
+ * @param callback Called with the read object
2484
+ */
2485
+ getObject(id, options, callback) {
2486
+ if (typeof options === "function") {
2487
+ callback = options;
2488
+ options = null;
2193
2489
  }
2194
2490
  if (!callback) {
2195
- return new Promise((resolve, reject) => this.getObject(id, options2, (err, obj) => err ? reject(err) : resolve(obj)));
2491
+ return new Promise((resolve, reject) => this.getObject(id, options, (err, obj) => err ? reject(err) : resolve(obj)));
2196
2492
  }
2197
2493
  if (typeof callback === "function") {
2198
- if (options2?.acl) {
2199
- options2.acl = null;
2200
- }
2201
- utils.checkObjectRights(this, null, null, options2, CONSTS.ACCESS_READ, (err, options3) => {
2494
+ utils.checkObjectRights(this, null, null, options, CONSTS.ACCESS_READ, (err, userContext2) => {
2202
2495
  if (err) {
2203
2496
  return import_db_base.tools.maybeCallbackWithError(callback, err);
2204
2497
  }
2205
- return this._getObject(id, options3, callback);
2498
+ return this._getObject(id, userContext2, callback);
2206
2499
  });
2207
2500
  }
2208
2501
  }
2209
2502
  /**
2503
+ * Promise-version of getObject
2210
2504
  *
2211
- * @param id
2212
- * @param options
2505
+ * @param id The id of the object to read
2506
+ * @param options The current request options including the user
2213
2507
  * @deprecated use `getObject` without callback instead
2214
2508
  */
2215
- getObjectAsync(id, options2) {
2216
- return new Promise((resolve, reject) => this.getObject(id, options2, (err, obj) => err ? reject(err) : resolve(obj)));
2509
+ getObjectAsync(id, options) {
2510
+ return new Promise((resolve, reject) => this.getObject(id, options, (err, obj) => err ? reject(err) : resolve(obj)));
2217
2511
  }
2218
- async _getKeys(pattern, options2, callback, dontModify) {
2512
+ async _getKeys(pattern, userContext2, dontModify, callback) {
2219
2513
  if (!this.client) {
2220
2514
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_DB_CLOSED);
2221
2515
  }
@@ -2232,23 +2526,23 @@ class ObjectsInRedisClient {
2232
2526
  if (!this.client) {
2233
2527
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_DB_CLOSED);
2234
2528
  }
2235
- const result2 = [];
2529
+ const result = [];
2236
2530
  if (keys2) {
2237
2531
  keys2.sort();
2238
- const result3 = [];
2239
- const dontCheck = options2.user === CONSTS.SYSTEM_ADMIN_USER || options2.group !== CONSTS.SYSTEM_ADMIN_GROUP || options2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP);
2532
+ const result2 = [];
2533
+ const dontCheck = userContext2.user === CONSTS.SYSTEM_ADMIN_USER || userContext2.group === CONSTS.SYSTEM_ADMIN_GROUP || userContext2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP);
2240
2534
  if (dontCheck) {
2241
2535
  for (let i = 0; i < keys2.length; i++) {
2242
2536
  const id = keys2[i].substring(this.objNamespaceL);
2243
2537
  if (r.test(id)) {
2244
2538
  if (!dontModify) {
2245
- result3.push(id);
2539
+ result2.push(id);
2246
2540
  } else {
2247
- result3.push(keys2[i]);
2541
+ result2.push(keys2[i]);
2248
2542
  }
2249
2543
  }
2250
2544
  }
2251
- return import_db_base.tools.maybeCallbackWithError(callback, null, result3);
2545
+ return import_db_base.tools.maybeCallbackWithError(callback, null, result2);
2252
2546
  }
2253
2547
  let metas;
2254
2548
  try {
@@ -2266,39 +2560,54 @@ class ObjectsInRedisClient {
2266
2560
  this.log.error(`${this.namespace} Cannot parse JSON ${keys2[i]}: ${metaStr}`);
2267
2561
  continue;
2268
2562
  }
2269
- if (r.test(keys2[i]) && utils.checkObject(meta, options2, CONSTS.ACCESS_READ)) {
2563
+ if (r.test(keys2[i]) && utils.checkObject(meta, userContext2, CONSTS.ACCESS_READ)) {
2270
2564
  if (!dontModify) {
2271
- result3.push(keys2[i].substring(this.objNamespaceL));
2565
+ result2.push(keys2[i].substring(this.objNamespaceL));
2272
2566
  } else {
2273
- result3.push(keys2[i]);
2567
+ result2.push(keys2[i]);
2274
2568
  }
2275
2569
  }
2276
2570
  }
2277
- return import_db_base.tools.maybeCallbackWithError(callback, null, result3);
2571
+ return import_db_base.tools.maybeCallbackWithError(callback, null, result2);
2278
2572
  }
2279
- return import_db_base.tools.maybeCallbackWithError(callback, null, result2);
2573
+ return import_db_base.tools.maybeCallbackWithError(callback, null, result);
2280
2574
  }
2281
- getKeys(pattern, options2, callback, dontModify) {
2282
- if (typeof options2 === "function") {
2283
- callback = options2;
2284
- options2 = null;
2575
+ /**
2576
+ * Get all object ids matching the given pattern
2577
+ *
2578
+ * @param pattern The pattern to match object ids against
2579
+ * @param options The current request options including the user, or the callback
2580
+ * @param callback Called with the matching keys
2581
+ * @param dontModify If true, the returned keys are not stripped of the namespace
2582
+ */
2583
+ getKeys(pattern, options, callback, dontModify) {
2584
+ if (typeof options === "function") {
2585
+ callback = options;
2586
+ options = null;
2285
2587
  }
2286
2588
  if (!callback) {
2287
- return new Promise((resolve, reject) => this.getKeys(pattern, options2, (err, obj) => err ? reject(err) : resolve(obj), dontModify));
2589
+ return new Promise((resolve, reject) => this.getKeys(pattern, options, (err, obj) => err ? reject(err) : resolve(obj), dontModify));
2288
2590
  }
2289
2591
  if (typeof callback === "function") {
2290
- utils.checkObjectRights(this, null, null, options2, "list", (err, options3) => {
2592
+ utils.checkObjectRights(this, null, null, options, "list", (err, userContext2) => {
2291
2593
  if (err) {
2292
2594
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
2293
2595
  }
2294
- return this._getKeys(pattern, options3, callback, dontModify);
2596
+ return this._getKeys(pattern, userContext2, dontModify, callback);
2295
2597
  });
2296
2598
  }
2297
2599
  }
2298
- getKeysAsync(id, options2) {
2299
- return this.getKeys(id, options2);
2600
+ /**
2601
+ * Promise-version of getKeys
2602
+ *
2603
+ * @param pattern The pattern to match object ids against
2604
+ * @param options The current request options including the user
2605
+ * @param options.user The user on whose behalf the keys are read
2606
+ */
2607
+ getKeysAsync(pattern, options) {
2608
+ return this.getKeys(pattern, options);
2300
2609
  }
2301
- async _getObjects(keys2, options2, callback, dontModify) {
2610
+ async _getObjects(keys2, userContext2, callback, dontModify) {
2302
2611
  if (!keys2) {
2303
2612
  return import_db_base.tools.maybeCallbackWithError(callback, "no keys");
2304
2613
  }
@@ -2326,9 +2635,9 @@ class ObjectsInRedisClient {
2326
2635
  } catch (e) {
2327
2636
  this.log.warn(`${this.namespace} redis mget ${!objs2 ? 0 : objs2.length} ${_keys.length}, err: ${e.message}`);
2328
2637
  }
2329
- let result2 = [];
2638
+ let result = [];
2330
2639
  if (objs2) {
2331
- const dontCheck = options2 && (options2.user === CONSTS.SYSTEM_ADMIN_USER || options2.group !== CONSTS.SYSTEM_ADMIN_GROUP || options2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP));
2640
+ const dontCheck = userContext2 && (userContext2.user === CONSTS.SYSTEM_ADMIN_USER || userContext2.group === CONSTS.SYSTEM_ADMIN_GROUP || userContext2.groups?.includes(CONSTS.SYSTEM_ADMIN_GROUP));
2332
2641
  if (!dontCheck) {
2333
2642
  for (let i = 0; i < objs2.length; i++) {
2334
2643
  const strObj = objs2[i];
@@ -2337,17 +2646,17 @@ class ObjectsInRedisClient {
2337
2646
  obj = strObj ? JSON.parse(strObj) : null;
2338
2647
  } catch {
2339
2648
  this.log.error(`${this.namespace} Cannot parse JSON ${_keys[i]}: ${objs2[i]}`);
2340
- result2.push({ error: ERRORS.ERROR_PERMISSION });
2649
+ result.push({ error: ERRORS.ERROR_PERMISSION });
2341
2650
  continue;
2342
2651
  }
2343
- if (utils.checkObject(obj, options2, CONSTS.ACCESS_READ)) {
2344
- result2.push(obj);
2652
+ if (utils.checkObject(obj, userContext2, CONSTS.ACCESS_READ)) {
2653
+ result.push(obj);
2345
2654
  } else {
2346
- result2.push({ error: ERRORS.ERROR_PERMISSION });
2655
+ result.push({ error: ERRORS.ERROR_PERMISSION });
2347
2656
  }
2348
2657
  }
2349
2658
  } else {
2350
- result2 = objs2.map((obj, i) => {
2659
+ result = objs2.map((obj, i) => {
2351
2660
  try {
2352
2661
  return obj !== null ? JSON.parse(obj) : obj;
2353
2662
  } catch {
@@ -2357,35 +2666,46 @@ class ObjectsInRedisClient {
2357
2666
  });
2358
2667
  }
2359
2668
  }
2360
- return import_db_base.tools.maybeCallbackWithError(callback, null, result2);
2669
+ return import_db_base.tools.maybeCallbackWithError(callback, null, result);
2361
2670
  }
2362
- getObjects(keys2, options2, callback, dontModify) {
2363
- if (typeof options2 === "function") {
2364
- callback = options2;
2365
- options2 = null;
2671
+ /**
2672
+ * Get multiple objects by their ids
2673
+ *
2674
+ * @param keys The ids of the objects to read
2675
+ * @param options The current request options including the user, or the callback
2676
+ * @param callback Called with the read objects
2677
+ * @param dontModify If true, the returned objects are not cloned/modified
2678
+ */
2679
+ getObjects(keys2, options, callback, dontModify) {
2680
+ if (typeof options === "function") {
2681
+ callback = options;
2682
+ options = null;
2366
2683
  }
2367
2684
  if (!callback) {
2368
2685
  return new Promise((resolve, reject) => (
2369
2686
  // @ts-expect-error need to clarify, that objs is not undefined if no error is provided
2370
- this.getObjects(keys2, options2, (err, objs2) => err ? reject(err) : resolve(objs2), dontModify)
2687
+ this.getObjects(keys2, options, (err, objs2) => err ? reject(err) : resolve(objs2), dontModify)
2371
2688
  ));
2372
2689
  }
2373
- if (options2?.acl) {
2374
- options2.acl = null;
2375
- }
2376
2690
  if (typeof callback === "function") {
2377
- utils.checkObjectRights(this, null, null, options2, CONSTS.ACCESS_READ, (err, options3) => {
2691
+ utils.checkObjectRights(this, null, null, options, CONSTS.ACCESS_READ, (err, userContext2) => {
2378
2692
  if (err) {
2379
2693
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
2380
2694
  }
2381
- return this._getObjects(keys2, options3, callback, dontModify);
2695
+ return this._getObjects(keys2, userContext2, callback, dontModify);
2382
2696
  });
2383
2697
  }
2384
2698
  }
2385
- getObjectsAsync(keys2, options2) {
2386
- return this.getObjects(keys2, options2);
2699
+ /**
2700
+ * Promise-version of getObjects
2701
+ *
2702
+ * @param keys The ids of the objects to read
2703
+ * @param options The current request options including the user
2704
+ */
2705
+ getObjectsAsync(keys2, options) {
2706
+ return this.getObjects(keys2, options);
2387
2707
  }
2388
- async _getObjectsByPattern(pattern, options2, callback) {
2708
+ async _getObjectsByPattern(pattern, userContext2, callback) {
2389
2709
  if (!pattern || typeof pattern !== "string") {
2390
2710
  return import_db_base.tools.maybeCallbackWithError(callback, `invalid pattern ${JSON.stringify(pattern)}`);
2391
2711
  }
@@ -2404,32 +2724,43 @@ class ObjectsInRedisClient {
2404
2724
  if (this.settings.connection.enhancedLogging) {
2405
2725
  this.log.silly(`${this.namespace} redis keys ${keys2.length} ${pattern}`);
2406
2726
  }
2407
- this._getObjects(keys2, options2, callback, true);
2727
+ this._getObjects(keys2, userContext2, callback, true).catch((e) => this.log.error(`${this.namespace} Cannot get objects: ${e.message}`));
2408
2728
  }
2409
- getObjectsByPattern(pattern, options2, callback) {
2410
- if (typeof options2 === "function") {
2411
- callback = options2;
2412
- options2 = null;
2729
+ /**
2730
+ * Get all objects whose id matches the given pattern
2731
+ *
2732
+ * @param pattern The pattern to match object ids against
2733
+ * @param options The current request options including the user, or the callback
2734
+ * @param callback Called with the matching objects
2735
+ */
2736
+ getObjectsByPattern(pattern, options, callback) {
2737
+ if (typeof options === "function") {
2738
+ callback = options;
2739
+ options = null;
2413
2740
  }
2414
2741
  if (!callback) {
2415
- return new Promise((resolve, reject) => this.getObjectsByPattern(pattern, options2, (err, obj) => err ? reject(err) : resolve(obj)));
2416
- }
2417
- if (options2?.acl) {
2418
- options2.acl = null;
2742
+ return new Promise((resolve, reject) => this.getObjectsByPattern(pattern, options, (err, obj) => err ? reject(err) : resolve(obj)));
2419
2743
  }
2420
2744
  if (typeof callback === "function") {
2421
- utils.checkObjectRights(this, null, null, options2, CONSTS.ACCESS_READ, (err, options3) => {
2745
+ utils.checkObjectRights(this, null, null, options, CONSTS.ACCESS_READ, (err, userContext2) => {
2422
2746
  if (err) {
2423
2747
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
2424
2748
  }
2425
- return this._getObjectsByPattern(pattern, options3, callback);
2749
+ return this._getObjectsByPattern(pattern, userContext2, callback);
2426
2750
  });
2427
2751
  }
2428
2752
  }
2429
- getObjectsByPatternAsync(pattern, options2) {
2430
- return new Promise((resolve, reject) => this.getObjectsByPattern(pattern, options2, (err, objs2) => err ? reject(err) : resolve(objs2)));
2753
+ /**
2754
+ * Promise-version of getObjectsByPattern
2755
+ *
2756
+ * @param pattern The pattern to match object ids against
2757
+ * @param options The current request options including the user
2758
+ * @param options.user The user on whose behalf the objects are read
2759
+ */
2760
+ getObjectsByPatternAsync(pattern, options) {
2761
+ return new Promise((resolve, reject) => this.getObjectsByPattern(pattern, options, (err, objs2) => err ? reject(err) : resolve(objs2)));
2431
2762
  }
2432
- async _setObject(id, obj, options2) {
2763
+ async _setObject(id, obj, userContext2) {
2433
2764
  if (!id || typeof id !== "string" || utils.REG_CHECK_ID.test(id)) {
2434
2765
  throw new Error(`Invalid ID: ${id}`);
2435
2766
  }
@@ -2438,7 +2769,7 @@ class ObjectsInRedisClient {
2438
2769
  throw new Error("obj is null");
2439
2770
  }
2440
2771
  if (!import_db_base.tools.isObject(obj)) {
2441
- this.log.warn(`${this.namespace} setObject: Argument object is no object: ${obj}`);
2772
+ this.log.warn(`${this.namespace} setObject: Argument object is no object: ${JSON.stringify(obj)}`);
2442
2773
  throw new Error("obj is no object");
2443
2774
  }
2444
2775
  if (!this.client) {
@@ -2530,15 +2861,15 @@ class ObjectsInRedisClient {
2530
2861
  if (obj.type !== "state") {
2531
2862
  delete obj.acl.state;
2532
2863
  }
2533
- if (options2.user && options2.user !== CONSTS.SYSTEM_ADMIN_USER) {
2534
- obj.acl.owner = options2.user;
2864
+ if (userContext2.user && userContext2.user !== CONSTS.SYSTEM_ADMIN_USER) {
2865
+ obj.acl.owner = userContext2.user;
2535
2866
  }
2536
- if (options2.group && options2.group !== CONSTS.SYSTEM_ADMIN_GROUP) {
2537
- obj.acl.ownerGroup = options2.group;
2867
+ if (userContext2.group && userContext2.group !== CONSTS.SYSTEM_ADMIN_GROUP) {
2868
+ obj.acl.ownerGroup = userContext2.group;
2538
2869
  }
2539
2870
  }
2540
- if (this.defaultNewAcl && obj.acl && !obj.acl.ownerGroup && options2.group) {
2541
- obj.acl.ownerGroup = options2.group;
2871
+ if (this.defaultNewAcl && obj.acl && !obj.acl.ownerGroup && userContext2.group) {
2872
+ obj.acl.ownerGroup = userContext2.group;
2542
2873
  }
2543
2874
  const message = JSON.stringify(obj);
2544
2875
  const commands = [];
@@ -2569,32 +2900,29 @@ class ObjectsInRedisClient {
2569
2900
  return { id };
2570
2901
  }
2571
2902
  /**
2572
- * set anew or update object
2903
+ * set anew or update an object
2573
2904
  *
2574
2905
  * This function writes the object into DB
2575
2906
  *
2576
2907
  * @param id ID of the object
2577
- * @param obj
2908
+ * @param obj The object to write
2578
2909
  * @param options options for access control are optional
2579
2910
  * @param callback return function
2580
2911
  */
2581
- setObject(id, obj, options2, callback) {
2582
- if (typeof options2 === "function") {
2583
- callback = options2;
2584
- options2 = {};
2912
+ setObject(id, obj, options, callback) {
2913
+ if (typeof options === "function") {
2914
+ callback = options;
2915
+ options = {};
2585
2916
  }
2586
2917
  if (!callback) {
2587
- return new Promise((resolve, reject) => this.setObject(id, obj, options2, (err, res) => err ? reject(err) : resolve(res)));
2588
- }
2589
- if (options2?.acl) {
2590
- options2.acl = null;
2918
+ return new Promise((resolve, reject) => this.setObject(id, obj, options, (err, res) => err ? reject(err) : resolve(res)));
2591
2919
  }
2592
- utils.checkObjectRights(this, null, null, options2, CONSTS.ACCESS_WRITE, async (err) => {
2920
+ utils.checkObjectRights(this, null, null, options, CONSTS.ACCESS_WRITE, async (err, userContext2) => {
2593
2921
  if (err) {
2594
2922
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
2595
2923
  }
2596
2924
  try {
2597
- const res = await this._setObject(id, obj, options2 || {});
2925
+ const res = await this._setObject(id, obj, userContext2);
2598
2926
  return import_db_base.tools.maybeCallbackWithError(callback, null, res);
2599
2927
  } catch (e) {
2600
2928
  return import_db_base.tools.maybeCallbackWithError(callback, e);
@@ -2602,19 +2930,20 @@ class ObjectsInRedisClient {
2602
2930
  });
2603
2931
  }
2604
2932
  /**
2933
+ * Promise-version of setObject
2605
2934
  *
2606
- * @param id
2607
- * @param obj
2608
- * @param options
2935
+ * @param id ID of the object
2936
+ * @param obj The object to write
2937
+ * @param options options for access control are optional
2609
2938
  * @deprecated use `setObject` without callback instead
2610
2939
  */
2611
- setObjectAsync(id, obj, options2) {
2940
+ setObjectAsync(id, obj, options) {
2612
2941
  return new Promise((resolve, reject) => (
2613
2942
  // @ts-expect-error TODO we are returning type Object for ease of use to devs, but formally these are AnyObjects, e.g. not guaranteed to have common
2614
- this.setObject(id, obj, options2, (err, res) => err ? reject(err) : resolve(res))
2943
+ this.setObject(id, obj, options, (err, res) => err ? reject(err) : resolve(res))
2615
2944
  ));
2616
2945
  }
2617
- async _delObject(id, options2) {
2946
+ async _delObject(id, userContext2) {
2618
2947
  if (!id || typeof id !== "string" || utils.REG_CHECK_ID.test(id)) {
2619
2948
  throw new Error(`Invalid ID: ${id}`);
2620
2949
  }
@@ -2638,7 +2967,7 @@ class ObjectsInRedisClient {
2638
2967
  this.log.warn(`${this.namespace} Cannot parse ${id} - ${oldObjStr}: ${e.message}`);
2639
2968
  oldObj = null;
2640
2969
  }
2641
- if (!utils.checkObject(oldObj, options2, CONSTS.ACCESS_WRITE)) {
2970
+ if (!utils.checkObject(oldObj, userContext2, CONSTS.ACCESS_WRITE)) {
2642
2971
  throw new Error(ERRORS.ERROR_PERMISSION);
2643
2972
  } else {
2644
2973
  const commands = [];
@@ -2662,34 +2991,45 @@ class ObjectsInRedisClient {
2662
2991
  await this.client.publish(this.objNamespace + id, "null");
2663
2992
  }
2664
2993
  }
2665
- delObject(id, options2, callback) {
2666
- if (typeof options2 === "function") {
2667
- callback = options2;
2668
- options2 = null;
2994
+ /**
2995
+ * Delete an object
2996
+ *
2997
+ * @param id The id of the object to delete
2998
+ * @param options The current request options including the user, or the callback
2999
+ * @param callback Called once the object has been deleted
3000
+ */
3001
+ delObject(id, options, callback) {
3002
+ if (typeof options === "function") {
3003
+ callback = options;
3004
+ options = null;
2669
3005
  }
2670
3006
  if (!callback) {
2671
- return new Promise((resolve, reject) => this.delObject(id, options2, (err) => err ? reject(err) : resolve()));
3007
+ return new Promise((resolve, reject) => this.delObject(id, options, (err) => err ? reject(err) : resolve()));
2672
3008
  }
2673
- if (options2?.acl) {
2674
- options2.acl = null;
2675
- }
2676
- utils.checkObjectRights(this, null, null, options2, CONSTS.ACCESS_DELETE, async (err, options3) => {
3009
+ utils.checkObjectRights(this, null, null, options, CONSTS.ACCESS_DELETE, async (err, userContext2) => {
2677
3010
  if (err) {
2678
3011
  return import_db_base.tools.maybeCallbackWithError(callback, err);
2679
3012
  }
2680
3013
  try {
2681
- await this._delObject(id, options3);
3014
+ await this._delObject(id, userContext2);
2682
3015
  return import_db_base.tools.maybeCallback(callback);
2683
3016
  } catch (e) {
2684
3017
  return import_db_base.tools.maybeCallbackWithRedisError(callback, e);
2685
3018
  }
2686
3019
  });
2687
3020
  }
2688
- delObjectAsync(id, options2) {
2689
- return this.delObject(id, options2);
3021
+ /**
3022
+ * Promise-version of delObject
3023
+ *
3024
+ * @param id The id of the object to delete
3025
+ * @param options The current request options including the user
3026
+ * @param options.user The user on whose behalf the operation is performed
3027
+ */
3028
+ delObjectAsync(id, options) {
3029
+ return this.delObject(id, options);
2690
3030
  }
2691
3031
  /**
2692
- * Function to checks if comparisons will work according to the configured Locale
3032
+ * Function to check if comparisons will work according to the configured Locale
2693
3033
  */
2694
3034
  async isSystemLocaleSupported() {
2695
3035
  if (!this.client) {
@@ -2704,14 +3044,12 @@ class ObjectsInRedisClient {
2704
3044
  }
2705
3045
  }
2706
3046
  // this function is very ineffective. Because reads all objects and then process them
2707
- async _applyViewFunc(func, params, options = {}) {
3047
+ async _applyViewFunc(func, params, userContext) {
2708
3048
  if (!this.client) {
2709
3049
  throw new Error(ERRORS.ERROR_DB_CLOSED);
2710
3050
  }
2711
- const result = {
2712
- rows: []
2713
- };
2714
- const filterEntries = (arr, duplicateFiltering) => {
3051
+ let rows = [];
3052
+ const filterEntries = /* @__PURE__ */ __name((arr, duplicateFiltering) => {
2715
3053
  if (duplicateFiltering) {
2716
3054
  const included = /* @__PURE__ */ new Map();
2717
3055
  return arr.filter((obj) => {
@@ -2725,10 +3063,10 @@ class ObjectsInRedisClient {
2725
3063
  return arr.filter((obj) => {
2726
3064
  return obj.value !== null;
2727
3065
  });
2728
- };
2729
- params = params || {};
2730
- params.startkey = params.startkey || "";
2731
- params.endkey = params.endkey || "\u9999";
3066
+ }, "filterEntries");
3067
+ params ||= {};
3068
+ params.startkey ||= "";
3069
+ params.endkey ||= "\u9999";
2732
3070
  const wildcardPos = params.endkey.indexOf("\u9999");
2733
3071
  let wildCardLastPos = true;
2734
3072
  if (wildcardPos !== -1 && wildcardPos !== params.endkey.length - 1) {
@@ -2747,16 +3085,7 @@ class ObjectsInRedisClient {
2747
3085
  }
2748
3086
  let objs2;
2749
3087
  try {
2750
- objs2 = await this.client.evalsha([
2751
- this.scripts.filter,
2752
- 6,
2753
- this.objNamespace,
2754
- params.startkey,
2755
- params.endkey,
2756
- matches[1],
2757
- cursor,
2758
- `${this.setNamespace}object.type.${matches[1]}`
2759
- ]);
3088
+ objs2 = await this.client.evalsha(this.scripts.filter, 6, this.objNamespace, params.startkey, params.endkey, matches[1], cursor, `${this.setNamespace}object.type.${matches[1]}`);
2760
3089
  } catch (e) {
2761
3090
  this.log.warn(`${this.namespace} Cannot get view: ${e.message}`);
2762
3091
  throw e;
@@ -2783,7 +3112,8 @@ class ObjectsInRedisClient {
2783
3112
  }
2784
3113
  if (matches[2] && matches[2].trim() === "doc._id") {
2785
3114
  return { id: obj._id, value: obj };
2786
- } else if (matches[2] && matches[2].trim() === "doc.common.name" && obj.common) {
3115
+ }
3116
+ if (matches[2] && matches[2].trim() === "doc.common.name" && obj.common) {
2787
3117
  if (typeof obj.common.name === "object") {
2788
3118
  if (obj.common.name.en) {
2789
3119
  return { id: obj.common.name.en, value: obj };
@@ -2796,25 +3126,26 @@ class ObjectsInRedisClient {
2796
3126
  return { id: "parseError", value: null };
2797
3127
  });
2798
3128
  if (currRows.length) {
2799
- result.rows = [...result.rows, ...currRows];
3129
+ rows = [...rows, ...currRows];
2800
3130
  }
2801
3131
  } while (cursor !== "0");
2802
3132
  if (func.reduce === "_stats") {
2803
3133
  let max = null;
2804
- for (let i = 0; i < result.rows.length; i++) {
2805
- if (max === null || result.rows[i].value > max) {
2806
- max = result.rows[i].value;
3134
+ for (let i = 0; i < rows.length; i++) {
3135
+ const _value = rows[i].value;
3136
+ if (max === null || _value !== null && _value > max) {
3137
+ max = _value;
2807
3138
  }
2808
3139
  }
2809
3140
  if (max !== null) {
2810
- result.rows = [{ id: "_stats", value: { max } }];
3141
+ rows = [{ id: "_stats", value: { max } }];
2811
3142
  } else {
2812
- result.rows = [];
3143
+ rows = [];
2813
3144
  }
2814
3145
  }
2815
- result.rows = filterEntries(result.rows, filterRequired);
2816
- return result;
2817
- } else if (
3146
+ return { rows: filterEntries(rows, filterRequired) };
3147
+ }
3148
+ if (
2818
3149
  // filter by script
2819
3150
  wildCardLastPos && func?.map && this.scripts.script && func.map.includes("doc.common.engineType")
2820
3151
  ) {
@@ -2826,15 +3157,7 @@ class ObjectsInRedisClient {
2826
3157
  }
2827
3158
  let res;
2828
3159
  try {
2829
- res = await this.client.evalsha([
2830
- this.scripts.script,
2831
- 5,
2832
- this.objNamespace,
2833
- params.startkey,
2834
- params.endkey,
2835
- cursor,
2836
- `${this.setNamespace}object.type.script`
2837
- ]);
3160
+ res = await this.client.evalsha(this.scripts.script, 5, this.objNamespace, params.startkey, params.endkey, cursor, `${this.setNamespace}object.type.script`);
2838
3161
  } catch (e) {
2839
3162
  this.log.warn(`${this.namespace} Cannot get "scripts" view: ${e.message}`);
2840
3163
  throw e;
@@ -2858,12 +3181,12 @@ class ObjectsInRedisClient {
2858
3181
  }
2859
3182
  });
2860
3183
  if (currRows.length) {
2861
- result.rows = [...result.rows, ...currRows];
3184
+ rows = [...rows, ...currRows];
2862
3185
  }
2863
3186
  } while (cursor !== "0");
2864
- result.rows = filterEntries(result.rows, filterRequired);
2865
- return result;
2866
- } else if (
3187
+ return { rows: filterEntries(rows, filterRequired) };
3188
+ }
3189
+ if (
2867
3190
  // filter by hm-rega programs
2868
3191
  wildCardLastPos && func?.map && this.scripts.programs && func.map.includes("doc.native.TypeName === 'PROGRAM'")
2869
3192
  ) {
@@ -2875,15 +3198,7 @@ class ObjectsInRedisClient {
2875
3198
  }
2876
3199
  let objs2;
2877
3200
  try {
2878
- objs2 = await this.client.evalsha([
2879
- this.scripts.programs,
2880
- 5,
2881
- `${this.objNamespace}hm-rega.`,
2882
- params.startkey,
2883
- params.endkey,
2884
- cursor,
2885
- `${this.setNamespace}object.type.channel`
2886
- ]);
3201
+ objs2 = await this.client.evalsha(this.scripts.programs, 5, `${this.objNamespace}hm-rega.`, params.startkey, params.endkey, cursor, `${this.setNamespace}object.type.channel`);
2887
3202
  } catch (e) {
2888
3203
  this.log.warn(`${this.namespace} Cannot get view: ${e.message}`);
2889
3204
  throw e;
@@ -2905,12 +3220,12 @@ class ObjectsInRedisClient {
2905
3220
  }
2906
3221
  });
2907
3222
  if (currRows.length) {
2908
- result.rows = [...result.rows, ...currRows];
3223
+ rows = [...rows, ...currRows];
2909
3224
  }
2910
3225
  } while (cursor !== "0");
2911
- result.rows = filterEntries(result.rows, filterRequired);
2912
- return result;
2913
- } else if (
3226
+ return { rows: filterEntries(rows, filterRequired) };
3227
+ }
3228
+ if (
2914
3229
  // filter by hm-rega variables
2915
3230
  wildCardLastPos && func?.map && this.scripts.variables && func.map.includes("doc.native.TypeName === 'ALARMDP'")
2916
3231
  ) {
@@ -2922,15 +3237,7 @@ class ObjectsInRedisClient {
2922
3237
  }
2923
3238
  let objs2;
2924
3239
  try {
2925
- objs2 = await this.client.evalsha([
2926
- this.scripts.variables,
2927
- 5,
2928
- `${this.objNamespace}hm-rega.`,
2929
- params.startkey,
2930
- params.endkey,
2931
- cursor,
2932
- `${this.setNamespace}object.type.state`
2933
- ]);
3240
+ objs2 = await this.client.evalsha(this.scripts.variables, 5, `${this.objNamespace}hm-rega.`, params.startkey, params.endkey, cursor, `${this.setNamespace}object.type.state`);
2934
3241
  } catch (e) {
2935
3242
  this.log.warn(`${this.namespace} Cannot get view ${e.message}`);
2936
3243
  throw e;
@@ -2952,12 +3259,12 @@ class ObjectsInRedisClient {
2952
3259
  }
2953
3260
  });
2954
3261
  if (currRows.length) {
2955
- result.rows = [...result.rows, ...currRows];
3262
+ rows = [...rows, ...currRows];
2956
3263
  }
2957
3264
  } while (cursor !== "0");
2958
- result.rows = filterEntries(result.rows, filterRequired);
2959
- return result;
2960
- } else if (
3265
+ return { rows: filterEntries(rows, filterRequired) };
3266
+ }
3267
+ if (
2961
3268
  // filter by custom, redis also returns if common.custom is not present
2962
3269
  wildCardLastPos && func?.map && this.scripts.custom && func.map.includes("doc.common.custom")
2963
3270
  ) {
@@ -2969,15 +3276,7 @@ class ObjectsInRedisClient {
2969
3276
  }
2970
3277
  let objs2;
2971
3278
  try {
2972
- objs2 = await this.client.evalsha([
2973
- this.scripts.custom,
2974
- 5,
2975
- this.objNamespace,
2976
- params.startkey,
2977
- params.endkey,
2978
- cursor,
2979
- `${this.setNamespace}object.common.custom`
2980
- ]);
3279
+ objs2 = await this.client.evalsha(this.scripts.custom, 5, this.objNamespace, params.startkey, params.endkey, cursor, `${this.setNamespace}object.common.custom`);
2981
3280
  } catch (e) {
2982
3281
  this.log.warn(`${this.namespace} Cannot get view: ${e.message}`);
2983
3282
  throw e;
@@ -3000,15 +3299,14 @@ class ObjectsInRedisClient {
3000
3299
  }
3001
3300
  if (obj?.common?.custom) {
3002
3301
  if (useFullObject) {
3003
- result.rows.push({ id: obj._id, value: obj });
3302
+ rows.push({ id: obj._id, value: obj });
3004
3303
  } else {
3005
- result.rows.push({ id: obj._id, value: obj.common.custom });
3304
+ rows.push({ id: obj._id, value: obj.common.custom });
3006
3305
  }
3007
3306
  }
3008
3307
  }
3009
3308
  } while (cursor !== "0");
3010
- result.rows = filterEntries(result.rows, filterRequired);
3011
- return result;
3309
+ return { rows: filterEntries(rows, filterRequired) };
3012
3310
  }
3013
3311
  if (!wildCardLastPos) {
3014
3312
  this.log.debug(`${this.namespace} Search can't be optimized because wildcard not at the end, fallback to keys!: ${func.map}`);
@@ -3051,9 +3349,9 @@ class ObjectsInRedisClient {
3051
3349
  } catch {
3052
3350
  objs = [];
3053
3351
  }
3054
- const _emit_ = (id, obj) => {
3055
- result.rows.push({ id, value: obj });
3056
- };
3352
+ const _emit_ = /* @__PURE__ */ __name((id, obj) => {
3353
+ rows.push({ id, value: obj });
3354
+ }, "_emit_");
3057
3355
  const f = eval(`(${func.map.replace(/^function\(([a-z0-9A-Z_]+)\)/g, "function($1, emit)")})`);
3058
3356
  for (let i = 0; i < keys.length; i++) {
3059
3357
  const strObj = objs[i];
@@ -3064,7 +3362,7 @@ class ObjectsInRedisClient {
3064
3362
  this.log.error(`${this.namespace} Cannot parse JSON ${keys[i]}: ${objs[i]}`);
3065
3363
  continue;
3066
3364
  }
3067
- if (!utils.checkObject(obj, options, CONSTS.ACCESS_READ)) {
3365
+ if (!utils.checkObject(obj, userContext, CONSTS.ACCESS_READ)) {
3068
3366
  continue;
3069
3367
  }
3070
3368
  if (obj) {
@@ -3077,20 +3375,21 @@ class ObjectsInRedisClient {
3077
3375
  }
3078
3376
  if (func.reduce === "_stats") {
3079
3377
  let max = null;
3080
- for (let i = 0; i < result.rows.length; i++) {
3081
- if (max === null || result.rows[i].value > max) {
3082
- max = result.rows[i].value;
3378
+ for (let i = 0; i < rows.length; i++) {
3379
+ const _value = rows[i].value;
3380
+ if (max === null || _value !== null && _value > max) {
3381
+ max = rows[i].value;
3083
3382
  }
3084
3383
  }
3085
3384
  if (max !== null) {
3086
- result.rows = [{ id: "_stats", value: { max } }];
3385
+ rows = [{ id: "_stats", value: { max } }];
3087
3386
  } else {
3088
- result.rows = [];
3387
+ rows = [];
3089
3388
  }
3090
3389
  }
3091
- return result;
3390
+ return { rows };
3092
3391
  }
3093
- async _getObjectView(design, search, params2, options2) {
3392
+ async _getObjectView(design, search, params2, userContext2) {
3094
3393
  if (!this.client) {
3095
3394
  throw new Error(ERRORS.ERROR_DB_CLOSED);
3096
3395
  }
@@ -3109,7 +3408,7 @@ class ObjectsInRedisClient {
3109
3408
  throw new Error(`Cannot parse JSON: "_design/${design}" / "${obj}"`);
3110
3409
  }
3111
3410
  if (obj.views?.[search]) {
3112
- return this._applyViewFunc(obj.views[search], params2, options2);
3411
+ return this._applyViewFunc(obj.views[search], params2, userContext2);
3113
3412
  }
3114
3413
  this.log.error(`${this.namespace} Cannot find search "${search}" in "${design}"`);
3115
3414
  throw new Error(`Cannot find search "${search}" in "${design}"`);
@@ -3118,24 +3417,30 @@ class ObjectsInRedisClient {
3118
3417
  throw new Error(`Cannot find view "${design}"`);
3119
3418
  }
3120
3419
  }
3121
- getObjectView(design, search, params2, options2, callback) {
3122
- if (typeof options2 === "function") {
3123
- callback = options2;
3124
- options2 = null;
3420
+ /**
3421
+ * Run a predefined object view (design document) and return the matching rows
3422
+ *
3423
+ * @param design The design document name
3424
+ * @param search The view name within the design document
3425
+ * @param params Query parameters such as startkey and endkey
3426
+ * @param options The current request options including the user, or the callback
3427
+ * @param callback Called with the matching rows
3428
+ */
3429
+ getObjectView(design, search, params2, options, callback) {
3430
+ if (typeof options === "function") {
3431
+ callback = options;
3432
+ options = null;
3125
3433
  }
3126
3434
  if (!callback) {
3127
- return new Promise((resolve, reject) => this.getObjectView(design, search, params2, options2, (err, obj) => err ? reject(err) : resolve(obj)));
3128
- }
3129
- if (options2?.acl) {
3130
- options2.acl = null;
3435
+ return new Promise((resolve, reject) => this.getObjectView(design, search, params2, options, (err, obj) => err ? reject(err) : resolve(obj)));
3131
3436
  }
3132
3437
  if (typeof callback === "function") {
3133
- utils.checkObjectRights(this, null, null, options2, "list", async (err, options3) => {
3438
+ utils.checkObjectRights(this, null, null, options, "list", async (err, userContext2) => {
3134
3439
  if (err) {
3135
3440
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
3136
3441
  }
3137
3442
  try {
3138
- const res = await this._getObjectView(design, search, params2, options3);
3443
+ const res = await this._getObjectView(design, search, params2, userContext2);
3139
3444
  return import_db_base.tools.maybeCallbackWithError(callback, null, res);
3140
3445
  } catch (e) {
3141
3446
  return import_db_base.tools.maybeCallbackWithRedisError(callback, e);
@@ -3143,16 +3448,25 @@ class ObjectsInRedisClient {
3143
3448
  });
3144
3449
  }
3145
3450
  }
3146
- getObjectViewAsync(design, search, params2, options2) {
3147
- return this.getObjectView(design, search, params2, options2);
3451
+ /**
3452
+ * Promise-version of getObjectView
3453
+ *
3454
+ * @param design The design document name
3455
+ * @param search The view name within the design document
3456
+ * @param params Query parameters such as startkey and endkey
3457
+ * @param options The current request options including the user
3458
+ * @param options.user The user on whose behalf the view is queried
3459
+ */
3460
+ getObjectViewAsync(design, search, params2, options) {
3461
+ return this.getObjectView(design, search, params2, options);
3148
3462
  }
3149
- async _getObjectList(params2, options2) {
3463
+ async _getObjectList(params2, userContext2) {
3150
3464
  if (!this.client) {
3151
3465
  throw new Error(ERRORS.ERROR_DB_CLOSED);
3152
3466
  }
3153
- params2 = params2 || {};
3154
- params2.startkey = params2.startkey || "";
3155
- params2.endkey = params2.endkey || "\u9999";
3467
+ params2 ||= {};
3468
+ params2.startkey ||= "";
3469
+ params2.endkey ||= "\u9999";
3156
3470
  const pattern = params2.endkey.substring(0, params2.startkey.length) === params2.startkey ? `${this.objNamespace + params2.startkey}*` : `${this.objNamespace}*`;
3157
3471
  const keys2 = await this._getKeysViaScan(pattern);
3158
3472
  if (!this.client) {
@@ -3181,7 +3495,7 @@ class ObjectsInRedisClient {
3181
3495
  objs2 = await this.client.mget(_keys);
3182
3496
  } catch {
3183
3497
  }
3184
- const result2 = {
3498
+ const result = {
3185
3499
  rows: []
3186
3500
  };
3187
3501
  if (objs2) {
@@ -3194,32 +3508,36 @@ class ObjectsInRedisClient {
3194
3508
  this.log.error(`${this.namespace} Cannot parse JSON ${_keys[i]}: ${objs2[i]}`);
3195
3509
  continue;
3196
3510
  }
3197
- if (!obj || !utils.checkObject(obj, options2, CONSTS.ACCESS_READ)) {
3511
+ if (!obj || !utils.checkObject(obj, userContext2, CONSTS.ACCESS_READ)) {
3198
3512
  continue;
3199
3513
  }
3200
- result2.rows.push({ id: obj._id, value: obj, doc: obj });
3514
+ result.rows.push({ id: obj._id, value: obj, doc: obj });
3201
3515
  }
3202
3516
  }
3203
- return result2;
3517
+ return result;
3204
3518
  }
3205
- getObjectList(params2, options2, callback) {
3206
- if (typeof options2 === "function") {
3207
- callback = options2;
3208
- options2 = null;
3519
+ /**
3520
+ * Get the list of objects matching the given parameters
3521
+ *
3522
+ * @param params Query parameters such as startkey and endkey
3523
+ * @param options The current request options including the user, or the callback
3524
+ * @param callback Called with the matching objects
3525
+ */
3526
+ getObjectList(params2, options, callback) {
3527
+ if (typeof options === "function") {
3528
+ callback = options;
3529
+ options = null;
3209
3530
  }
3210
3531
  if (!callback) {
3211
- return new Promise((resolve, reject) => this.getObjectList(params2, options2, (err, obj) => err ? reject(err) : resolve(obj)));
3212
- }
3213
- if (options2?.acl) {
3214
- options2.acl = null;
3532
+ return new Promise((resolve, reject) => this.getObjectList(params2, options, (err, obj) => err ? reject(err) : resolve(obj)));
3215
3533
  }
3216
3534
  if (typeof callback === "function") {
3217
- utils.checkObjectRights(this, null, null, options2, "list", async (err, options3) => {
3535
+ utils.checkObjectRights(this, null, null, options, "list", async (err, userContext2) => {
3218
3536
  if (err) {
3219
3537
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
3220
3538
  }
3221
3539
  try {
3222
- const res = await this._getObjectList(params2, options3 || {});
3540
+ const res = await this._getObjectList(params2, userContext2);
3223
3541
  return import_db_base.tools.maybeCallbackWithError(callback, null, res);
3224
3542
  } catch (e) {
3225
3543
  return import_db_base.tools.maybeCallbackWithError(callback, e);
@@ -3227,156 +3545,147 @@ class ObjectsInRedisClient {
3227
3545
  });
3228
3546
  }
3229
3547
  }
3230
- getObjectListAsync(params2, options2) {
3231
- return this.getObjectList(params2, options2);
3548
+ /**
3549
+ * Promise-version of getObjectList
3550
+ *
3551
+ * @param params Query parameters such as startkey and endkey
3552
+ * @param options The current request options including the user
3553
+ */
3554
+ getObjectListAsync(params2, options) {
3555
+ return this.getObjectList(params2, options);
3232
3556
  }
3233
- // could be optimized, to read object only once. Now it will read 3 times
3234
- async _extendObject(id, obj, options2, callback) {
3557
+ // could be optimized, to read an object only once. Now it will read 3 times
3558
+ async _extendObject(id, obj, options, userContext2) {
3235
3559
  if (!id || typeof id !== "string" || utils.REG_CHECK_ID.test(id)) {
3236
- return import_db_base.tools.maybeCallbackWithError(callback, `Invalid ID: ${id}`);
3560
+ throw new Error(`Invalid ID: ${id}`);
3237
3561
  }
3238
3562
  if (!this.client) {
3239
- return import_db_base.tools.maybeCallbackWithRedisError(callback, ERRORS.ERROR_DB_CLOSED);
3563
+ throw new Error(ERRORS.ERROR_DB_CLOSED);
3240
3564
  }
3565
+ const oldObjStr = await this.client.get(this.objNamespace + id);
3241
3566
  let oldObj;
3242
3567
  try {
3243
- oldObj = await this.client.get(this.objNamespace + id);
3244
- } catch (e) {
3245
- return import_db_base.tools.maybeCallbackWithRedisError(callback, e);
3246
- }
3247
- try {
3248
- oldObj = oldObj ? JSON.parse(oldObj) : null;
3568
+ oldObj = oldObjStr ? JSON.parse(oldObjStr) : null;
3249
3569
  } catch {
3250
- this.log.error(`${this.namespace} Cannot parse JSON ${id}: ${oldObj}`);
3251
- return import_db_base.tools.maybeCallbackWithError(callback, `Cannot parse JSON ${id}: ${oldObj}`);
3570
+ this.log.error(`${this.namespace} Cannot parse JSON ${id}: ${oldObjStr}`);
3571
+ throw new Error(`Cannot parse JSON ${id}: ${oldObjStr}`);
3252
3572
  }
3253
- if (!utils.checkObject(oldObj, options2, CONSTS.ACCESS_WRITE)) {
3254
- return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION);
3573
+ if (!utils.checkObject(oldObj, userContext2, CONSTS.ACCESS_WRITE)) {
3574
+ throw new Error(ERRORS.ERROR_PERMISSION);
3255
3575
  }
3256
3576
  let _oldObj;
3257
3577
  if (oldObj?.nonEdit) {
3258
3578
  _oldObj = (0, import_deep_clone.default)(oldObj);
3259
3579
  }
3260
3580
  const oldObjHasCustom = !!oldObj?.common?.custom;
3261
- oldObj = oldObj || {};
3262
- obj = (0, import_deep_clone.default)(obj);
3263
- if (oldObj.common && oldObj.common.custom !== void 0 && oldObj.common.custom !== null && !import_db_base.tools.isObject(oldObj.common.custom)) {
3264
- delete oldObj.common.custom;
3265
- }
3266
- const oldType = oldObj.type;
3267
- oldObj = (0, import_node.default)(true, oldObj, obj);
3268
- oldObj._id = id;
3269
- if (this.defaultNewAcl && !oldObj.acl) {
3270
- oldObj.acl = (0, import_deep_clone.default)(this.defaultNewAcl);
3271
- delete oldObj.acl.file;
3272
- if (oldObj.type !== "state") {
3273
- delete oldObj.acl.state;
3274
- }
3275
- if (options2.owner) {
3276
- oldObj.acl.owner = options2.owner;
3277
- if (!options2.ownerGroup) {
3278
- oldObj.acl.ownerGroup = null;
3279
- return void this.getUserGroup(options2.owner, (user, groups) => {
3280
- if (!groups || !groups[0]) {
3281
- options2.ownerGroup = this.defaultNewAcl?.ownerGroup || CONSTS.SYSTEM_ADMIN_GROUP;
3282
- } else {
3283
- options2.ownerGroup = groups[0];
3284
- }
3285
- this._extendObject(id, obj, options2, callback);
3286
- });
3581
+ const objClone = (0, import_deep_clone.default)(obj);
3582
+ let oldObjNotNull = oldObj || {};
3583
+ if (oldObjNotNull.common?.custom !== void 0 && oldObjNotNull.common.custom !== null && !import_db_base.tools.isObject(oldObjNotNull.common.custom)) {
3584
+ delete oldObjNotNull.common.custom;
3585
+ }
3586
+ const oldType = oldObjNotNull.type;
3587
+ oldObjNotNull = (0, import_node.default)(true, oldObjNotNull, objClone);
3588
+ oldObjNotNull._id = id;
3589
+ if (this.defaultNewAcl && !oldObjNotNull.acl) {
3590
+ oldObjNotNull.acl = {
3591
+ owner: this.defaultNewAcl.owner,
3592
+ ownerGroup: this.defaultNewAcl.ownerGroup,
3593
+ object: this.defaultNewAcl.object
3594
+ };
3595
+ if (oldObjNotNull.type === "state") {
3596
+ oldObjNotNull.acl.state = this.defaultNewAcl.state;
3597
+ }
3598
+ if (options.owner) {
3599
+ oldObjNotNull.acl.owner = options.owner;
3600
+ if (!options.ownerGroup) {
3601
+ const [, groups] = await utils.getUserGroupAsync(this, options.owner);
3602
+ if (!groups?.[0]) {
3603
+ options.ownerGroup = this.defaultNewAcl.ownerGroup || CONSTS.SYSTEM_ADMIN_GROUP;
3604
+ } else {
3605
+ options.ownerGroup = groups[0];
3606
+ }
3287
3607
  }
3288
3608
  }
3289
3609
  }
3290
- if (this.defaultNewAcl && options2.ownerGroup && oldObj.acl && !oldObj.acl.ownerGroup) {
3291
- oldObj.acl.ownerGroup = options2.ownerGroup;
3610
+ if (this.defaultNewAcl && options.ownerGroup && oldObjNotNull.acl && !oldObjNotNull.acl.ownerGroup) {
3611
+ oldObjNotNull.acl.ownerGroup = options.ownerGroup;
3292
3612
  }
3293
3613
  if (obj.common && "alias" in obj.common && obj.common.alias.id) {
3294
3614
  if (typeof obj.common.alias.id === "object") {
3295
3615
  if (typeof obj.common.alias.id.write !== "string" || typeof obj.common.alias.id.read !== "string") {
3296
- return import_db_base.tools.maybeCallbackWithError(callback, "Invalid alias ID");
3616
+ throw new Error("Invalid alias ID");
3297
3617
  }
3298
3618
  if (obj.common.alias.id.write.startsWith("alias.") || obj.common.alias.id.read.startsWith("alias.")) {
3299
- return import_db_base.tools.maybeCallbackWithError(callback, "Cannot make alias on alias");
3619
+ throw new Error("Cannot make alias on alias");
3300
3620
  }
3301
3621
  } else {
3302
3622
  if (typeof obj.common.alias.id !== "string") {
3303
- return import_db_base.tools.maybeCallbackWithError(callback, "Invalid alias ID");
3623
+ throw new Error("Invalid alias ID");
3304
3624
  }
3305
3625
  if (obj.common.alias.id.startsWith("alias.")) {
3306
- return import_db_base.tools.maybeCallbackWithError(callback, "Cannot make alias on alias");
3626
+ throw new Error("Cannot make alias on alias");
3307
3627
  }
3308
3628
  }
3309
3629
  }
3310
- if (_oldObj && !import_db_base.tools.checkNonEditable(_oldObj, oldObj)) {
3311
- return import_db_base.tools.maybeCallbackWithError(callback, "Invalid password for update of vendor information");
3630
+ if (_oldObj && !import_db_base.tools.checkNonEditable(_oldObj, oldObjNotNull)) {
3631
+ throw new Error("Invalid password for update of vendor information");
3312
3632
  }
3313
- const message = JSON.stringify(oldObj);
3314
- try {
3315
- const commands = [];
3316
- if (this.useSets) {
3317
- if (oldObj.type && !oldType) {
3318
- commands.push(["sadd", `${this.setNamespace}object.type.${obj.type}`, this.objNamespace + id]);
3319
- } else if (oldObj.type && oldType && oldObj.type !== oldType) {
3320
- commands.push(["sadd", `${this.setNamespace}object.type.${obj.type}`, this.objNamespace + id], ["srem", `${this.setNamespace}object.type.${oldObj.type}`, this.objNamespace + id]);
3321
- } else if (oldType && !oldObj.type) {
3322
- commands.push(["srem", `${this.setNamespace}object.type.${obj.type}`, this.objNamespace + id]);
3323
- }
3324
- }
3325
- if (oldObj.common?.custom && !oldObjHasCustom) {
3326
- commands.push(["sadd", `${this.setNamespace}object.common.custom`, this.objNamespace + id]);
3327
- } else if (oldObjHasCustom && (!oldObj.common || !oldObj.common.custom)) {
3328
- commands.push(["srem", `${this.setNamespace}object.common.custom`, this.objNamespace + id]);
3329
- }
3330
- if (!commands.length) {
3331
- await this.client.set(this.objNamespace + id, message);
3332
- } else {
3333
- commands.push(["set", this.objNamespace + id, message]);
3334
- await this.client.multi(commands).exec();
3335
- }
3336
- if (this.existingMetaObjects[id] === false && oldObj && oldObj.type === "meta") {
3337
- this.existingMetaObjects[id] = true;
3633
+ const message = JSON.stringify(oldObjNotNull);
3634
+ const commands = [];
3635
+ if (this.useSets) {
3636
+ if (oldObjNotNull.type && !oldType) {
3637
+ commands.push(["sadd", `${this.setNamespace}object.type.${obj.type}`, this.objNamespace + id]);
3638
+ } else if (oldObjNotNull.type && oldType && oldObjNotNull.type !== oldType) {
3639
+ commands.push(["sadd", `${this.setNamespace}object.type.${obj.type}`, this.objNamespace + id], ["srem", `${this.setNamespace}object.type.${oldObjNotNull.type}`, this.objNamespace + id]);
3640
+ } else if (oldType && !oldObjNotNull.type) {
3641
+ commands.push(["srem", `${this.setNamespace}object.type.${obj.type}`, this.objNamespace + id]);
3338
3642
  }
3339
- await this.client.publish(this.objNamespace + id, message);
3340
- return import_db_base.tools.maybeCallbackWithError(callback, null, { id, value: oldObj }, id);
3341
- } catch (e) {
3342
- return import_db_base.tools.maybeCallbackWithRedisError(callback, e);
3343
3643
  }
3344
- }
3345
- extendObject(id, obj, options2, callback) {
3346
- if (typeof options2 === "function") {
3347
- callback = options2;
3348
- options2 = null;
3644
+ if (oldObjNotNull.common?.custom && !oldObjHasCustom) {
3645
+ commands.push(["sadd", `${this.setNamespace}object.common.custom`, this.objNamespace + id]);
3646
+ } else if (oldObjHasCustom && (!oldObjNotNull.common || !oldObjNotNull.common.custom)) {
3647
+ commands.push(["srem", `${this.setNamespace}object.common.custom`, this.objNamespace + id]);
3349
3648
  }
3350
- if (!callback) {
3351
- return new Promise((resolve, reject) => this.extendObject(id, obj, options2, (err, res) => err ? reject(err) : resolve(res)));
3649
+ if (!commands.length) {
3650
+ await this.client.set(this.objNamespace + id, message);
3651
+ } else {
3652
+ commands.push(["set", this.objNamespace + id, message]);
3653
+ await this.client.multi(commands).exec();
3352
3654
  }
3353
- if (options2?.acl) {
3354
- options2.acl = null;
3655
+ if (this.existingMetaObjects[id] === false && oldObjNotNull?.type === "meta") {
3656
+ this.existingMetaObjects[id] = true;
3355
3657
  }
3356
- utils.checkObjectRights(this, null, null, options2, CONSTS.ACCESS_WRITE, (err, options3) => {
3357
- if (err) {
3358
- return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
3359
- }
3360
- return this._extendObject(id, obj, options3, callback);
3361
- });
3658
+ await this.client.publish(this.objNamespace + id, message);
3659
+ return { id, value: oldObjNotNull };
3362
3660
  }
3363
- extendObjectAsync(id, obj, options2) {
3364
- return new Promise((resolve, reject) => this.extendObject(id, obj, options2 || null, (err, res) => err ? reject(err) : resolve(res)));
3661
+ /**
3662
+ * Extend an existing object with the given partial object, creating it if it does not exist
3663
+ *
3664
+ * @param id The id of the object to extend
3665
+ * @param obj The partial object to merge into the existing object
3666
+ * @param options The current request options including the user, or the callback
3667
+ */
3668
+ async extendObject(id, obj, options) {
3669
+ const userContext2 = await (0, import_objectsUtils.checkObjectRightsAsync)(this, null, null, options, CONSTS.ACCESS_WRITE);
3670
+ return this._extendObject(id, obj, options || {}, userContext2);
3365
3671
  }
3366
3672
  /**
3367
3673
  * Returns the object id if found
3368
3674
  *
3369
- * @param idOrName
3370
- * @param type
3371
- * @param options
3372
- * @param callback
3675
+ * @param idOrName The id or name to search for
3676
+ * @param type The expected common type, or null for any
3677
+ * @param options The current request options (may include a language)
3678
+ * @param options.language The language to use when resolving names
3679
+ * @param options.user The user on whose behalf the lookup is performed
3680
+ * @param userContext The resolved user context to check the rights against
3681
+ * @param callback Called with the found id and the original id/name
3373
3682
  */
3374
- _findObject(idOrName, type, options2, callback) {
3375
- this._getObject(idOrName, options2, (err, obj) => {
3376
- if (obj && utils.checkObject(obj, options2, CONSTS.ACCESS_READ) && (!type || obj.common?.type === type)) {
3683
+ _findObject(idOrName, type, options, userContext2, callback) {
3684
+ this._getObject(idOrName, userContext2, (err, obj) => {
3685
+ if (obj && utils.checkObject(obj, userContext2, CONSTS.ACCESS_READ) && (!type || obj.common?.type === type)) {
3377
3686
  return import_db_base.tools.maybeCallbackWithError(callback, null, idOrName, obj.common.name);
3378
3687
  }
3379
- this._getKeys("*", options2, async (err2, keys2) => {
3688
+ return this._getKeys("*", userContext2, true, async (err2, keys2) => {
3380
3689
  if (!this.client) {
3381
3690
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_DB_CLOSED);
3382
3691
  }
@@ -3402,7 +3711,7 @@ class ObjectsInRedisClient {
3402
3711
  if (obj2?.common && (!type || "type" in obj2.common && obj2.common.type === type)) {
3403
3712
  let name = obj2?.common?.name;
3404
3713
  if (name && typeof name === "object") {
3405
- name = name[options2.language || "en"] || name.en;
3714
+ name = name[options.language || "en"] || name.en;
3406
3715
  }
3407
3716
  if (name === idOrName) {
3408
3717
  return import_db_base.tools.maybeCallbackWithError(callback, null, obj2._id, obj2.common.name);
@@ -3410,35 +3719,45 @@ class ObjectsInRedisClient {
3410
3719
  }
3411
3720
  }
3412
3721
  return import_db_base.tools.maybeCallbackWithError(callback, null, void 0, idOrName);
3413
- }, true);
3414
- });
3722
+ });
3723
+ }).catch((e) => this.log.error(`${this.namespace} Cannot find object: ${e.message}`));
3415
3724
  }
3416
- findObject(idOrName, type, options2, callback) {
3725
+ /**
3726
+ * Find an object by its id or name
3727
+ *
3728
+ * @param idOrName The id or name to search for
3729
+ * @param type The expected common type, or null for any
3730
+ * @param options The current request options (may include a language), or the callback
3731
+ * @param callback Called with the found id and the original id/name
3732
+ */
3733
+ findObject(idOrName, type, options, callback) {
3417
3734
  if (typeof type === "function") {
3418
3735
  callback = type;
3419
- options2 = null;
3736
+ options = null;
3420
3737
  type = null;
3421
3738
  }
3422
- if (typeof options2 === "function") {
3423
- callback = options2;
3424
- options2 = null;
3739
+ if (typeof options === "function") {
3740
+ callback = options;
3741
+ options = null;
3425
3742
  }
3426
3743
  if (!callback) {
3427
- return new Promise((resolve, reject) => this.findObject(idOrName, type, options2, (err, id, _idOrName) => err ? reject(err) : resolve(id)));
3428
- }
3429
- if (options2?.acl) {
3430
- options2.acl = null;
3744
+ return new Promise((resolve, reject) => this.findObject(idOrName, type, options, (err, id, _idOrName) => err ? reject(err) : resolve(id)));
3431
3745
  }
3432
3746
  if (typeof callback === "function") {
3433
- utils.checkObjectRights(this, null, null, options2, CONSTS.ACCESS_LIST, (err, options3) => {
3747
+ utils.checkObjectRights(this, null, null, options, CONSTS.ACCESS_LIST, (err, userContext2) => {
3434
3748
  if (err) {
3435
3749
  return import_db_base.tools.maybeCallbackWithError(callback, err);
3436
3750
  }
3437
- return this._findObject(idOrName, type, options3, callback);
3751
+ return this._findObject(idOrName, type, options, userContext2, callback);
3438
3752
  });
3439
3753
  }
3440
3754
  }
3441
3755
  // can be called only from js-controller
3756
+ /**
3757
+ * Add object property paths that should be preserved when an object is overwritten (controller only)
3758
+ *
3759
+ * @param settings One or more property paths to preserve
3760
+ */
3442
3761
  addPreserveSettings(settings) {
3443
3762
  if (!Array.isArray(settings)) {
3444
3763
  settings = [settings];
@@ -3475,26 +3794,40 @@ class ObjectsInRedisClient {
3475
3794
  return import_db_base.tools.maybeCallbackWithRedisError(callback, e);
3476
3795
  }
3477
3796
  }
3478
- destroyDB(options2, callback) {
3479
- if (typeof options2 === "function") {
3480
- callback = options2;
3481
- options2 = null;
3797
+ /**
3798
+ * Delete the whole objects database (requires admin rights)
3799
+ *
3800
+ * @param options The current request options including the user, or the callback
3801
+ * @param callback Called once the database has been destroyed
3802
+ */
3803
+ destroyDB(options, callback) {
3804
+ if (typeof options === "function") {
3805
+ callback = options;
3806
+ options = null;
3482
3807
  }
3483
- options2 = options2 || {};
3484
- utils.checkObjectRights(this, null, null, options2, CONSTS.ACCESS_WRITE, (err, options3) => {
3808
+ options = options || {};
3809
+ utils.checkObjectRights(this, null, null, options, CONSTS.ACCESS_WRITE, (err, userContext2) => {
3485
3810
  if (err) {
3486
3811
  return import_db_base.tools.maybeCallbackWithRedisError(callback, err);
3487
3812
  }
3488
- if (!options3.acl.file.write || options3.user !== CONSTS.SYSTEM_ADMIN_USER) {
3813
+ if (!userContext2.acl.file.write || userContext2.user !== CONSTS.SYSTEM_ADMIN_USER) {
3489
3814
  return import_db_base.tools.maybeCallbackWithError(callback, ERRORS.ERROR_PERMISSION);
3490
3815
  }
3491
3816
  return this._destroyDB(callback);
3492
3817
  });
3493
3818
  }
3494
- destroyDBAsync(options2) {
3495
- return new Promise((resolve, reject) => this.destroyDB(options2, (err) => err ? reject(err) : resolve()));
3819
+ /**
3820
+ * Promise-version of destroyDB
3821
+ *
3822
+ * @param options The current request options including the user
3823
+ * @param options.user The user on whose behalf the database is destroyed
3824
+ */
3825
+ destroyDBAsync(options) {
3826
+ return new Promise((resolve, reject) => this.destroyDB(options, (err) => err ? reject(err) : resolve()));
3496
3827
  }
3497
- // Destructor of the class. Called by shutting down.
3828
+ /**
3829
+ * Destructor of the class. Called when shutting down to close the redis connections.
3830
+ */
3498
3831
  async destroy() {
3499
3832
  this.stop = true;
3500
3833
  if (this.client) {
@@ -3522,6 +3855,9 @@ class ObjectsInRedisClient {
3522
3855
  }
3523
3856
  }
3524
3857
  }
3858
+ /**
3859
+ * Load and register the Lua scripts used for atomic operations on the redis server
3860
+ */
3525
3861
  async loadLuaScripts() {
3526
3862
  let luaDirName;
3527
3863
  if (this.noLegacyMultihost && this.useSets) {
@@ -3540,15 +3876,13 @@ class ObjectsInRedisClient {
3540
3876
  return { name: name.replace(/\.lua$/, ""), text: script, hash };
3541
3877
  });
3542
3878
  const hashes = scripts.map((e) => e.hash);
3543
- hashes.unshift("EXISTS");
3544
3879
  if (!this.client) {
3545
3880
  throw new Error(import_db_base.tools.ERRORS.ERROR_DB_CLOSED);
3546
3881
  }
3547
- let arr;
3548
3882
  try {
3549
- arr = await this.client.script(hashes);
3883
+ const arr = await this.client.script("EXISTS", ...hashes);
3550
3884
  if (arr) {
3551
- scripts.forEach((e, i) => scripts[i].loaded = !!arr[i]);
3885
+ scripts.forEach((_e, i) => scripts[i].loaded = !!arr[i]);
3552
3886
  }
3553
3887
  } catch {
3554
3888
  }
@@ -3559,7 +3893,7 @@ class ObjectsInRedisClient {
3559
3893
  if (!script.loaded) {
3560
3894
  let hash;
3561
3895
  try {
3562
- hash = await this.client.script(["LOAD", script.text]);
3896
+ hash = await this.client.script("LOAD", script.text);
3563
3897
  script.loaded = true;
3564
3898
  } catch (e) {
3565
3899
  script.loaded = false;
@@ -3568,16 +3902,16 @@ class ObjectsInRedisClient {
3568
3902
  throw new Error(`Cannot load "${script.name}" into objects database: ${e.message}`);
3569
3903
  }
3570
3904
  }
3571
- script.hash = hash;
3905
+ script.hash = hash || "";
3572
3906
  }
3573
3907
  }
3574
3908
  this.scripts = {};
3575
3909
  scripts.forEach((e) => this.scripts[e.name] = e.hash);
3576
3910
  }
3577
3911
  /**
3578
- * Get all keys matching a pattern using redis SCAN command, duplicates are filtered out
3912
+ * Get all keys matching a pattern using redis SCAN command; duplicates are filtered out
3579
3913
  *
3580
- * @param pattern - pattern to match, e. g. io.hm-rpc.0*
3914
+ * @param pattern - pattern to match, e.g. io.hm-rpc.0*
3581
3915
  * @param count - count argument used by redis SCAN, default is 250
3582
3916
  */
3583
3917
  _getKeysViaScan(pattern, count = 250) {
@@ -3597,6 +3931,7 @@ class ObjectsInRedisClient {
3597
3931
  stream.on("end", () => {
3598
3932
  resolve(Array.from(new Set(uniqueKeys)));
3599
3933
  });
3934
+ stream.on("error", reject);
3600
3935
  });
3601
3936
  }
3602
3937
  /**
@@ -3667,17 +4002,11 @@ class ObjectsInRedisClient {
3667
4002
  if (!this.noLegacyMultihost) {
3668
4003
  return Promise.resolve(0);
3669
4004
  }
3670
- return this.client.evalsha([
3671
- this.scripts.redlock_extend,
3672
- 3,
3673
- `${this.metaNamespace}objects.primaryHost`,
3674
- this.hostname,
3675
- ms
3676
- ]);
4005
+ return this.client.evalsha(this.scripts.redlock_extend, 3, `${this.metaNamespace}objects.primaryHost`, this.hostname, ms);
3677
4006
  }
3678
4007
  /**
3679
- * Sets current host as primary if no primary host active
3680
- * Value will expire after ms milliseconds
4008
+ * Sets the current host as primary if no primary host active
4009
+ * Value expires after ms milliseconds
3681
4010
  *
3682
4011
  * @param ms - ms until value expires
3683
4012
  * @returns 1 if lock acquired else 0
@@ -3689,16 +4018,10 @@ class ObjectsInRedisClient {
3689
4018
  if (!this.noLegacyMultihost) {
3690
4019
  return Promise.resolve(0);
3691
4020
  }
3692
- return this.client.evalsha([
3693
- this.scripts.redlock_acquire,
3694
- 3,
3695
- `${this.metaNamespace}objects.primaryHost`,
3696
- this.hostname,
3697
- ms
3698
- ]);
4021
+ return this.client.evalsha(this.scripts.redlock_acquire, 3, `${this.metaNamespace}objects.primaryHost`, this.hostname, ms);
3699
4022
  }
3700
4023
  /**
3701
- * Get name of the primary host
4024
+ * Get the name of the primary host
3702
4025
  */
3703
4026
  getPrimaryHost() {
3704
4027
  if (!this.client) {
@@ -3719,14 +4042,7 @@ class ObjectsInRedisClient {
3719
4042
  if (!this.noLegacyMultihost) {
3720
4043
  return Promise.resolve();
3721
4044
  }
3722
- return this.client.evalsha([
3723
- this.scripts.redlock_release,
3724
- 4,
3725
- `${this.metaNamespace}objects.primaryHost`,
3726
- this.hostname,
3727
- this.settings.connection.options.db,
3728
- `${this.metaNamespace}objects.primaryHost`
3729
- ]);
4045
+ return this.client.evalsha(this.scripts.redlock_release, 4, `${this.metaNamespace}objects.primaryHost`, this.hostname, this.settings.connection.options.db || 0, `${this.metaNamespace}objects.primaryHost`);
3730
4046
  }
3731
4047
  /**
3732
4048
  * Sets the protocol version to the DB
@@ -3746,7 +4062,7 @@ class ObjectsInRedisClient {
3746
4062
  }
3747
4063
  }
3748
4064
  /**
3749
- * Subscribe to expired events to get expiration of primary host
4065
+ * Subscribe to expired events to get expiration of the primary host
3750
4066
  */
3751
4067
  async subscribePrimaryHost() {
3752
4068
  if (this.subSystem) {