dbgate-api-premium 6.6.1 → 6.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/src/auth/authProvider.js +13 -1
- package/src/auth/storageAuthProvider.js +87 -20
- package/src/controllers/auth.js +2 -1
- package/src/controllers/config.js +8 -5
- package/src/controllers/connections.js +8 -7
- package/src/controllers/databaseConnections.js +137 -72
- package/src/controllers/files.js +25 -15
- package/src/controllers/plugins.js +7 -4
- package/src/controllers/runners.js +3 -0
- package/src/controllers/scheduler.js +3 -2
- package/src/controllers/serverConnections.js +65 -10
- package/src/controllers/sessions.js +4 -1
- package/src/controllers/storage.js +75 -11
- package/src/controllers/storageDb.js +114 -1
- package/src/currentVersion.js +2 -2
- package/src/index.js +2 -1
- package/src/proc/databaseConnectionProcess.js +24 -1
- package/src/proc/serverConnectionProcess.js +26 -0
- package/src/storageModel.js +726 -105
- package/src/utility/authProxy.js +1 -1
- package/src/utility/hasPermission.js +286 -71
|
@@ -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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
|
|
274
|
-
|
|
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
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
884
|
+
connection,
|
|
885
|
+
{ outputFile, database, options, selectedTables, skippedTables, argsFormat },
|
|
886
|
+
// @ts-ignore
|
|
887
|
+
externalTools
|
|
888
|
+
)
|
|
824
889
|
: driver.restoreDatabaseCommand(
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
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 });
|
package/src/controllers/files.js
CHANGED
|
@@ -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
|
-
|
|
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`,
|
|
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
|
-
|
|
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
|
-
|
|
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`,
|
|
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
|
-
|
|
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`,
|
|
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`,
|
|
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`,
|
|
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
|
-
|
|
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`,
|
|
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`,
|
|
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`,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|