dbgate-api-premium 6.6.1 → 6.6.2

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.
@@ -29,7 +29,7 @@ const generateDeploySql = require('../shell/generateDeploySql');
29
29
  const { createTwoFilesPatch } = require('diff');
30
30
  const diff2htmlPage = require('../utility/diff2htmlPage');
31
31
  const processArgs = require('../utility/processArgs');
32
- const { testConnectionPermission } = require('../utility/hasPermission');
32
+ const { testConnectionPermission, hasPermission, loadPermissionsFromRequest, loadTablePermissionsFromRequest, getTablePermissionRole, loadDatabasePermissionsFromRequest, getDatabasePermissionRole, getTablePermissionRoleLevelIndex, testDatabaseRolePermission } = require('../utility/hasPermission');
33
33
  const { MissingCredentialsError } = require('../utility/exceptions');
34
34
  const pipeForkLogs = require('../utility/pipeForkLogs');
35
35
  const crypto = require('crypto');
@@ -100,7 +100,7 @@ module.exports = {
100
100
  socket.emitChanged(`database-status-changed`, { conid, database });
101
101
  },
102
102
 
103
- handle_ping() {},
103
+ handle_ping() { },
104
104
 
105
105
  // session event handlers
106
106
 
@@ -235,7 +235,7 @@ module.exports = {
235
235
 
236
236
  queryData_meta: true,
237
237
  async queryData({ conid, database, sql }, req) {
238
- testConnectionPermission(conid, req);
238
+ await testConnectionPermission(conid, req);
239
239
  logger.info({ conid, database, sql }, 'DBGM-00007 Processing query');
240
240
  const opened = await this.ensureOpened(conid, database);
241
241
  // if (opened && opened.status && opened.status.name == 'error') {
@@ -247,7 +247,7 @@ module.exports = {
247
247
 
248
248
  sqlSelect_meta: true,
249
249
  async sqlSelect({ conid, database, select, auditLogSessionGroup }, req) {
250
- testConnectionPermission(conid, req);
250
+ await testConnectionPermission(conid, req);
251
251
  const opened = await this.ensureOpened(conid, database);
252
252
  const res = await this.sendRequest(
253
253
  opened,
@@ -256,24 +256,23 @@ module.exports = {
256
256
  auditLogger:
257
257
  auditLogSessionGroup && select?.from?.name?.pureName
258
258
  ? response => {
259
- sendToAuditLog(req, {
260
- category: 'dbop',
261
- component: 'DatabaseConnectionsController',
262
- event: 'sql.select',
263
- action: 'select',
264
- severity: 'info',
265
- conid,
266
- database,
267
- schemaName: select?.from?.name?.schemaName,
268
- pureName: select?.from?.name?.pureName,
269
- sumint1: response?.rows?.length,
270
- sessionParam: `${conid}::${database}::${select?.from?.name?.schemaName || '0'}::${
271
- select?.from?.name?.pureName
259
+ sendToAuditLog(req, {
260
+ category: 'dbop',
261
+ component: 'DatabaseConnectionsController',
262
+ event: 'sql.select',
263
+ action: 'select',
264
+ severity: 'info',
265
+ conid,
266
+ database,
267
+ schemaName: select?.from?.name?.schemaName,
268
+ pureName: select?.from?.name?.pureName,
269
+ sumint1: response?.rows?.length,
270
+ sessionParam: `${conid}::${database}::${select?.from?.name?.schemaName || '0'}::${select?.from?.name?.pureName
272
271
  }`,
273
- sessionGroup: auditLogSessionGroup,
274
- message: `Loaded table data from ${select?.from?.name?.pureName}`,
275
- });
276
- }
272
+ sessionGroup: auditLogSessionGroup,
273
+ message: `Loaded table data from ${select?.from?.name?.pureName}`,
274
+ });
275
+ }
277
276
  : null,
278
277
  }
279
278
  );
@@ -282,7 +281,9 @@ module.exports = {
282
281
 
283
282
  runScript_meta: true,
284
283
  async runScript({ conid, database, sql, useTransaction, logMessage }, req) {
285
- testConnectionPermission(conid, req);
284
+ const loadedPermissions = await loadPermissionsFromRequest(req);
285
+ await testConnectionPermission(conid, req, loadedPermissions);
286
+ await testDatabaseRolePermission(conid, database, 'run_script', req);
286
287
  logger.info({ conid, database, sql }, 'DBGM-00008 Processing script');
287
288
  const opened = await this.ensureOpened(conid, database);
288
289
  sendToAuditLog(req, {
@@ -303,7 +304,7 @@ module.exports = {
303
304
 
304
305
  runOperation_meta: true,
305
306
  async runOperation({ conid, database, operation, useTransaction }, req) {
306
- testConnectionPermission(conid, req);
307
+ await testConnectionPermission(conid, req);
307
308
  logger.info({ conid, database, operation }, 'DBGM-00009 Processing operation');
308
309
 
309
310
  sendToAuditLog(req, {
@@ -325,7 +326,7 @@ module.exports = {
325
326
 
326
327
  collectionData_meta: true,
327
328
  async collectionData({ conid, database, options, auditLogSessionGroup }, req) {
328
- testConnectionPermission(conid, req);
329
+ await testConnectionPermission(conid, req);
329
330
  const opened = await this.ensureOpened(conid, database);
330
331
  const res = await this.sendRequest(
331
332
  opened,
@@ -334,21 +335,21 @@ module.exports = {
334
335
  auditLogger:
335
336
  auditLogSessionGroup && options?.pureName
336
337
  ? response => {
337
- sendToAuditLog(req, {
338
- category: 'dbop',
339
- component: 'DatabaseConnectionsController',
340
- event: 'nosql.collectionData',
341
- action: 'select',
342
- severity: 'info',
343
- conid,
344
- database,
345
- pureName: options?.pureName,
346
- sumint1: response?.result?.rows?.length,
347
- sessionParam: `${conid}::${database}::${options?.pureName}`,
348
- sessionGroup: auditLogSessionGroup,
349
- message: `Loaded collection data ${options?.pureName}`,
350
- });
351
- }
338
+ sendToAuditLog(req, {
339
+ category: 'dbop',
340
+ component: 'DatabaseConnectionsController',
341
+ event: 'nosql.collectionData',
342
+ action: 'select',
343
+ severity: 'info',
344
+ conid,
345
+ database,
346
+ pureName: options?.pureName,
347
+ sumint1: response?.result?.rows?.length,
348
+ sessionParam: `${conid}::${database}::${options?.pureName}`,
349
+ sessionGroup: auditLogSessionGroup,
350
+ message: `Loaded collection data ${options?.pureName}`,
351
+ });
352
+ }
352
353
  : null,
353
354
  }
354
355
  );
@@ -356,7 +357,7 @@ module.exports = {
356
357
  },
357
358
 
358
359
  async loadDataCore(msgtype, { conid, database, ...args }, req) {
359
- testConnectionPermission(conid, req);
360
+ await testConnectionPermission(conid, req);
360
361
  const opened = await this.ensureOpened(conid, database);
361
362
  const res = await this.sendRequest(opened, { msgtype, ...args });
362
363
  if (res.errorMessage) {
@@ -371,7 +372,7 @@ module.exports = {
371
372
 
372
373
  schemaList_meta: true,
373
374
  async schemaList({ conid, database }, req) {
374
- testConnectionPermission(conid, req);
375
+ await testConnectionPermission(conid, req);
375
376
  return this.loadDataCore('schemaList', { conid, database });
376
377
  },
377
378
 
@@ -383,43 +384,43 @@ module.exports = {
383
384
 
384
385
  loadKeys_meta: true,
385
386
  async loadKeys({ conid, database, root, filter, limit }, req) {
386
- testConnectionPermission(conid, req);
387
+ await testConnectionPermission(conid, req);
387
388
  return this.loadDataCore('loadKeys', { conid, database, root, filter, limit });
388
389
  },
389
390
 
390
391
  scanKeys_meta: true,
391
392
  async scanKeys({ conid, database, root, pattern, cursor, count }, req) {
392
- testConnectionPermission(conid, req);
393
+ await testConnectionPermission(conid, req);
393
394
  return this.loadDataCore('scanKeys', { conid, database, root, pattern, cursor, count });
394
395
  },
395
396
 
396
397
  exportKeys_meta: true,
397
398
  async exportKeys({ conid, database, options }, req) {
398
- testConnectionPermission(conid, req);
399
+ await testConnectionPermission(conid, req);
399
400
  return this.loadDataCore('exportKeys', { conid, database, options });
400
401
  },
401
402
 
402
403
  loadKeyInfo_meta: true,
403
404
  async loadKeyInfo({ conid, database, key }, req) {
404
- testConnectionPermission(conid, req);
405
+ await testConnectionPermission(conid, req);
405
406
  return this.loadDataCore('loadKeyInfo', { conid, database, key });
406
407
  },
407
408
 
408
409
  loadKeyTableRange_meta: true,
409
410
  async loadKeyTableRange({ conid, database, key, cursor, count }, req) {
410
- testConnectionPermission(conid, req);
411
+ await testConnectionPermission(conid, req);
411
412
  return this.loadDataCore('loadKeyTableRange', { conid, database, key, cursor, count });
412
413
  },
413
414
 
414
415
  loadFieldValues_meta: true,
415
416
  async loadFieldValues({ conid, database, schemaName, pureName, field, search, dataType }, req) {
416
- testConnectionPermission(conid, req);
417
+ await testConnectionPermission(conid, req);
417
418
  return this.loadDataCore('loadFieldValues', { conid, database, schemaName, pureName, field, search, dataType });
418
419
  },
419
420
 
420
421
  callMethod_meta: true,
421
422
  async callMethod({ conid, database, method, args }, req) {
422
- testConnectionPermission(conid, req);
423
+ await testConnectionPermission(conid, req);
423
424
  return this.loadDataCore('callMethod', { conid, database, method, args });
424
425
 
425
426
  // const opened = await this.ensureOpened(conid, database);
@@ -432,7 +433,8 @@ module.exports = {
432
433
 
433
434
  updateCollection_meta: true,
434
435
  async updateCollection({ conid, database, changeSet }, req) {
435
- testConnectionPermission(conid, req);
436
+ await testConnectionPermission(conid, req);
437
+
436
438
  const opened = await this.ensureOpened(conid, database);
437
439
  const res = await this.sendRequest(opened, { msgtype: 'updateCollection', changeSet });
438
440
  if (res.errorMessage) {
@@ -443,6 +445,36 @@ module.exports = {
443
445
  return res.result || null;
444
446
  },
445
447
 
448
+ saveTableData_meta: true,
449
+ async saveTableData({ conid, database, changeSet }, req) {
450
+ await testConnectionPermission(conid, req);
451
+
452
+ const databasePermissions = await loadDatabasePermissionsFromRequest(req);
453
+ const tablePermissions = await loadTablePermissionsFromRequest(req);
454
+ const fieldsAndRoles = [
455
+ [changeSet.inserts, 'create_update_delete'],
456
+ [changeSet.deletes, 'create_update_delete'],
457
+ [changeSet.updates, 'update_only'],
458
+ ]
459
+ for (const [operations, requiredRole] of fieldsAndRoles) {
460
+ for (const operation of operations) {
461
+ const role = getTablePermissionRole(conid, database, 'tables', operation.schemaName, operation.pureName, tablePermissions, databasePermissions);
462
+ if (getTablePermissionRoleLevelIndex(role) < getTablePermissionRoleLevelIndex(requiredRole)) {
463
+ throw new Error('DBGM-00262 Permission not granted');
464
+ }
465
+ }
466
+ }
467
+
468
+ const opened = await this.ensureOpened(conid, database);
469
+ const res = await this.sendRequest(opened, { msgtype: 'saveTableData', changeSet });
470
+ if (res.errorMessage) {
471
+ return {
472
+ errorMessage: res.errorMessage,
473
+ };
474
+ }
475
+ return res.result || null;
476
+ },
477
+
446
478
  status_meta: true,
447
479
  async status({ conid, database }, req) {
448
480
  if (!conid) {
@@ -451,7 +483,7 @@ module.exports = {
451
483
  message: 'No connection',
452
484
  };
453
485
  }
454
- testConnectionPermission(conid, req);
486
+ await testConnectionPermission(conid, req);
455
487
  const existing = this.opened.find(x => x.conid == conid && x.database == database);
456
488
  if (existing) {
457
489
  return {
@@ -474,7 +506,7 @@ module.exports = {
474
506
 
475
507
  ping_meta: true,
476
508
  async ping({ conid, database }, req) {
477
- testConnectionPermission(conid, req);
509
+ await testConnectionPermission(conid, req);
478
510
  let existing = this.opened.find(x => x.conid == conid && x.database == database);
479
511
 
480
512
  if (existing) {
@@ -502,7 +534,7 @@ module.exports = {
502
534
 
503
535
  refresh_meta: true,
504
536
  async refresh({ conid, database, keepOpen }, req) {
505
- testConnectionPermission(conid, req);
537
+ await testConnectionPermission(conid, req);
506
538
  if (!keepOpen) this.close(conid, database);
507
539
 
508
540
  await this.ensureOpened(conid, database);
@@ -516,7 +548,7 @@ module.exports = {
516
548
  return { status: 'ok' };
517
549
  }
518
550
 
519
- testConnectionPermission(conid, req);
551
+ await testConnectionPermission(conid, req);
520
552
  const conn = await this.ensureOpened(conid, database);
521
553
  conn.subprocess.send({ msgtype: 'syncModel', isFullRefresh });
522
554
  return { status: 'ok' };
@@ -553,7 +585,7 @@ module.exports = {
553
585
 
554
586
  disconnect_meta: true,
555
587
  async disconnect({ conid, database }, req) {
556
- testConnectionPermission(conid, req);
588
+ await testConnectionPermission(conid, req);
557
589
  await this.close(conid, database, true);
558
590
  return { status: 'ok' };
559
591
  },
@@ -563,8 +595,9 @@ module.exports = {
563
595
  if (!conid || !database) {
564
596
  return {};
565
597
  }
598
+ const loadedPermissions = await loadPermissionsFromRequest(req);
566
599
 
567
- testConnectionPermission(conid, req);
600
+ await testConnectionPermission(conid, req, loadedPermissions);
568
601
  if (conid == '__model') {
569
602
  const model = await importDbModel(database);
570
603
  const trans = await loadModelTransform(modelTransFile);
@@ -586,6 +619,38 @@ module.exports = {
586
619
  message: `Loaded database structure for ${database}`,
587
620
  });
588
621
 
622
+ if (!hasPermission(`all-tables`, loadedPermissions)) {
623
+ // filter databases by permissions
624
+ const tablePermissions = await loadTablePermissionsFromRequest(req);
625
+ const databasePermissions = await loadDatabasePermissionsFromRequest(req);
626
+ const databasePermissionRole = getDatabasePermissionRole(conid, database, databasePermissions);
627
+
628
+ function applyTablePermissionRole(list, objectTypeField) {
629
+ const res = [];
630
+ for (const item of list ?? []) {
631
+ const tablePermissionRole = getTablePermissionRole(conid, database, objectTypeField, item.schemaName, item.pureName, tablePermissions, databasePermissionRole);
632
+ if (tablePermissionRole != 'deny') {
633
+ res.push({
634
+ ...item,
635
+ tablePermissionRole,
636
+ });
637
+ }
638
+ }
639
+ return res;
640
+ }
641
+
642
+ const res = {
643
+ ...opened.structure,
644
+ tables: applyTablePermissionRole(opened.structure.tables, 'tables'),
645
+ views: applyTablePermissionRole(opened.structure.views, 'views'),
646
+ procedures: applyTablePermissionRole(opened.structure.procedures, 'procedures'),
647
+ functions: applyTablePermissionRole(opened.structure.functions, 'functions'),
648
+ triggers: applyTablePermissionRole(opened.structure.triggers, 'triggers'),
649
+ collections: applyTablePermissionRole(opened.structure.collections, 'collections'),
650
+ }
651
+ return res;
652
+ }
653
+
589
654
  return opened.structure;
590
655
  // const existing = this.opened.find((x) => x.conid == conid && x.database == database);
591
656
  // if (existing) return existing.status;
@@ -600,7 +665,7 @@ module.exports = {
600
665
  if (!conid) {
601
666
  return null;
602
667
  }
603
- testConnectionPermission(conid, req);
668
+ await testConnectionPermission(conid, req);
604
669
  if (!conid) return null;
605
670
  const opened = await this.ensureOpened(conid, database);
606
671
  return opened.serverVersion || null;
@@ -608,7 +673,7 @@ module.exports = {
608
673
 
609
674
  sqlPreview_meta: true,
610
675
  async sqlPreview({ conid, database, objects, options }, req) {
611
- testConnectionPermission(conid, req);
676
+ await testConnectionPermission(conid, req);
612
677
  // wait for structure
613
678
  await this.structure({ conid, database });
614
679
 
@@ -619,7 +684,7 @@ module.exports = {
619
684
 
620
685
  exportModel_meta: true,
621
686
  async exportModel({ conid, database, outputFolder, schema }, req) {
622
- testConnectionPermission(conid, req);
687
+ await testConnectionPermission(conid, req);
623
688
 
624
689
  const realFolder = outputFolder.startsWith('archive:')
625
690
  ? resolveArchiveFolder(outputFolder.substring('archive:'.length))
@@ -637,7 +702,7 @@ module.exports = {
637
702
 
638
703
  exportModelSql_meta: true,
639
704
  async exportModelSql({ conid, database, outputFolder, outputFile, schema }, req) {
640
- testConnectionPermission(conid, req);
705
+ await testConnectionPermission(conid, req);
641
706
 
642
707
  const connection = await connections.getCore({ conid });
643
708
  const driver = requireEngineDriver(connection);
@@ -651,7 +716,7 @@ module.exports = {
651
716
 
652
717
  generateDeploySql_meta: true,
653
718
  async generateDeploySql({ conid, database, archiveFolder }, req) {
654
- testConnectionPermission(conid, req);
719
+ await testConnectionPermission(conid, req);
655
720
  const opened = await this.ensureOpened(conid, database);
656
721
  const res = await this.sendRequest(opened, {
657
722
  msgtype: 'generateDeploySql',
@@ -816,17 +881,17 @@ module.exports = {
816
881
  return {
817
882
  ...(command == 'backup'
818
883
  ? driver.backupDatabaseCommand(
819
- connection,
820
- { outputFile, database, options, selectedTables, skippedTables, argsFormat },
821
- // @ts-ignore
822
- externalTools
823
- )
884
+ connection,
885
+ { outputFile, database, options, selectedTables, skippedTables, argsFormat },
886
+ // @ts-ignore
887
+ externalTools
888
+ )
824
889
  : driver.restoreDatabaseCommand(
825
- connection,
826
- { inputFile, database, options, argsFormat },
827
- // @ts-ignore
828
- externalTools
829
- )),
890
+ connection,
891
+ { inputFile, database, options, argsFormat },
892
+ // @ts-ignore
893
+ externalTools
894
+ )),
830
895
  transformMessage: driver.transformNativeCommandMessage
831
896
  ? message => driver.transformNativeCommandMessage(message, command)
832
897
  : null,
@@ -923,7 +988,7 @@ module.exports = {
923
988
 
924
989
  executeSessionQuery_meta: true,
925
990
  async executeSessionQuery({ sesid, conid, database, sql }, req) {
926
- testConnectionPermission(conid, req);
991
+ await testConnectionPermission(conid, req);
927
992
  logger.info({ sesid, sql }, 'DBGM-00010 Processing query');
928
993
  sessions.dispatchMessage(sesid, 'Query execution started');
929
994
 
@@ -935,7 +1000,7 @@ module.exports = {
935
1000
 
936
1001
  evalJsonScript_meta: true,
937
1002
  async evalJsonScript({ conid, database, script, runid }, req) {
938
- testConnectionPermission(conid, req);
1003
+ await testConnectionPermission(conid, req);
939
1004
  const opened = await this.ensureOpened(conid, database);
940
1005
 
941
1006
  opened.subprocess.send({ msgtype: 'evalJsonScript', script, runid });
@@ -3,7 +3,7 @@ const path = require('path');
3
3
  const crypto = require('crypto');
4
4
  const { filesdir, archivedir, resolveArchiveFolder, uploadsdir, appdir, jsldir } = require('../utility/directories');
5
5
  const getChartExport = require('../utility/getChartExport');
6
- const { hasPermission } = require('../utility/hasPermission');
6
+ const { hasPermission, loadPermissionsFromRequest } = require('../utility/hasPermission');
7
7
  const socket = require('../utility/socket');
8
8
  const scheduler = require('./scheduler');
9
9
  const getDiagramExport = require('../utility/getDiagramExport');
@@ -31,7 +31,8 @@ function deserialize(format, text) {
31
31
  module.exports = {
32
32
  list_meta: true,
33
33
  async list({ folder }, req) {
34
- if (!hasPermission(`files/${folder}/read`, req)) return [];
34
+ const loadedPermissions = await loadPermissionsFromRequest(req);
35
+ if (!hasPermission(`files/${folder}/read`, loadedPermissions)) return [];
35
36
  const dir = path.join(filesdir(), folder);
36
37
  if (!(await fs.exists(dir))) return [];
37
38
  const files = (await fs.readdir(dir)).map(file => ({ folder, file }));
@@ -40,10 +41,11 @@ module.exports = {
40
41
 
41
42
  listAll_meta: true,
42
43
  async listAll(_params, req) {
44
+ const loadedPermissions = await loadPermissionsFromRequest(req);
43
45
  const folders = await fs.readdir(filesdir());
44
46
  const res = [];
45
47
  for (const folder of folders) {
46
- if (!hasPermission(`files/${folder}/read`, req)) continue;
48
+ if (!hasPermission(`files/${folder}/read`, loadedPermissions)) continue;
47
49
  const dir = path.join(filesdir(), folder);
48
50
  const files = (await fs.readdir(dir)).map(file => ({ folder, file }));
49
51
  res.push(...files);
@@ -53,7 +55,8 @@ module.exports = {
53
55
 
54
56
  delete_meta: true,
55
57
  async delete({ folder, file }, req) {
56
- if (!hasPermission(`files/${folder}/write`, req)) return false;
58
+ const loadedPermissions = await loadPermissionsFromRequest(req);
59
+ if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
57
60
  if (!checkSecureFilePathsWithoutDirectory(folder, file)) {
58
61
  return false;
59
62
  }
@@ -65,7 +68,8 @@ module.exports = {
65
68
 
66
69
  rename_meta: true,
67
70
  async rename({ folder, file, newFile }, req) {
68
- if (!hasPermission(`files/${folder}/write`, req)) return false;
71
+ const loadedPermissions = await loadPermissionsFromRequest(req);
72
+ if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
69
73
  if (!checkSecureFilePathsWithoutDirectory(folder, file, newFile)) {
70
74
  return false;
71
75
  }
@@ -86,10 +90,11 @@ module.exports = {
86
90
 
87
91
  copy_meta: true,
88
92
  async copy({ folder, file, newFile }, req) {
93
+ const loadedPermissions = await loadPermissionsFromRequest(req);
89
94
  if (!checkSecureFilePathsWithoutDirectory(folder, file, newFile)) {
90
95
  return false;
91
96
  }
92
- if (!hasPermission(`files/${folder}/write`, req)) return false;
97
+ if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
93
98
  await fs.copyFile(path.join(filesdir(), folder, file), path.join(filesdir(), folder, newFile));
94
99
  socket.emitChanged(`files-changed`, { folder });
95
100
  socket.emitChanged(`all-files-changed`);
@@ -113,7 +118,8 @@ module.exports = {
113
118
  });
114
119
  return deserialize(format, text);
115
120
  } else {
116
- if (!hasPermission(`files/${folder}/read`, req)) return null;
121
+ const loadedPermissions = await loadPermissionsFromRequest(req);
122
+ if (!hasPermission(`files/${folder}/read`, loadedPermissions)) return null;
117
123
  const text = await fs.readFile(path.join(filesdir(), folder, file), { encoding: 'utf-8' });
118
124
  return deserialize(format, text);
119
125
  }
@@ -131,18 +137,19 @@ module.exports = {
131
137
 
132
138
  save_meta: true,
133
139
  async save({ folder, file, data, format }, req) {
140
+ const loadedPermissions = await loadPermissionsFromRequest(req);
134
141
  if (!checkSecureFilePathsWithoutDirectory(folder, file)) {
135
142
  return false;
136
143
  }
137
144
 
138
145
  if (folder.startsWith('archive:')) {
139
- if (!hasPermission(`archive/write`, req)) return false;
146
+ if (!hasPermission(`archive/write`, loadedPermissions)) return false;
140
147
  const dir = resolveArchiveFolder(folder.substring('archive:'.length));
141
148
  await fs.writeFile(path.join(dir, file), serialize(format, data));
142
149
  socket.emitChanged(`archive-files-changed`, { folder: folder.substring('archive:'.length) });
143
150
  return true;
144
151
  } else if (folder.startsWith('app:')) {
145
- if (!hasPermission(`apps/write`, req)) return false;
152
+ if (!hasPermission(`apps/write`, loadedPermissions)) return false;
146
153
  const app = folder.substring('app:'.length);
147
154
  await fs.writeFile(path.join(appdir(), app, file), serialize(format, data));
148
155
  socket.emitChanged(`app-files-changed`, { app });
@@ -150,7 +157,7 @@ module.exports = {
150
157
  apps.emitChangedDbApp(folder);
151
158
  return true;
152
159
  } else {
153
- if (!hasPermission(`files/${folder}/write`, req)) return false;
160
+ if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
154
161
  const dir = path.join(filesdir(), folder);
155
162
  if (!(await fs.exists(dir))) {
156
163
  await fs.mkdir(dir);
@@ -177,7 +184,8 @@ module.exports = {
177
184
 
178
185
  favorites_meta: true,
179
186
  async favorites(_params, req) {
180
- if (!hasPermission(`files/favorites/read`, req)) return [];
187
+ const loadedPermissions = await loadPermissionsFromRequest(req);
188
+ if (!hasPermission(`files/favorites/read`, loadedPermissions)) return [];
181
189
  const dir = path.join(filesdir(), 'favorites');
182
190
  if (!(await fs.exists(dir))) return [];
183
191
  const files = await fs.readdir(dir);
@@ -234,16 +242,17 @@ module.exports = {
234
242
 
235
243
  getFileRealPath_meta: true,
236
244
  async getFileRealPath({ folder, file }, req) {
245
+ const loadedPermissions = await loadPermissionsFromRequest(req);
237
246
  if (folder.startsWith('archive:')) {
238
- if (!hasPermission(`archive/write`, req)) return false;
247
+ if (!hasPermission(`archive/write`, loadedPermissions)) return false;
239
248
  const dir = resolveArchiveFolder(folder.substring('archive:'.length));
240
249
  return path.join(dir, file);
241
250
  } else if (folder.startsWith('app:')) {
242
- if (!hasPermission(`apps/write`, req)) return false;
251
+ if (!hasPermission(`apps/write`, loadedPermissions)) return false;
243
252
  const app = folder.substring('app:'.length);
244
253
  return path.join(appdir(), app, file);
245
254
  } else {
246
- if (!hasPermission(`files/${folder}/write`, req)) return false;
255
+ if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
247
256
  const dir = path.join(filesdir(), folder);
248
257
  if (!(await fs.exists(dir))) {
249
258
  await fs.mkdir(dir);
@@ -297,7 +306,8 @@ module.exports = {
297
306
 
298
307
  exportFile_meta: true,
299
308
  async exportFile({ folder, file, filePath }, req) {
300
- if (!hasPermission(`files/${folder}/read`, req)) return false;
309
+ const loadedPermissions = await loadPermissionsFromRequest(req);
310
+ if (!hasPermission(`files/${folder}/read`, loadedPermissions)) return false;
301
311
  await fs.copyFile(path.join(filesdir(), folder, file), filePath);
302
312
  return true;
303
313
  },
@@ -7,7 +7,7 @@ const socket = require('../utility/socket');
7
7
  const compareVersions = require('compare-versions');
8
8
  const requirePlugin = require('../shell/requirePlugin');
9
9
  const downloadPackage = require('../utility/downloadPackage');
10
- const { hasPermission } = require('../utility/hasPermission');
10
+ const { hasPermission, loadPermissionsFromRequest } = require('../utility/hasPermission');
11
11
  const _ = require('lodash');
12
12
  const packagedPluginsContent = require('../packagedPluginsContent');
13
13
 
@@ -118,7 +118,8 @@ module.exports = {
118
118
 
119
119
  install_meta: true,
120
120
  async install({ packageName }, req) {
121
- if (!hasPermission(`plugins/install`, req)) return;
121
+ const loadedPermissions = await loadPermissionsFromRequest(req);
122
+ if (!hasPermission(`plugins/install`, loadedPermissions)) return;
122
123
  const dir = path.join(pluginsdir(), packageName);
123
124
  // @ts-ignore
124
125
  if (!(await fs.exists(dir))) {
@@ -132,7 +133,8 @@ module.exports = {
132
133
 
133
134
  uninstall_meta: true,
134
135
  async uninstall({ packageName }, req) {
135
- if (!hasPermission(`plugins/install`, req)) return;
136
+ const loadedPermissions = await loadPermissionsFromRequest(req);
137
+ if (!hasPermission(`plugins/install`, loadedPermissions)) return;
136
138
  const dir = path.join(pluginsdir(), packageName);
137
139
  await fs.rmdir(dir, { recursive: true });
138
140
  socket.emitChanged(`installed-plugins-changed`);
@@ -143,7 +145,8 @@ module.exports = {
143
145
 
144
146
  upgrade_meta: true,
145
147
  async upgrade({ packageName }, req) {
146
- if (!hasPermission(`plugins/install`, req)) return;
148
+ const loadedPermissions = await loadPermissionsFromRequest(req);
149
+ if (!hasPermission(`plugins/install`, loadedPermissions)) return;
147
150
  const dir = path.join(pluginsdir(), packageName);
148
151
  // @ts-ignore
149
152
  if (await fs.exists(dir)) {
@@ -21,6 +21,7 @@ const processArgs = require('../utility/processArgs');
21
21
  const platformInfo = require('../utility/platformInfo');
22
22
  const { checkSecureDirectories, checkSecureDirectoriesInScript } = require('../utility/security');
23
23
  const { sendToAuditLog, logJsonRunnerScript } = require('../utility/auditlog');
24
+ const { testStandardPermission } = require('../utility/hasPermission');
24
25
  const logger = getLogger('runners');
25
26
 
26
27
  function extractPlugins(script) {
@@ -273,6 +274,8 @@ module.exports = {
273
274
 
274
275
  start_meta: true,
275
276
  async start({ script }, req) {
277
+ await testStandardPermission('run-shell-script', req);
278
+
276
279
  const runid = crypto.randomUUID();
277
280
 
278
281
  if (script.type == 'json') {
@@ -3,7 +3,7 @@ const fs = require('fs-extra');
3
3
  const path = require('path');
4
4
  const cron = require('node-cron');
5
5
  const runners = require('./runners');
6
- const { hasPermission } = require('../utility/hasPermission');
6
+ const { hasPermission, loadPermissionsFromRequest } = require('../utility/hasPermission');
7
7
  const { getLogger } = require('dbgate-tools');
8
8
 
9
9
  const logger = getLogger('scheduler');
@@ -30,7 +30,8 @@ module.exports = {
30
30
  },
31
31
 
32
32
  async reload(_params, req) {
33
- if (!hasPermission('files/shell/read', req)) return;
33
+ const loadedPermissions = await loadPermissionsFromRequest(req);
34
+ if (!hasPermission('files/shell/read', loadedPermissions)) return;
34
35
  const shellDir = path.join(filesdir(), 'shell');
35
36
  await this.unload();
36
37
  if (!(await fs.exists(shellDir))) return;