@iobroker/socket-classes 2.6.2 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var _a;
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
4
|
exports.SocketCommands = exports.COMMANDS_PERMISSIONS = void 0;
|
|
4
5
|
const adapter_core_1 = require("@iobroker/adapter-core"); // Get common adapter utils
|
|
6
|
+
/**
|
|
7
|
+
* The events that are waiting for the database to say whether their socket may see them.
|
|
8
|
+
*
|
|
9
|
+
* Kept per connection and per event, and only ever the **latest** one: while the first event of an id
|
|
10
|
+
* waits for its answer, a newer value of the same state would otherwise overtake it and the client
|
|
11
|
+
* would end up with the older one. What is kept is how to deliver it - the event goes through
|
|
12
|
+
* `publish`/`publishFile` again, so a subscription that was given up in the meantime still counts. It
|
|
13
|
+
* lives in a `WeakMap`, so a connection that goes away takes its waiting events with it.
|
|
14
|
+
*/
|
|
15
|
+
const pendingEvents = new WeakMap();
|
|
5
16
|
exports.COMMANDS_PERMISSIONS = {
|
|
6
17
|
getObject: { type: 'object', operation: 'read' },
|
|
7
18
|
getObjects: { type: 'object', operation: 'list' },
|
|
@@ -70,6 +81,21 @@ class SocketCommands {
|
|
|
70
81
|
subscribes = {};
|
|
71
82
|
#logEnabled = false;
|
|
72
83
|
#clientSubscribes = {};
|
|
84
|
+
/**
|
|
85
|
+
* What each user may read, by object id. Filled while the events flow, emptied where an object -
|
|
86
|
+
* or the rights themselves - change. Per user, because every browser tab of one person would
|
|
87
|
+
* otherwise ask the same question again.
|
|
88
|
+
*/
|
|
89
|
+
#readable = new Map();
|
|
90
|
+
/** Questions that are with the database right now, so the same id is asked only once at a time */
|
|
91
|
+
#deciding = new Map();
|
|
92
|
+
/**
|
|
93
|
+
* Counts how often the decisions were thrown away. An answer that was asked for before the last
|
|
94
|
+
* change arrives too late to be trusted and is dropped instead of being remembered.
|
|
95
|
+
*/
|
|
96
|
+
#readableGeneration = 0;
|
|
97
|
+
/** Whether this instance watches the objects for the sake of its own decisions - see `#rememberDecision` */
|
|
98
|
+
#watchingObjects = false;
|
|
73
99
|
#updateSession;
|
|
74
100
|
adapterName;
|
|
75
101
|
_sendToHost;
|
|
@@ -196,41 +222,41 @@ class SocketCommands {
|
|
|
196
222
|
if (socket._acl?.user !== 'system.user.admin') {
|
|
197
223
|
// type: file, object, state, other
|
|
198
224
|
// operation: create, read, write, list, delete, sendto, execute, sendToHost, readLogs
|
|
199
|
-
if (
|
|
225
|
+
if (_a.COMMANDS_PERMISSIONS[_command]) {
|
|
200
226
|
// If permission required
|
|
201
|
-
const commandType =
|
|
227
|
+
const commandType = _a.COMMANDS_PERMISSIONS[_command].type;
|
|
202
228
|
if (commandType) {
|
|
203
229
|
if (commandType === 'object') {
|
|
204
|
-
const operation =
|
|
230
|
+
const operation = _a.COMMANDS_PERMISSIONS[_command].operation;
|
|
205
231
|
if (socket._acl?.object?.[operation]) {
|
|
206
232
|
return true;
|
|
207
233
|
}
|
|
208
234
|
}
|
|
209
235
|
else if (commandType === 'state') {
|
|
210
|
-
const operation =
|
|
236
|
+
const operation = _a.COMMANDS_PERMISSIONS[_command].operation;
|
|
211
237
|
if (socket._acl?.state?.[operation]) {
|
|
212
238
|
return true;
|
|
213
239
|
}
|
|
214
240
|
}
|
|
215
241
|
else if (commandType === 'users') {
|
|
216
|
-
const operation =
|
|
242
|
+
const operation = _a.COMMANDS_PERMISSIONS[_command].operation;
|
|
217
243
|
if (socket._acl?.users?.[operation]) {
|
|
218
244
|
return true;
|
|
219
245
|
}
|
|
220
246
|
}
|
|
221
247
|
else if (commandType === 'other') {
|
|
222
|
-
const operation =
|
|
248
|
+
const operation = _a.COMMANDS_PERMISSIONS[_command].operation;
|
|
223
249
|
if (socket._acl?.other?.[operation]) {
|
|
224
250
|
return true;
|
|
225
251
|
}
|
|
226
252
|
}
|
|
227
253
|
else if (commandType === 'file') {
|
|
228
|
-
const operation =
|
|
254
|
+
const operation = _a.COMMANDS_PERMISSIONS[_command].operation;
|
|
229
255
|
if (socket._acl?.file?.[operation]) {
|
|
230
256
|
return true;
|
|
231
257
|
}
|
|
232
258
|
}
|
|
233
|
-
this.adapter.log.warn(`No permission for "${socket._acl?.user}" to call ${_command}. Need "${commandType}"."${
|
|
259
|
+
this.adapter.log.warn(`No permission for "${socket._acl?.user}" to call ${_command}. Need "${commandType}"."${_a.COMMANDS_PERMISSIONS[_command].operation}"`);
|
|
234
260
|
}
|
|
235
261
|
else {
|
|
236
262
|
return true;
|
|
@@ -240,29 +266,269 @@ class SocketCommands {
|
|
|
240
266
|
this.adapter.log.warn(`No rule for command: ${_command}`);
|
|
241
267
|
}
|
|
242
268
|
if (typeof callback === 'function') {
|
|
243
|
-
callback(
|
|
269
|
+
callback(_a.ERROR_PERMISSION);
|
|
244
270
|
}
|
|
245
271
|
else {
|
|
246
|
-
if (
|
|
247
|
-
socket.emit(
|
|
272
|
+
if (_a.COMMANDS_PERMISSIONS[_command]) {
|
|
273
|
+
socket.emit(_a.ERROR_PERMISSION, {
|
|
248
274
|
command,
|
|
249
|
-
type:
|
|
250
|
-
operation:
|
|
275
|
+
type: _a.COMMANDS_PERMISSIONS[_command].type,
|
|
276
|
+
operation: _a.COMMANDS_PERMISSIONS[_command].operation,
|
|
251
277
|
args,
|
|
252
278
|
});
|
|
253
279
|
}
|
|
254
280
|
else {
|
|
255
|
-
socket.emit(
|
|
281
|
+
socket.emit(_a.ERROR_PERMISSION, { command: _command, args });
|
|
256
282
|
}
|
|
257
283
|
}
|
|
258
284
|
return false;
|
|
259
285
|
}
|
|
260
286
|
return true;
|
|
261
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Whether this connection belongs to somebody who may see everything.
|
|
290
|
+
*
|
|
291
|
+
* The administrator group is the whole point of the administrator group, and with authentication
|
|
292
|
+
* switched off every connection is the configured default user - usually exactly that one. So this
|
|
293
|
+
* is the answer for almost every connection there is, and it costs a look at an ACL that the
|
|
294
|
+
* socket layer calculated when the connection was opened.
|
|
295
|
+
*
|
|
296
|
+
* @param socket the connection in question
|
|
297
|
+
*/
|
|
298
|
+
static #seesEverything(socket) {
|
|
299
|
+
const acl = socket?._acl;
|
|
300
|
+
return acl?.user === 'system.user.admin' || !!acl?.groups?.includes('system.group.administrator');
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Whether the user of this connection may read `id`, as far as it is already known.
|
|
304
|
+
*
|
|
305
|
+
* `undefined` means "not decided yet" - the caller has to let `#decideReadable` ask the database
|
|
306
|
+
* and come back. Decisions are kept per user, not per connection: three browser tabs of the same
|
|
307
|
+
* person ask once.
|
|
308
|
+
*
|
|
309
|
+
* @param socket the connection the event would go to
|
|
310
|
+
* @param question what the event is about
|
|
311
|
+
*/
|
|
312
|
+
#mayRead(socket, question) {
|
|
313
|
+
const user = socket?._acl?.user;
|
|
314
|
+
if (!user) {
|
|
315
|
+
// a connection without a user has no rights at all, not even to be told that `id` exists
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
if (_a.#seesEverything(socket)) {
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
return this.#readable.get(user)?.get(_a.#questionKey(question));
|
|
322
|
+
}
|
|
323
|
+
/** What one question is remembered under: the kind, the id, and for a file its name */
|
|
324
|
+
static #questionKey(question) {
|
|
325
|
+
return question.fileName
|
|
326
|
+
? `${question.type}####${question.id}####${question.fileName}`
|
|
327
|
+
: `${question.type}####${question.id}`;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Ask the database whether the user may read `id`, and remember the answer.
|
|
331
|
+
*
|
|
332
|
+
* The database owns this decision - the ACL of the object, its owner, the groups of the user and
|
|
333
|
+
* the default ACL of the system all go into it, and a second implementation here would drift away
|
|
334
|
+
* from it sooner or later. Everybody who is waiting for the same id is answered together.
|
|
335
|
+
*
|
|
336
|
+
* @param socket the connection the event would go to
|
|
337
|
+
* @param question what the event is about
|
|
338
|
+
* @param andThen what to do once it is decided, with the decision
|
|
339
|
+
*/
|
|
340
|
+
#decideReadable(socket, question, andThen) {
|
|
341
|
+
// a connection without a user never gets this far: `#mayRead` refuses it outright
|
|
342
|
+
const user = socket._acl.user;
|
|
343
|
+
const questionKey = _a.#questionKey(question);
|
|
344
|
+
const key = `${user}####${questionKey}`;
|
|
345
|
+
const waiting = this.#deciding.get(key);
|
|
346
|
+
if (waiting) {
|
|
347
|
+
waiting.push(andThen);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
this.#deciding.set(key, [andThen]);
|
|
351
|
+
const generation = this.#readableGeneration;
|
|
352
|
+
const answer = (allowed) => {
|
|
353
|
+
if (generation === this.#readableGeneration) {
|
|
354
|
+
this.#rememberDecision(user, questionKey, allowed);
|
|
355
|
+
}
|
|
356
|
+
/*
|
|
357
|
+
* An answer that is too old to be remembered still answers the events that waited for it:
|
|
358
|
+
* a "no" drops them, a "yes" sends them back through `publish`, which - with nothing in
|
|
359
|
+
* memory - asks again under the rights as they are now.
|
|
360
|
+
*/
|
|
361
|
+
const callbacks = this.#deciding.get(key) || [];
|
|
362
|
+
this.#deciding.delete(key);
|
|
363
|
+
for (const callback of callbacks) {
|
|
364
|
+
callback(allowed);
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
const mayRead = this.adapter.mayRead;
|
|
368
|
+
if (mayRead) {
|
|
369
|
+
/*
|
|
370
|
+
* The controller answers this itself from js-controller 8.0 on, and it is the only place
|
|
371
|
+
* that knows the whole truth: the ACL of a state rather than of its object, the mode of a
|
|
372
|
+
* single file rather than of the adapter it belongs to.
|
|
373
|
+
*/
|
|
374
|
+
mayRead.call(this.adapter, { ...question, user }).then(answer, (e) => {
|
|
375
|
+
this.adapter.log.warn(`Cannot check what "${user}" may read of "${question.id}": ${e.message}`);
|
|
376
|
+
answer(false);
|
|
377
|
+
});
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
/*
|
|
381
|
+
* An older controller cannot be asked, so the object has to stand in for all three. It is
|
|
382
|
+
* coarser - the right on the object instead of the one on the state, the adapter instead of
|
|
383
|
+
* the single file - but it errs the same way: only a refusal of the database counts as "no".
|
|
384
|
+
* That an object does not exist is not a refusal, and `mayRead` of a newer controller says the
|
|
385
|
+
* same, because such a state can be read by anybody.
|
|
386
|
+
*/
|
|
387
|
+
// the callback form also returns a promise, which nobody here waits for
|
|
388
|
+
void this.adapter.getForeignObject(question.id, { user }, (error) => answer(!error));
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Keep one decision, and make sure the object changes that would invalidate it arrive here.
|
|
392
|
+
*
|
|
393
|
+
* Nothing of this is needed as long as everybody who is connected sees everything anyway, which is
|
|
394
|
+
* the normal case - so the subscription is taken out where the first decision is made and not
|
|
395
|
+
* before. The events themselves reach `publish` the way every other object change does.
|
|
396
|
+
*
|
|
397
|
+
* @param user whose decision it is
|
|
398
|
+
* @param questionKey what was asked - the kind, the id and for a file its name
|
|
399
|
+
* @param allowed what was decided
|
|
400
|
+
*/
|
|
401
|
+
#rememberDecision(user, questionKey, allowed) {
|
|
402
|
+
let decisions = this.#readable.get(user);
|
|
403
|
+
if (!decisions) {
|
|
404
|
+
decisions = new Map();
|
|
405
|
+
this.#readable.set(user, decisions);
|
|
406
|
+
}
|
|
407
|
+
decisions.set(questionKey, allowed);
|
|
408
|
+
if (!this.#watchingObjects) {
|
|
409
|
+
this.#watchingObjects = true;
|
|
410
|
+
/*
|
|
411
|
+
* The ACL of an object can change at any time, and without this the decision above would
|
|
412
|
+
* be kept until the connection goes away. The watch counts as one more subscriber of `*`:
|
|
413
|
+
* a client that subscribed to all objects and gives them up again would otherwise take
|
|
414
|
+
* the subscription of the database away from under it.
|
|
415
|
+
*/
|
|
416
|
+
this.subscribes.objectChange ||= {};
|
|
417
|
+
if (this.subscribes.objectChange['*'] === undefined) {
|
|
418
|
+
this.subscribes.objectChange['*'] = 1;
|
|
419
|
+
this.adapter
|
|
420
|
+
.subscribeForeignObjectsAsync('*')
|
|
421
|
+
.catch(e => this.adapter.log.warn(`Cannot watch the objects for the permissions of the clients: ${e.message}`));
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
this.subscribes.objectChange['*']++;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/** Forget what was decided about `id`, because the object - and with it its ACL - changed. */
|
|
429
|
+
#forgetReadable(id) {
|
|
430
|
+
this.#readableGeneration++;
|
|
431
|
+
if (id.startsWith('system.user.') || id.startsWith('system.group.') || id === 'system.config') {
|
|
432
|
+
// the rights themselves moved, so nothing that was decided with them still counts
|
|
433
|
+
this.#readable.clear();
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
for (const decisions of this.#readable.values()) {
|
|
437
|
+
// everything that was decided about this id, whatever was asked about it
|
|
438
|
+
for (const questionKey of decisions.keys()) {
|
|
439
|
+
if (questionKey.endsWith(`####${id}`) || questionKey.includes(`####${id}####`)) {
|
|
440
|
+
decisions.delete(questionKey);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Hold one event until it is decided whether its connection may see it.
|
|
447
|
+
*
|
|
448
|
+
* Only the latest event of the same kind and id waits; a newer one replaces it, so nothing can
|
|
449
|
+
* overtake it. The question is asked once - every further event of that id joins the one that is
|
|
450
|
+
* already open.
|
|
451
|
+
*
|
|
452
|
+
* @param socket the connection the event would go to
|
|
453
|
+
* @param question what the decision is about
|
|
454
|
+
* @param key what identifies this event - its kind and what it is about
|
|
455
|
+
* @param deliver how to send it once it is decided
|
|
456
|
+
*/
|
|
457
|
+
#holdUntilDecided(socket, question, key, deliver) {
|
|
458
|
+
let waiting = pendingEvents.get(socket);
|
|
459
|
+
if (!waiting) {
|
|
460
|
+
waiting = new Map();
|
|
461
|
+
pendingEvents.set(socket, waiting);
|
|
462
|
+
}
|
|
463
|
+
const first = !waiting.has(key);
|
|
464
|
+
waiting.set(key, deliver);
|
|
465
|
+
if (first) {
|
|
466
|
+
this.#decideReadable(socket, question, allowed => this.#flushPending(socket, key, allowed));
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Send the event that waited for a decision, if it may go out after all.
|
|
471
|
+
*
|
|
472
|
+
* What is sent is whatever arrived last while the question was open - a client that subscribes to
|
|
473
|
+
* a state wants its value, not the one it had a moment ago. It goes through `publish`/`publishFile`
|
|
474
|
+
* again: the subscription may have been given up in the meantime. Usually the decision is in memory
|
|
475
|
+
* by then and the event goes out; only where the object changed while the question was open was
|
|
476
|
+
* the answer not remembered, and the event waits once more for a fresh one.
|
|
477
|
+
*
|
|
478
|
+
* @param socket the connection that waited
|
|
479
|
+
* @param key what identifies the event
|
|
480
|
+
* @param allowed what was decided
|
|
481
|
+
*/
|
|
482
|
+
#flushPending(socket, key, allowed) {
|
|
483
|
+
const waiting = pendingEvents.get(socket);
|
|
484
|
+
const deliver = waiting?.get(key);
|
|
485
|
+
if (!deliver || !waiting) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
waiting.delete(key);
|
|
489
|
+
if (allowed) {
|
|
490
|
+
deliver();
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Give one event to one connection, if it is subscribed to it and may see it.
|
|
495
|
+
*
|
|
496
|
+
* Returns whether the event is on its way to this client: `true` where it was sent, and also
|
|
497
|
+
* where it is waiting for the database to decide whether this user may read the object - the
|
|
498
|
+
* answer to that is not worth holding up every other connection for. `false` means the client is
|
|
499
|
+
* not subscribed to it, or may not see it.
|
|
500
|
+
*
|
|
501
|
+
* @param socket the connection
|
|
502
|
+
* @param type what kind of event it is
|
|
503
|
+
* @param id the object it is about
|
|
504
|
+
* @param obj the state or object as it is sent to the client
|
|
505
|
+
*/
|
|
262
506
|
publish(socket, type, id, obj) {
|
|
507
|
+
if (type === 'objectChange') {
|
|
508
|
+
// the ACL of the object travelled with it, so what was decided about it is out of date
|
|
509
|
+
this.#forgetReadable(id);
|
|
510
|
+
}
|
|
263
511
|
if (socket?.subscribe?.[type] && this.#updateSession(socket)) {
|
|
264
512
|
return !!socket.subscribe[type].find(sub => {
|
|
265
513
|
if (sub.regex.test(id)) {
|
|
514
|
+
/*
|
|
515
|
+
* Who may know that this exists? A subscription says which ids a client is
|
|
516
|
+
* interested in, never which ids it may see - `subscribe` only asks whether the
|
|
517
|
+
* user may read states at all, so whoever subscribed to `*` was told about every
|
|
518
|
+
* state of the system, the ACL of the single objects notwithstanding.
|
|
519
|
+
*/
|
|
520
|
+
const question = { type: type === 'objectChange' ? 'object' : 'state', id };
|
|
521
|
+
const allowed = this.#mayRead(socket, question);
|
|
522
|
+
if (allowed === false) {
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
if (allowed === undefined) {
|
|
526
|
+
// the first event of an id for this user waits for the answer instead of
|
|
527
|
+
// being guessed at
|
|
528
|
+
this.#holdUntilDecided(socket, question, `${type}####${id}`, () => this.publish(socket, type, id, obj));
|
|
529
|
+
// on its way: either it goes out in a moment, or the user may not see it
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
266
532
|
// replace language
|
|
267
533
|
if (this.context.language &&
|
|
268
534
|
id === 'system.config' &&
|
|
@@ -276,11 +542,36 @@ class SocketCommands {
|
|
|
276
542
|
}
|
|
277
543
|
return false;
|
|
278
544
|
}
|
|
545
|
+
/**
|
|
546
|
+
* Give one file event to one connection, if it is subscribed to it and may see it.
|
|
547
|
+
*
|
|
548
|
+
* The decision is taken per file and user. Where the controller has `mayRead`, it answers for the
|
|
549
|
+
* single file, with its own owner and mode as `chownFile` and `chmodFile` set them. An older
|
|
550
|
+
* controller cannot be asked about a file, so the meta object the files belong to - `vis.0` for
|
|
551
|
+
* every file of vis - stands in for it: still one question per file, but every one of them about
|
|
552
|
+
* the adapter. That also works where the file the event is about has just been deleted.
|
|
553
|
+
*
|
|
554
|
+
* The answer means the same as in {@link publish}: `true` where the event is on its way.
|
|
555
|
+
*
|
|
556
|
+
* @param socket the connection
|
|
557
|
+
* @param id the adapter the file belongs to, e.g. `vis.0`
|
|
558
|
+
* @param fileName the path of the file inside it
|
|
559
|
+
* @param size how big it is now, or null where it is gone
|
|
560
|
+
*/
|
|
279
561
|
publishFile(socket, id, fileName, size) {
|
|
280
562
|
if (socket?.subscribe?.fileChange && this.#updateSession(socket)) {
|
|
281
563
|
const key = `${id}####${fileName}`;
|
|
282
564
|
return !!socket.subscribe.fileChange.find(sub => {
|
|
283
565
|
if (sub.regex.test(key)) {
|
|
566
|
+
const question = { type: 'file', id, fileName };
|
|
567
|
+
const allowed = this.#mayRead(socket, question);
|
|
568
|
+
if (allowed === false) {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
if (allowed === undefined) {
|
|
572
|
+
this.#holdUntilDecided(socket, question, `fileChange####${key}`, () => this.publishFile(socket, id, fileName, size));
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
284
575
|
socket.emit('fileChange', id, fileName, size);
|
|
285
576
|
return true;
|
|
286
577
|
}
|
|
@@ -288,17 +579,30 @@ class SocketCommands {
|
|
|
288
579
|
}
|
|
289
580
|
return false;
|
|
290
581
|
}
|
|
582
|
+
/**
|
|
583
|
+
* The send options for a message triggered by this socket: the user it is sent on behalf of.
|
|
584
|
+
*
|
|
585
|
+
* Objects, states and files have always been read and written with `{ user }` so the database
|
|
586
|
+
* applies the ACLs of the logged-in user. A message had no such channel: the receiving instance saw
|
|
587
|
+
* `from` and nothing else, so every adapter reachable over `sendTo` had to act with its own rights,
|
|
588
|
+
* and could not tell one caller from another. The user travels with the message now: js-controller
|
|
589
|
+
* 7.2.5 and newer put it into the message as `obj.user`, older ones ignore the option, so nothing
|
|
590
|
+
* breaks. A receiver treats the field as optional - where nobody was named there is nothing to
|
|
591
|
+
* check against, which is how every message looked before.
|
|
592
|
+
*
|
|
593
|
+
* @param socket the socket the command came in on
|
|
594
|
+
*/
|
|
595
|
+
static sendOptionsOf(socket) {
|
|
596
|
+
const user = socket?._acl?.user;
|
|
597
|
+
return user ? { user } : undefined;
|
|
598
|
+
}
|
|
291
599
|
publishInstanceMessage(socket, sourceInstance, messageType, data) {
|
|
292
600
|
if (this.#clientSubscribes[socket.id]?.[sourceInstance]?.includes(messageType)) {
|
|
293
601
|
socket.emit('im', messageType, sourceInstance, data);
|
|
294
602
|
return true;
|
|
295
603
|
}
|
|
296
604
|
// inform instance about missing subscription
|
|
297
|
-
this.adapter.sendTo(sourceInstance, 'clientSubscribeError', {
|
|
298
|
-
type: messageType,
|
|
299
|
-
sid: socket.id,
|
|
300
|
-
reason: 'no one subscribed',
|
|
301
|
-
});
|
|
605
|
+
this.adapter.sendTo(sourceInstance, 'clientSubscribeError', { type: messageType, sid: socket.id, reason: 'no one subscribed' }, undefined, _a.sendOptionsOf(socket));
|
|
302
606
|
return false;
|
|
303
607
|
}
|
|
304
608
|
_showSubscribes(socket, type) {
|
|
@@ -550,7 +854,7 @@ class SocketCommands {
|
|
|
550
854
|
return;
|
|
551
855
|
}
|
|
552
856
|
// inform all instances about disconnected socket, also if the socket has never subscribed to anything
|
|
553
|
-
this.#informAboutDisconnect(socket
|
|
857
|
+
this.#informAboutDisconnect(socket);
|
|
554
858
|
if (!socket.subscribe) {
|
|
555
859
|
return;
|
|
556
860
|
}
|
|
@@ -859,18 +1163,18 @@ class SocketCommands {
|
|
|
859
1163
|
*/
|
|
860
1164
|
this.commands.checkFeatureSupported = (_socket, feature, callback) => {
|
|
861
1165
|
if (feature === 'INSTANCE_MESSAGES') {
|
|
862
|
-
|
|
1166
|
+
_a._fixCallback(callback, null, true);
|
|
863
1167
|
}
|
|
864
1168
|
else if (feature === 'PARTIAL_OBJECT_TREE') {
|
|
865
|
-
|
|
1169
|
+
_a._fixCallback(callback, null, true);
|
|
866
1170
|
}
|
|
867
1171
|
else if (feature === 'OBJECTS_COUNT') {
|
|
868
1172
|
// only the admin variant of these commands has it, and a client that asks must not
|
|
869
1173
|
// send a command that nobody answers - it would sit there until its timeout
|
|
870
|
-
|
|
1174
|
+
_a._fixCallback(callback, null, !!this.commands.getObjectsCount);
|
|
871
1175
|
}
|
|
872
1176
|
else {
|
|
873
|
-
|
|
1177
|
+
_a._fixCallback(callback, null, this.adapter.supportsFeature(feature));
|
|
874
1178
|
}
|
|
875
1179
|
};
|
|
876
1180
|
/**
|
|
@@ -894,11 +1198,11 @@ class SocketCommands {
|
|
|
894
1198
|
options.user = socket._acl?.user;
|
|
895
1199
|
options.aggregate ||= 'none';
|
|
896
1200
|
try {
|
|
897
|
-
this.adapter.getHistory(id, options, (error, ...args) =>
|
|
1201
|
+
this.adapter.getHistory(id, options, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
898
1202
|
}
|
|
899
1203
|
catch (error) {
|
|
900
1204
|
this.adapter.log.error(`[getHistory] ERROR: ${error.toString()}`);
|
|
901
|
-
|
|
1205
|
+
_a._fixCallback(callback, error);
|
|
902
1206
|
}
|
|
903
1207
|
}
|
|
904
1208
|
};
|
|
@@ -936,7 +1240,7 @@ class SocketCommands {
|
|
|
936
1240
|
this.commands.sendTo = (socket, adapterInstance, command, message, callback) => {
|
|
937
1241
|
if (this._checkPermissions(socket, 'sendTo', callback, command)) {
|
|
938
1242
|
try {
|
|
939
|
-
this.adapter.sendTo(adapterInstance, command, message, res => typeof callback === 'function' && setImmediate(() => callback(res)));
|
|
1243
|
+
this.adapter.sendTo(adapterInstance, command, message, res => typeof callback === 'function' && setImmediate(() => callback(res)), _a.sendOptionsOf(socket));
|
|
940
1244
|
}
|
|
941
1245
|
catch (error) {
|
|
942
1246
|
if (typeof callback === 'function') {
|
|
@@ -971,7 +1275,7 @@ class SocketCommands {
|
|
|
971
1275
|
* @param callback callback `(result: { error?: string; result?: any }) => void`
|
|
972
1276
|
*/
|
|
973
1277
|
this.commands.sendToHost = (socket, host, command, message, callback) => {
|
|
974
|
-
if (this._checkPermissions(socket, protectedCommands.includes(command) ? 'cmdExec' : 'sendToHost', (error) => callback({ error: error ||
|
|
1278
|
+
if (this._checkPermissions(socket, protectedCommands.includes(command) ? 'cmdExec' : 'sendToHost', (error) => callback({ error: error || _a.ERROR_PERMISSION }), command)) {
|
|
975
1279
|
// Try to decode this file locally as redis has a limitation for files bigger than 20MB
|
|
976
1280
|
if (command === 'writeDirAsZip' && message && message.data.length > 1024 * 1024) {
|
|
977
1281
|
let buffer;
|
|
@@ -1002,7 +1306,10 @@ class SocketCommands {
|
|
|
1002
1306
|
}
|
|
1003
1307
|
else {
|
|
1004
1308
|
try {
|
|
1005
|
-
|
|
1309
|
+
// the 5th parameter exists from js-controller 7.2.5 on; an older one
|
|
1310
|
+
// ignores the extra argument
|
|
1311
|
+
const sendToHost = this.adapter.sendToHost;
|
|
1312
|
+
sendToHost.call(this.adapter, host, command, message, callback, _a.sendOptionsOf(socket));
|
|
1006
1313
|
}
|
|
1007
1314
|
catch (error) {
|
|
1008
1315
|
if (callback) {
|
|
@@ -1101,7 +1408,7 @@ class SocketCommands {
|
|
|
1101
1408
|
*/
|
|
1102
1409
|
this.commands.listPermissions = (_socket, callback) => {
|
|
1103
1410
|
if (typeof callback === 'function') {
|
|
1104
|
-
callback(
|
|
1411
|
+
callback(_a.COMMANDS_PERMISSIONS);
|
|
1105
1412
|
}
|
|
1106
1413
|
else {
|
|
1107
1414
|
this.adapter.log.warn('[listPermissions] Invalid callback');
|
|
@@ -1173,11 +1480,11 @@ class SocketCommands {
|
|
|
1173
1480
|
this.commands.readFile = (socket, adapter, fileName, callback) => {
|
|
1174
1481
|
if (this._checkPermissions(socket, 'readFile', callback, fileName)) {
|
|
1175
1482
|
try {
|
|
1176
|
-
this.adapter.readFile(adapter, fileName, { user: socket._acl?.user }, (error, ...args) =>
|
|
1483
|
+
this.adapter.readFile(adapter, fileName, { user: socket._acl?.user }, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
1177
1484
|
}
|
|
1178
1485
|
catch (error) {
|
|
1179
1486
|
this.adapter.log.error(`[readFile] ERROR: ${error.toString()}`);
|
|
1180
|
-
|
|
1487
|
+
_a._fixCallback(callback, error);
|
|
1181
1488
|
}
|
|
1182
1489
|
}
|
|
1183
1490
|
};
|
|
@@ -1226,7 +1533,7 @@ class SocketCommands {
|
|
|
1226
1533
|
}
|
|
1227
1534
|
catch (error) {
|
|
1228
1535
|
this.adapter.log.error(`[readFile64] ERROR: ${error.toString()}`);
|
|
1229
|
-
|
|
1536
|
+
_a._fixCallback(callback, error);
|
|
1230
1537
|
}
|
|
1231
1538
|
}
|
|
1232
1539
|
};
|
|
@@ -1255,16 +1562,16 @@ class SocketCommands {
|
|
|
1255
1562
|
}
|
|
1256
1563
|
if (this._checkPermissions(socket, 'writeFile64', callback, fileName)) {
|
|
1257
1564
|
if (!data64) {
|
|
1258
|
-
return
|
|
1565
|
+
return _a._fixCallback(callback, 'No data provided');
|
|
1259
1566
|
}
|
|
1260
1567
|
// Convert base 64 to buffer
|
|
1261
1568
|
try {
|
|
1262
1569
|
const buffer = Buffer.from(data64, 'base64');
|
|
1263
|
-
this.adapter.writeFile(adapter, fileName, buffer, _options, (error, ...args) =>
|
|
1570
|
+
this.adapter.writeFile(adapter, fileName, buffer, _options, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
1264
1571
|
}
|
|
1265
1572
|
catch (error) {
|
|
1266
1573
|
this.adapter.log.error(`[writeFile64] Cannot convert data: ${error.toString()}`);
|
|
1267
|
-
|
|
1574
|
+
_a._fixCallback(callback, `Cannot convert data: ${error.toString()}`);
|
|
1268
1575
|
}
|
|
1269
1576
|
}
|
|
1270
1577
|
};
|
|
@@ -1297,11 +1604,11 @@ class SocketCommands {
|
|
|
1297
1604
|
}
|
|
1298
1605
|
this.adapter.log.debug('writeFile deprecated. Please use writeFile64');
|
|
1299
1606
|
try {
|
|
1300
|
-
this.adapter.writeFile(adapter, fileName, data, _options, (error, ...args) =>
|
|
1607
|
+
this.adapter.writeFile(adapter, fileName, data, _options, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
1301
1608
|
}
|
|
1302
1609
|
catch (error) {
|
|
1303
1610
|
this.adapter.log.error(`[writeFile] ERROR: ${error.toString()}`);
|
|
1304
|
-
|
|
1611
|
+
_a._fixCallback(callback, error);
|
|
1305
1612
|
}
|
|
1306
1613
|
}
|
|
1307
1614
|
};
|
|
@@ -1318,12 +1625,12 @@ class SocketCommands {
|
|
|
1318
1625
|
if (this._checkPermissions(socket, 'unlink', callback, name)) {
|
|
1319
1626
|
try {
|
|
1320
1627
|
this.#unlink(adapter, name, { user: socket._acl?.user })
|
|
1321
|
-
.then(() =>
|
|
1322
|
-
.catch(error =>
|
|
1628
|
+
.then(() => _a._fixCallback(callback, undefined))
|
|
1629
|
+
.catch(error => _a._fixCallback(callback, error));
|
|
1323
1630
|
}
|
|
1324
1631
|
catch (error) {
|
|
1325
1632
|
this.adapter.log.error(`[unlink] ERROR: ${error.toString()}`);
|
|
1326
|
-
|
|
1633
|
+
_a._fixCallback(callback, error);
|
|
1327
1634
|
}
|
|
1328
1635
|
}
|
|
1329
1636
|
};
|
|
@@ -1339,11 +1646,11 @@ class SocketCommands {
|
|
|
1339
1646
|
this.commands.deleteFile = (socket, adapter, name, callback) => {
|
|
1340
1647
|
if (this._checkPermissions(socket, 'unlink', callback, name)) {
|
|
1341
1648
|
try {
|
|
1342
|
-
this.adapter.unlink(adapter, name, { user: socket._acl?.user }, (error, ...args) =>
|
|
1649
|
+
this.adapter.unlink(adapter, name, { user: socket._acl?.user }, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
1343
1650
|
}
|
|
1344
1651
|
catch (error) {
|
|
1345
1652
|
this.adapter.log.error(`[deleteFile] ERROR: ${error.toString()}`);
|
|
1346
|
-
|
|
1653
|
+
_a._fixCallback(callback, error);
|
|
1347
1654
|
}
|
|
1348
1655
|
}
|
|
1349
1656
|
};
|
|
@@ -1360,12 +1667,12 @@ class SocketCommands {
|
|
|
1360
1667
|
if (this._checkPermissions(socket, 'unlink', callback, name)) {
|
|
1361
1668
|
try {
|
|
1362
1669
|
this.#unlink(adapter, name, { user: socket._acl?.user })
|
|
1363
|
-
.then(() =>
|
|
1364
|
-
.catch(error =>
|
|
1670
|
+
.then(() => _a._fixCallback(callback, null))
|
|
1671
|
+
.catch(error => _a._fixCallback(callback, error));
|
|
1365
1672
|
}
|
|
1366
1673
|
catch (error) {
|
|
1367
1674
|
this.adapter.log.error(`[deleteFolder] ERROR: ${error.toString()}`);
|
|
1368
|
-
|
|
1675
|
+
_a._fixCallback(callback, error);
|
|
1369
1676
|
}
|
|
1370
1677
|
}
|
|
1371
1678
|
};
|
|
@@ -1382,11 +1689,11 @@ class SocketCommands {
|
|
|
1382
1689
|
this.commands.renameFile = (socket, adapter, oldName, newName, callback) => {
|
|
1383
1690
|
if (this._checkPermissions(socket, 'rename', callback, oldName)) {
|
|
1384
1691
|
try {
|
|
1385
|
-
this.adapter.rename(adapter, oldName, newName, { user: socket._acl?.user }, (error, ...args) =>
|
|
1692
|
+
this.adapter.rename(adapter, oldName, newName, { user: socket._acl?.user }, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
1386
1693
|
}
|
|
1387
1694
|
catch (error) {
|
|
1388
1695
|
this.adapter.log.error(`[renameFile] ERROR: ${error.toString()}`);
|
|
1389
|
-
|
|
1696
|
+
_a._fixCallback(callback, error);
|
|
1390
1697
|
}
|
|
1391
1698
|
}
|
|
1392
1699
|
};
|
|
@@ -1404,12 +1711,12 @@ class SocketCommands {
|
|
|
1404
1711
|
if (this._checkPermissions(socket, 'rename', callback, oldName)) {
|
|
1405
1712
|
try {
|
|
1406
1713
|
this.#rename(adapter, oldName, newName, { user: socket._acl?.user })
|
|
1407
|
-
.then(() =>
|
|
1408
|
-
.catch(error =>
|
|
1714
|
+
.then(() => _a._fixCallback(callback, undefined))
|
|
1715
|
+
.catch(error => _a._fixCallback(callback, error));
|
|
1409
1716
|
}
|
|
1410
1717
|
catch (error) {
|
|
1411
1718
|
this.adapter.log.error(`[rename] ERROR: ${error.toString()}`);
|
|
1412
|
-
|
|
1719
|
+
_a._fixCallback(callback, error);
|
|
1413
1720
|
}
|
|
1414
1721
|
}
|
|
1415
1722
|
};
|
|
@@ -1425,11 +1732,11 @@ class SocketCommands {
|
|
|
1425
1732
|
this.commands.mkdir = (socket, adapter, dirName, callback) => {
|
|
1426
1733
|
if (this._checkPermissions(socket, 'mkdir', callback, dirName)) {
|
|
1427
1734
|
try {
|
|
1428
|
-
this.adapter.mkdir(adapter, dirName, { user: socket._acl?.user }, (error, ...args) =>
|
|
1735
|
+
this.adapter.mkdir(adapter, dirName, { user: socket._acl?.user }, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
1429
1736
|
}
|
|
1430
1737
|
catch (error) {
|
|
1431
1738
|
this.adapter.log.error(`[mkdir] ERROR: ${error.toString()}`);
|
|
1432
|
-
|
|
1739
|
+
_a._fixCallback(callback, error);
|
|
1433
1740
|
}
|
|
1434
1741
|
}
|
|
1435
1742
|
};
|
|
@@ -1449,11 +1756,11 @@ class SocketCommands {
|
|
|
1449
1756
|
}
|
|
1450
1757
|
if (this._checkPermissions(socket, 'readDir', callback, dirName)) {
|
|
1451
1758
|
try {
|
|
1452
|
-
this.adapter.readDir(adapter, dirName, { user: socket._acl?.user }, (error, ...args) =>
|
|
1759
|
+
this.adapter.readDir(adapter, dirName, { user: socket._acl?.user }, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
1453
1760
|
}
|
|
1454
1761
|
catch (error) {
|
|
1455
1762
|
this.adapter.log.error(`[readDir] ERROR: ${error.toString()}`);
|
|
1456
|
-
|
|
1763
|
+
_a._fixCallback(callback, error);
|
|
1457
1764
|
}
|
|
1458
1765
|
}
|
|
1459
1766
|
};
|
|
@@ -1475,16 +1782,16 @@ class SocketCommands {
|
|
|
1475
1782
|
}
|
|
1476
1783
|
else {
|
|
1477
1784
|
this.adapter.log.error(`[chownFile] ERROR: no options`);
|
|
1478
|
-
|
|
1785
|
+
_a._fixCallback(callback, 'no options');
|
|
1479
1786
|
return;
|
|
1480
1787
|
}
|
|
1481
1788
|
if (this._checkPermissions(socket, 'chmodFile', callback, fileName)) {
|
|
1482
1789
|
try {
|
|
1483
|
-
this.adapter.chmodFile(adapter, fileName, _options, (error, ...args) =>
|
|
1790
|
+
this.adapter.chmodFile(adapter, fileName, _options, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
1484
1791
|
}
|
|
1485
1792
|
catch (error) {
|
|
1486
1793
|
this.adapter.log.error(`[chmodFile] ERROR: ${error.toString()}`);
|
|
1487
|
-
|
|
1794
|
+
_a._fixCallback(callback, error);
|
|
1488
1795
|
}
|
|
1489
1796
|
}
|
|
1490
1797
|
};
|
|
@@ -1507,16 +1814,16 @@ class SocketCommands {
|
|
|
1507
1814
|
}
|
|
1508
1815
|
else {
|
|
1509
1816
|
this.adapter.log.error(`[chownFile] ERROR: no options`);
|
|
1510
|
-
|
|
1817
|
+
_a._fixCallback(callback, 'no options');
|
|
1511
1818
|
return;
|
|
1512
1819
|
}
|
|
1513
1820
|
if (this._checkPermissions(socket, 'chownFile', callback, fileName)) {
|
|
1514
1821
|
try {
|
|
1515
|
-
this.adapter.chownFile(adapter, fileName, _options, (error, ...args) =>
|
|
1822
|
+
this.adapter.chownFile(adapter, fileName, _options, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
1516
1823
|
}
|
|
1517
1824
|
catch (error) {
|
|
1518
1825
|
this.adapter.log.error(`[chownFile] ERROR: ${error.toString()}`);
|
|
1519
|
-
|
|
1826
|
+
_a._fixCallback(callback, error);
|
|
1520
1827
|
}
|
|
1521
1828
|
}
|
|
1522
1829
|
};
|
|
@@ -1532,11 +1839,11 @@ class SocketCommands {
|
|
|
1532
1839
|
this.commands.fileExists = (socket, adapter, fileName, callback) => {
|
|
1533
1840
|
if (this._checkPermissions(socket, 'fileExists', callback, fileName)) {
|
|
1534
1841
|
try {
|
|
1535
|
-
this.adapter.fileExists(adapter, fileName, { user: socket._acl?.user }, (error, ...args) =>
|
|
1842
|
+
this.adapter.fileExists(adapter, fileName, { user: socket._acl?.user }, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
1536
1843
|
}
|
|
1537
1844
|
catch (error) {
|
|
1538
1845
|
this.adapter.log.error(`[fileExists] ERROR: ${error.toString()}`);
|
|
1539
|
-
|
|
1846
|
+
_a._fixCallback(callback, error);
|
|
1540
1847
|
}
|
|
1541
1848
|
}
|
|
1542
1849
|
};
|
|
@@ -1603,7 +1910,7 @@ class SocketCommands {
|
|
|
1603
1910
|
}
|
|
1604
1911
|
catch (error) {
|
|
1605
1912
|
this.adapter.log.error(`[getAdapterInstances] ERROR: ${error.toString()}`);
|
|
1606
|
-
|
|
1913
|
+
_a._fixCallback(callback, error);
|
|
1607
1914
|
}
|
|
1608
1915
|
}
|
|
1609
1916
|
}
|
|
@@ -1627,11 +1934,11 @@ class SocketCommands {
|
|
|
1627
1934
|
}
|
|
1628
1935
|
if (typeof callback === 'function') {
|
|
1629
1936
|
try {
|
|
1630
|
-
this.adapter.getForeignStates(pattern || '*', { user: socket._acl?.user }, (error, ...args) =>
|
|
1937
|
+
this.adapter.getForeignStates(pattern || '*', { user: socket._acl?.user }, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
1631
1938
|
}
|
|
1632
1939
|
catch (error) {
|
|
1633
1940
|
this.adapter.log.error(`[getStates] ERROR: ${error.toString()}`);
|
|
1634
|
-
|
|
1941
|
+
_a._fixCallback(callback, error);
|
|
1635
1942
|
}
|
|
1636
1943
|
}
|
|
1637
1944
|
else {
|
|
@@ -1669,15 +1976,15 @@ class SocketCommands {
|
|
|
1669
1976
|
try {
|
|
1670
1977
|
void this.adapter
|
|
1671
1978
|
.getForeignStateAsync(id, { user: socket._acl?.user })
|
|
1672
|
-
.then(state =>
|
|
1979
|
+
.then(state => _a._fixCallback(callback, null, state))
|
|
1673
1980
|
.catch(error => {
|
|
1674
1981
|
this.adapter.log.error(`[getState] ERROR: ${error.toString()}`);
|
|
1675
|
-
|
|
1982
|
+
_a._fixCallback(callback, error);
|
|
1676
1983
|
});
|
|
1677
1984
|
}
|
|
1678
1985
|
catch (error) {
|
|
1679
1986
|
this.adapter.log.error(`[getState] ERROR: ${error.toString()}`);
|
|
1680
|
-
|
|
1987
|
+
_a._fixCallback(callback, error);
|
|
1681
1988
|
}
|
|
1682
1989
|
}
|
|
1683
1990
|
}
|
|
@@ -1705,11 +2012,11 @@ class SocketCommands {
|
|
|
1705
2012
|
delete this.states[id];
|
|
1706
2013
|
}
|
|
1707
2014
|
try {
|
|
1708
|
-
this.adapter.setForeignState(id, state, { user: socket._acl?.user }, (error, ...args) =>
|
|
2015
|
+
this.adapter.setForeignState(id, state, { user: socket._acl?.user }, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
1709
2016
|
}
|
|
1710
2017
|
catch (error) {
|
|
1711
2018
|
this.adapter.log.error(`[setState] ERROR: ${error.toString()}`);
|
|
1712
|
-
|
|
2019
|
+
_a._fixCallback(callback, error);
|
|
1713
2020
|
}
|
|
1714
2021
|
}
|
|
1715
2022
|
};
|
|
@@ -1810,12 +2117,12 @@ class SocketCommands {
|
|
|
1810
2117
|
if (this.context.language && id === 'system.config' && obj?.common) {
|
|
1811
2118
|
obj.common.language = this.context.language;
|
|
1812
2119
|
}
|
|
1813
|
-
|
|
2120
|
+
_a._fixCallback(callback, error, obj);
|
|
1814
2121
|
});
|
|
1815
2122
|
}
|
|
1816
2123
|
catch (error) {
|
|
1817
2124
|
this.adapter.log.error(`[getObject] ERROR: ${error.toString()}`);
|
|
1818
|
-
|
|
2125
|
+
_a._fixCallback(callback, error);
|
|
1819
2126
|
}
|
|
1820
2127
|
}
|
|
1821
2128
|
};
|
|
@@ -1837,11 +2144,11 @@ class SocketCommands {
|
|
|
1837
2144
|
if (this._checkPermissions(socket, 'getObject', callback)) {
|
|
1838
2145
|
if (typeof callback === 'function') {
|
|
1839
2146
|
try {
|
|
1840
|
-
this.adapter.getForeignObjects(list, { user: socket._acl?.user }, (error, objs) =>
|
|
2147
|
+
this.adapter.getForeignObjects(list, { user: socket._acl?.user }, (error, objs) => _a._fixCallback(callback, error, objs));
|
|
1841
2148
|
}
|
|
1842
2149
|
catch (error) {
|
|
1843
2150
|
this.adapter.log.error(`[getObjects] ERROR: ${error.toString()}`);
|
|
1844
|
-
|
|
2151
|
+
_a._fixCallback(callback, error);
|
|
1845
2152
|
}
|
|
1846
2153
|
}
|
|
1847
2154
|
else {
|
|
@@ -1879,7 +2186,7 @@ class SocketCommands {
|
|
|
1879
2186
|
if (this.context.language && result['system.config']?.common) {
|
|
1880
2187
|
result['system.config'].common.language = this.context.language;
|
|
1881
2188
|
}
|
|
1882
|
-
|
|
2189
|
+
_a._fixCallback(callback, error, result);
|
|
1883
2190
|
});
|
|
1884
2191
|
}
|
|
1885
2192
|
else {
|
|
@@ -1888,7 +2195,7 @@ class SocketCommands {
|
|
|
1888
2195
|
}
|
|
1889
2196
|
catch (error) {
|
|
1890
2197
|
this.adapter.log.error(`[getObjects] ERROR: ${error.toString()}`);
|
|
1891
|
-
|
|
2198
|
+
_a._fixCallback(callback, error);
|
|
1892
2199
|
}
|
|
1893
2200
|
}
|
|
1894
2201
|
};
|
|
@@ -2045,7 +2352,7 @@ class SocketCommands {
|
|
|
2045
2352
|
}
|
|
2046
2353
|
catch (error) {
|
|
2047
2354
|
this.adapter.log.error(`[getObjectView] ERROR: ${error.toString()}`);
|
|
2048
|
-
|
|
2355
|
+
_a._fixCallback(callback, error);
|
|
2049
2356
|
}
|
|
2050
2357
|
}
|
|
2051
2358
|
}
|
|
@@ -2065,11 +2372,11 @@ class SocketCommands {
|
|
|
2065
2372
|
this.commands.setObject = (socket, id, obj, callback) => {
|
|
2066
2373
|
if (this._checkPermissions(socket, 'setObject', callback, id)) {
|
|
2067
2374
|
try {
|
|
2068
|
-
void this.adapter.setForeignObject(id, obj, { user: socket._acl?.user }, (error, ...args) =>
|
|
2375
|
+
void this.adapter.setForeignObject(id, obj, { user: socket._acl?.user }, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
2069
2376
|
}
|
|
2070
2377
|
catch (error) {
|
|
2071
2378
|
this.adapter.log.error(`[setObject] ERROR: ${error.toString()}`);
|
|
2072
|
-
|
|
2379
|
+
_a._fixCallback(callback, error);
|
|
2073
2380
|
}
|
|
2074
2381
|
}
|
|
2075
2382
|
};
|
|
@@ -2087,16 +2394,16 @@ class SocketCommands {
|
|
|
2087
2394
|
if (id.startsWith('flot.') || id.startsWith('fullcalendar.')) {
|
|
2088
2395
|
if (this._checkPermissions(socket, 'delObject', callback, id)) {
|
|
2089
2396
|
try {
|
|
2090
|
-
this.adapter.delForeignObject(id, { user: socket._acl?.user }, (error, ...args) =>
|
|
2397
|
+
this.adapter.delForeignObject(id, { user: socket._acl?.user }, (error, ...args) => _a._fixCallback(callback, error, ...args));
|
|
2091
2398
|
}
|
|
2092
2399
|
catch (error) {
|
|
2093
2400
|
this.adapter.log.error(`[delObject] ERROR: ${error.toString()}`);
|
|
2094
|
-
|
|
2401
|
+
_a._fixCallback(callback, error);
|
|
2095
2402
|
}
|
|
2096
2403
|
}
|
|
2097
2404
|
}
|
|
2098
2405
|
else {
|
|
2099
|
-
|
|
2406
|
+
_a._fixCallback(callback, _a.ERROR_PERMISSION);
|
|
2100
2407
|
}
|
|
2101
2408
|
};
|
|
2102
2409
|
/**
|
|
@@ -2131,7 +2438,7 @@ class SocketCommands {
|
|
|
2131
2438
|
this.#clientSubscribes[sid][targetInstance].push(messageType);
|
|
2132
2439
|
}
|
|
2133
2440
|
// inform instance about new subscription
|
|
2134
|
-
this.adapter.sendTo(targetInstance, 'clientSubscribe', { type: messageType, sid, data }, result =>
|
|
2441
|
+
this.adapter.sendTo(targetInstance, 'clientSubscribe', { type: messageType, sid, data }, result => _a._fixCallback(callback, null, result), _a.sendOptionsOf(socket));
|
|
2135
2442
|
};
|
|
2136
2443
|
/**
|
|
2137
2444
|
* #DOCUMENTATION commands
|
|
@@ -2157,16 +2464,12 @@ class SocketCommands {
|
|
|
2157
2464
|
if (pos !== -1) {
|
|
2158
2465
|
this.#clientSubscribes[sid][targetInstance].splice(pos, 1);
|
|
2159
2466
|
// inform instance about unsubscription
|
|
2160
|
-
this.adapter.sendTo(targetInstance, 'clientUnsubscribe', {
|
|
2161
|
-
|
|
2162
|
-
sid,
|
|
2163
|
-
reason: 'client',
|
|
2164
|
-
});
|
|
2165
|
-
SocketCommands._fixCallback(callback, null, true);
|
|
2467
|
+
this.adapter.sendTo(targetInstance, 'clientUnsubscribe', { type: [messageType], sid, reason: 'client' }, undefined, _a.sendOptionsOf(socket));
|
|
2468
|
+
_a._fixCallback(callback, null, true);
|
|
2166
2469
|
return;
|
|
2167
2470
|
}
|
|
2168
2471
|
}
|
|
2169
|
-
|
|
2472
|
+
_a._fixCallback(callback, null, false);
|
|
2170
2473
|
};
|
|
2171
2474
|
/**
|
|
2172
2475
|
* #DOCUMENTATION commands
|
|
@@ -2190,7 +2493,7 @@ class SocketCommands {
|
|
|
2190
2493
|
obj.native ||= {};
|
|
2191
2494
|
obj.native.vendor = vendor;
|
|
2192
2495
|
}
|
|
2193
|
-
|
|
2496
|
+
_a._fixCallback(callback, error, obj);
|
|
2194
2497
|
});
|
|
2195
2498
|
}
|
|
2196
2499
|
};
|
|
@@ -2202,15 +2505,26 @@ class SocketCommands {
|
|
|
2202
2505
|
this._initCommandsStates();
|
|
2203
2506
|
this._initCommandsFiles();
|
|
2204
2507
|
}
|
|
2205
|
-
|
|
2508
|
+
/**
|
|
2509
|
+
* Tell every instance this socket had subscribed to that it is gone.
|
|
2510
|
+
*
|
|
2511
|
+
* Carries the user of the connection like the explicit `clientUnsubscribe` does: an instance that
|
|
2512
|
+
* keeps something per user - a recording, a session, a pending request - learns whose it was,
|
|
2513
|
+
* instead of only which socket id disappeared.
|
|
2514
|
+
*
|
|
2515
|
+
* @param socket the socket that went away
|
|
2516
|
+
*/
|
|
2517
|
+
#informAboutDisconnect(socket) {
|
|
2518
|
+
const socketId = socket.id;
|
|
2206
2519
|
// say to all instances that this socket was disconnected
|
|
2207
2520
|
if (this.#clientSubscribes[socketId]) {
|
|
2521
|
+
const options = _a.sendOptionsOf(socket);
|
|
2208
2522
|
Object.keys(this.#clientSubscribes[socketId]).forEach(targetInstance => {
|
|
2209
2523
|
this.adapter.sendTo(targetInstance, 'clientUnsubscribe', {
|
|
2210
2524
|
type: this.#clientSubscribes[socketId][targetInstance],
|
|
2211
2525
|
sid: socketId,
|
|
2212
2526
|
reason: 'disconnect',
|
|
2213
|
-
});
|
|
2527
|
+
}, undefined, options);
|
|
2214
2528
|
});
|
|
2215
2529
|
delete this.#clientSubscribes[socketId];
|
|
2216
2530
|
}
|
|
@@ -2220,7 +2534,7 @@ class SocketCommands {
|
|
|
2220
2534
|
// Check if the authentication is still valid. Announcing a new access token is the only way
|
|
2221
2535
|
// to make an expired session valid again, so that command must pass the check - otherwise
|
|
2222
2536
|
// a client whose token has expired could never recover without reloading the page.
|
|
2223
|
-
if (
|
|
2537
|
+
if (_a.COMMANDS_WITHOUT_SESSION_CHECK.includes(command) || this.#updateSession(socket)) {
|
|
2224
2538
|
this.commands[command](socket, ...args);
|
|
2225
2539
|
}
|
|
2226
2540
|
else {
|
|
@@ -2236,4 +2550,5 @@ class SocketCommands {
|
|
|
2236
2550
|
}
|
|
2237
2551
|
}
|
|
2238
2552
|
exports.SocketCommands = SocketCommands;
|
|
2553
|
+
_a = SocketCommands;
|
|
2239
2554
|
//# sourceMappingURL=socketCommands.js.map
|