dbgate-api-premium 6.6.0 → 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.
- package/package.json +6 -6
- package/src/auth/authProvider.js +14 -2
- package/src/auth/storageAuthProvider.js +89 -22
- package/src/controllers/archive.js +1 -1
- package/src/controllers/auth.js +3 -2
- package/src/controllers/cloud.js +1 -1
- package/src/controllers/config.js +8 -5
- package/src/controllers/connections.js +12 -11
- package/src/controllers/databaseConnections.js +148 -83
- package/src/controllers/files.js +49 -19
- package/src/controllers/plugins.js +7 -4
- package/src/controllers/runners.js +10 -6
- package/src/controllers/scheduler.js +4 -3
- package/src/controllers/serverConnections.js +69 -14
- package/src/controllers/sessions.js +8 -5
- package/src/controllers/storage.js +81 -51
- package/src/controllers/storageDb.js +118 -4
- package/src/controllers/uploads.js +2 -2
- package/src/currentVersion.js +2 -2
- package/src/index.js +36 -5
- package/src/main.js +59 -20
- package/src/proc/databaseConnectionProcess.js +45 -13
- package/src/proc/serverConnectionProcess.js +32 -6
- package/src/proc/sessionProcess.js +2 -2
- package/src/proc/sshForwardProcess.js +1 -1
- package/src/shell/archiveWriter.js +1 -1
- package/src/shell/copyStream.js +1 -1
- package/src/shell/executeQuery.js +3 -3
- package/src/shell/importDatabase.js +3 -3
- package/src/shell/jsonLinesReader.js +1 -1
- package/src/shell/jsonLinesWriter.js +1 -1
- package/src/shell/jsonReader.js +1 -1
- package/src/shell/jsonWriter.js +1 -1
- package/src/shell/loadDatabase.js +2 -2
- package/src/shell/modifyJsonLinesReader.js +1 -1
- package/src/shell/queryReader.js +1 -1
- package/src/shell/requirePlugin.js +6 -1
- package/src/shell/runScript.js +1 -1
- package/src/shell/sqlDataWriter.js +1 -1
- package/src/shell/tableReader.js +3 -3
- package/src/shell/tableWriter.js +1 -1
- package/src/shell/unzipDirectory.js +4 -4
- package/src/shell/zipDirectory.js +3 -3
- package/src/shell/zipJsonLinesData.js +3 -3
- package/src/storageModel.js +726 -105
- package/src/utility/DatastoreProxy.js +3 -3
- package/src/utility/JsonLinesDatastore.js +4 -2
- package/src/utility/appLogStore.js +119 -0
- package/src/utility/auditlog.js +1 -1
- package/src/utility/authProxy.js +4 -4
- package/src/utility/checkLicense.js +10 -4
- package/src/utility/childProcessChecker.js +1 -1
- package/src/utility/cloudIntf.js +5 -5
- package/src/utility/cloudUpgrade.js +4 -4
- package/src/utility/connectUtility.js +1 -1
- package/src/utility/directories.js +2 -2
- package/src/utility/extractSingleFileFromZip.js +3 -3
- package/src/utility/hasPermission.js +286 -71
- package/src/utility/loadModelTransform.js +1 -1
- package/src/utility/sshTunnel.js +7 -7
- package/src/utility/sshTunnelProxy.js +1 -1
- package/src/utility/useController.js +3 -3
package/src/controllers/files.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const fs = require('fs-extra');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const crypto = require('crypto');
|
|
4
|
-
const { filesdir, archivedir, resolveArchiveFolder, uploadsdir, appdir } = require('../utility/directories');
|
|
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');
|
|
@@ -13,6 +13,7 @@ const dbgateApi = require('../shell');
|
|
|
13
13
|
const { getLogger } = require('dbgate-tools');
|
|
14
14
|
const platformInfo = require('../utility/platformInfo');
|
|
15
15
|
const { checkSecureFilePathsWithoutDirectory, checkSecureDirectories } = require('../utility/security');
|
|
16
|
+
const { copyAppLogsIntoFile, getRecentAppLogRecords } = require('../utility/appLogStore');
|
|
16
17
|
const logger = getLogger('files');
|
|
17
18
|
|
|
18
19
|
function serialize(format, data) {
|
|
@@ -30,7 +31,8 @@ function deserialize(format, text) {
|
|
|
30
31
|
module.exports = {
|
|
31
32
|
list_meta: true,
|
|
32
33
|
async list({ folder }, req) {
|
|
33
|
-
|
|
34
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
35
|
+
if (!hasPermission(`files/${folder}/read`, loadedPermissions)) return [];
|
|
34
36
|
const dir = path.join(filesdir(), folder);
|
|
35
37
|
if (!(await fs.exists(dir))) return [];
|
|
36
38
|
const files = (await fs.readdir(dir)).map(file => ({ folder, file }));
|
|
@@ -39,10 +41,11 @@ module.exports = {
|
|
|
39
41
|
|
|
40
42
|
listAll_meta: true,
|
|
41
43
|
async listAll(_params, req) {
|
|
44
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
42
45
|
const folders = await fs.readdir(filesdir());
|
|
43
46
|
const res = [];
|
|
44
47
|
for (const folder of folders) {
|
|
45
|
-
if (!hasPermission(`files/${folder}/read`,
|
|
48
|
+
if (!hasPermission(`files/${folder}/read`, loadedPermissions)) continue;
|
|
46
49
|
const dir = path.join(filesdir(), folder);
|
|
47
50
|
const files = (await fs.readdir(dir)).map(file => ({ folder, file }));
|
|
48
51
|
res.push(...files);
|
|
@@ -52,7 +55,8 @@ module.exports = {
|
|
|
52
55
|
|
|
53
56
|
delete_meta: true,
|
|
54
57
|
async delete({ folder, file }, req) {
|
|
55
|
-
|
|
58
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
59
|
+
if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
|
|
56
60
|
if (!checkSecureFilePathsWithoutDirectory(folder, file)) {
|
|
57
61
|
return false;
|
|
58
62
|
}
|
|
@@ -64,7 +68,8 @@ module.exports = {
|
|
|
64
68
|
|
|
65
69
|
rename_meta: true,
|
|
66
70
|
async rename({ folder, file, newFile }, req) {
|
|
67
|
-
|
|
71
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
72
|
+
if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
|
|
68
73
|
if (!checkSecureFilePathsWithoutDirectory(folder, file, newFile)) {
|
|
69
74
|
return false;
|
|
70
75
|
}
|
|
@@ -85,10 +90,11 @@ module.exports = {
|
|
|
85
90
|
|
|
86
91
|
copy_meta: true,
|
|
87
92
|
async copy({ folder, file, newFile }, req) {
|
|
93
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
88
94
|
if (!checkSecureFilePathsWithoutDirectory(folder, file, newFile)) {
|
|
89
95
|
return false;
|
|
90
96
|
}
|
|
91
|
-
if (!hasPermission(`files/${folder}/write`,
|
|
97
|
+
if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
|
|
92
98
|
await fs.copyFile(path.join(filesdir(), folder, file), path.join(filesdir(), folder, newFile));
|
|
93
99
|
socket.emitChanged(`files-changed`, { folder });
|
|
94
100
|
socket.emitChanged(`all-files-changed`);
|
|
@@ -112,7 +118,8 @@ module.exports = {
|
|
|
112
118
|
});
|
|
113
119
|
return deserialize(format, text);
|
|
114
120
|
} else {
|
|
115
|
-
|
|
121
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
122
|
+
if (!hasPermission(`files/${folder}/read`, loadedPermissions)) return null;
|
|
116
123
|
const text = await fs.readFile(path.join(filesdir(), folder, file), { encoding: 'utf-8' });
|
|
117
124
|
return deserialize(format, text);
|
|
118
125
|
}
|
|
@@ -130,18 +137,19 @@ module.exports = {
|
|
|
130
137
|
|
|
131
138
|
save_meta: true,
|
|
132
139
|
async save({ folder, file, data, format }, req) {
|
|
140
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
133
141
|
if (!checkSecureFilePathsWithoutDirectory(folder, file)) {
|
|
134
142
|
return false;
|
|
135
143
|
}
|
|
136
144
|
|
|
137
145
|
if (folder.startsWith('archive:')) {
|
|
138
|
-
if (!hasPermission(`archive/write`,
|
|
146
|
+
if (!hasPermission(`archive/write`, loadedPermissions)) return false;
|
|
139
147
|
const dir = resolveArchiveFolder(folder.substring('archive:'.length));
|
|
140
148
|
await fs.writeFile(path.join(dir, file), serialize(format, data));
|
|
141
149
|
socket.emitChanged(`archive-files-changed`, { folder: folder.substring('archive:'.length) });
|
|
142
150
|
return true;
|
|
143
151
|
} else if (folder.startsWith('app:')) {
|
|
144
|
-
if (!hasPermission(`apps/write`,
|
|
152
|
+
if (!hasPermission(`apps/write`, loadedPermissions)) return false;
|
|
145
153
|
const app = folder.substring('app:'.length);
|
|
146
154
|
await fs.writeFile(path.join(appdir(), app, file), serialize(format, data));
|
|
147
155
|
socket.emitChanged(`app-files-changed`, { app });
|
|
@@ -149,7 +157,7 @@ module.exports = {
|
|
|
149
157
|
apps.emitChangedDbApp(folder);
|
|
150
158
|
return true;
|
|
151
159
|
} else {
|
|
152
|
-
if (!hasPermission(`files/${folder}/write`,
|
|
160
|
+
if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
|
|
153
161
|
const dir = path.join(filesdir(), folder);
|
|
154
162
|
if (!(await fs.exists(dir))) {
|
|
155
163
|
await fs.mkdir(dir);
|
|
@@ -176,7 +184,8 @@ module.exports = {
|
|
|
176
184
|
|
|
177
185
|
favorites_meta: true,
|
|
178
186
|
async favorites(_params, req) {
|
|
179
|
-
|
|
187
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
188
|
+
if (!hasPermission(`files/favorites/read`, loadedPermissions)) return [];
|
|
180
189
|
const dir = path.join(filesdir(), 'favorites');
|
|
181
190
|
if (!(await fs.exists(dir))) return [];
|
|
182
191
|
const files = await fs.readdir(dir);
|
|
@@ -233,16 +242,17 @@ module.exports = {
|
|
|
233
242
|
|
|
234
243
|
getFileRealPath_meta: true,
|
|
235
244
|
async getFileRealPath({ folder, file }, req) {
|
|
245
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
236
246
|
if (folder.startsWith('archive:')) {
|
|
237
|
-
if (!hasPermission(`archive/write`,
|
|
247
|
+
if (!hasPermission(`archive/write`, loadedPermissions)) return false;
|
|
238
248
|
const dir = resolveArchiveFolder(folder.substring('archive:'.length));
|
|
239
249
|
return path.join(dir, file);
|
|
240
250
|
} else if (folder.startsWith('app:')) {
|
|
241
|
-
if (!hasPermission(`apps/write`,
|
|
251
|
+
if (!hasPermission(`apps/write`, loadedPermissions)) return false;
|
|
242
252
|
const app = folder.substring('app:'.length);
|
|
243
253
|
return path.join(appdir(), app, file);
|
|
244
254
|
} else {
|
|
245
|
-
if (!hasPermission(`files/${folder}/write`,
|
|
255
|
+
if (!hasPermission(`files/${folder}/write`, loadedPermissions)) return false;
|
|
246
256
|
const dir = path.join(filesdir(), folder);
|
|
247
257
|
if (!(await fs.exists(dir))) {
|
|
248
258
|
await fs.mkdir(dir);
|
|
@@ -253,7 +263,7 @@ module.exports = {
|
|
|
253
263
|
|
|
254
264
|
createZipFromJsons_meta: true,
|
|
255
265
|
async createZipFromJsons({ db, filePath }) {
|
|
256
|
-
logger.info(`Creating zip file from JSONS ${filePath}`);
|
|
266
|
+
logger.info(`DBGM-00011 Creating zip file from JSONS ${filePath}`);
|
|
257
267
|
await dbgateApi.zipJsonLinesData(db, filePath);
|
|
258
268
|
return true;
|
|
259
269
|
},
|
|
@@ -279,7 +289,7 @@ module.exports = {
|
|
|
279
289
|
const FOLDERS = ['sql', 'sqlite'];
|
|
280
290
|
for (const folder of FOLDERS) {
|
|
281
291
|
if (fileName.toLowerCase().endsWith('.' + folder)) {
|
|
282
|
-
logger.info(`Saving ${folder} file ${fileName}`);
|
|
292
|
+
logger.info(`DBGM-00012 Saving ${folder} file ${fileName}`);
|
|
283
293
|
await fs.copyFile(filePath, path.join(filesdir(), folder, fileName));
|
|
284
294
|
|
|
285
295
|
socket.emitChanged(`files-changed`, { folder: folder });
|
|
@@ -291,12 +301,13 @@ module.exports = {
|
|
|
291
301
|
}
|
|
292
302
|
}
|
|
293
303
|
|
|
294
|
-
throw new Error(
|
|
304
|
+
throw new Error(`DBGM-00013 ${fileName} doesn't have one of supported extensions: ${FOLDERS.join(', ')}`);
|
|
295
305
|
},
|
|
296
306
|
|
|
297
307
|
exportFile_meta: true,
|
|
298
308
|
async exportFile({ folder, file, filePath }, req) {
|
|
299
|
-
|
|
309
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
310
|
+
if (!hasPermission(`files/${folder}/read`, loadedPermissions)) return false;
|
|
300
311
|
await fs.copyFile(path.join(filesdir(), folder, file), filePath);
|
|
301
312
|
return true;
|
|
302
313
|
},
|
|
@@ -311,4 +322,23 @@ module.exports = {
|
|
|
311
322
|
await fs.copyFile(sourceFilePath, targetFilePath);
|
|
312
323
|
return true;
|
|
313
324
|
},
|
|
325
|
+
|
|
326
|
+
fillAppLogs_meta: true,
|
|
327
|
+
async fillAppLogs({ dateFrom = 0, dateTo = new Date().getTime(), prepareForExport = false }) {
|
|
328
|
+
const jslid = crypto.randomUUID();
|
|
329
|
+
const outputFile = path.join(jsldir(), `${jslid}.jsonl`);
|
|
330
|
+
await copyAppLogsIntoFile(dateFrom, dateTo, outputFile, prepareForExport);
|
|
331
|
+
return {
|
|
332
|
+
jslid,
|
|
333
|
+
};
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
getRecentAppLog_meta: true,
|
|
337
|
+
getRecentAppLog({ limit }) {
|
|
338
|
+
const res = getRecentAppLogRecords();
|
|
339
|
+
if (limit) {
|
|
340
|
+
return res.slice(-limit);
|
|
341
|
+
}
|
|
342
|
+
return res;
|
|
343
|
+
},
|
|
314
344
|
};
|
|
@@ -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) {
|
|
@@ -48,7 +49,7 @@ require=null;
|
|
|
48
49
|
async function run() {
|
|
49
50
|
${script}
|
|
50
51
|
await dbgateApi.finalizer.run();
|
|
51
|
-
logger.info('Finished job script');
|
|
52
|
+
logger.info('DBGM-00014 Finished job script');
|
|
52
53
|
}
|
|
53
54
|
dbgateApi.runScript(run);
|
|
54
55
|
`;
|
|
@@ -74,7 +75,8 @@ module.exports = {
|
|
|
74
75
|
|
|
75
76
|
dispatchMessage(runid, message) {
|
|
76
77
|
if (message) {
|
|
77
|
-
if (_.isPlainObject(message))
|
|
78
|
+
if (_.isPlainObject(message))
|
|
79
|
+
logger.log({ ...message, msg: message.msg || message.message || '', message: undefined });
|
|
78
80
|
else logger.info(message);
|
|
79
81
|
|
|
80
82
|
const toEmit = _.isPlainObject(message)
|
|
@@ -132,7 +134,7 @@ module.exports = {
|
|
|
132
134
|
const pluginNames = extractPlugins(scriptText);
|
|
133
135
|
// console.log('********************** SCRIPT TEXT **********************');
|
|
134
136
|
// console.log(scriptText);
|
|
135
|
-
logger.info({ scriptFile }, 'Running script');
|
|
137
|
+
logger.info({ scriptFile }, 'DBGM-00015 Running script');
|
|
136
138
|
// const subprocess = fork(scriptFile, ['--checkParent', '--max-old-space-size=8192'], {
|
|
137
139
|
const subprocess = fork(
|
|
138
140
|
scriptFile,
|
|
@@ -171,7 +173,7 @@ module.exports = {
|
|
|
171
173
|
subprocess.on('exit', code => {
|
|
172
174
|
// console.log('... EXITED', code);
|
|
173
175
|
this.rejectRequest(runid, { message: 'No data returned, maybe input data source is too big' });
|
|
174
|
-
logger.info({ code, pid: subprocess.pid }, 'Exited process');
|
|
176
|
+
logger.info({ code, pid: subprocess.pid }, 'DBGM-00016 Exited process');
|
|
175
177
|
socket.emit(`runner-done-${runid}`, code);
|
|
176
178
|
this.opened = this.opened.filter(x => x.runid != runid);
|
|
177
179
|
});
|
|
@@ -222,7 +224,7 @@ module.exports = {
|
|
|
222
224
|
|
|
223
225
|
subprocess.on('exit', code => {
|
|
224
226
|
console.log('... EXITED', code);
|
|
225
|
-
logger.info({ code, pid: subprocess.pid }, 'Exited process');
|
|
227
|
+
logger.info({ code, pid: subprocess.pid }, 'DBGM-00017 Exited process');
|
|
226
228
|
this.dispatchMessage(runid, `Finished external process with code ${code}`);
|
|
227
229
|
socket.emit(`runner-done-${runid}`, code);
|
|
228
230
|
if (onFinished) {
|
|
@@ -258,7 +260,7 @@ module.exports = {
|
|
|
258
260
|
severity: 'error',
|
|
259
261
|
message: extractErrorMessage(err),
|
|
260
262
|
});
|
|
261
|
-
logger.error(extractErrorLogData(err), 'Caught error on stdin');
|
|
263
|
+
logger.error(extractErrorLogData(err), 'DBGM-00118 Caught error on stdin');
|
|
262
264
|
});
|
|
263
265
|
}
|
|
264
266
|
|
|
@@ -272,6 +274,8 @@ module.exports = {
|
|
|
272
274
|
|
|
273
275
|
start_meta: true,
|
|
274
276
|
async start({ script }, req) {
|
|
277
|
+
await testStandardPermission('run-shell-script', req);
|
|
278
|
+
|
|
275
279
|
const runid = crypto.randomUUID();
|
|
276
280
|
|
|
277
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');
|
|
@@ -24,13 +24,14 @@ module.exports = {
|
|
|
24
24
|
if (!match) return;
|
|
25
25
|
const pattern = match[1];
|
|
26
26
|
if (!cron.validate(pattern)) return;
|
|
27
|
-
logger.info(`Schedule script ${file} with pattern ${pattern}`);
|
|
27
|
+
logger.info(`DBGM-00018 Schedule script ${file} with pattern ${pattern}`);
|
|
28
28
|
const task = cron.schedule(pattern, () => runners.start({ script: text }));
|
|
29
29
|
this.tasks.push(task);
|
|
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;
|
|
@@ -8,7 +8,13 @@ const { handleProcessCommunication } = require('../utility/processComm');
|
|
|
8
8
|
const lock = new AsyncLock();
|
|
9
9
|
const config = require('./config');
|
|
10
10
|
const processArgs = require('../utility/processArgs');
|
|
11
|
-
const {
|
|
11
|
+
const {
|
|
12
|
+
testConnectionPermission,
|
|
13
|
+
loadPermissionsFromRequest,
|
|
14
|
+
hasPermission,
|
|
15
|
+
loadDatabasePermissionsFromRequest,
|
|
16
|
+
getDatabasePermissionRole,
|
|
17
|
+
} = require('../utility/hasPermission');
|
|
12
18
|
const { MissingCredentialsError } = require('../utility/exceptions');
|
|
13
19
|
const pipeForkLogs = require('../utility/pipeForkLogs');
|
|
14
20
|
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
|
@@ -40,7 +46,7 @@ module.exports = {
|
|
|
40
46
|
existing.status = status;
|
|
41
47
|
socket.emitChanged(`server-status-changed`);
|
|
42
48
|
},
|
|
43
|
-
handle_ping() {},
|
|
49
|
+
handle_ping() { },
|
|
44
50
|
handle_response(conid, { msgid, ...response }) {
|
|
45
51
|
const [resolve, reject] = this.requests[msgid];
|
|
46
52
|
resolve(response);
|
|
@@ -103,7 +109,7 @@ module.exports = {
|
|
|
103
109
|
this.close(conid, false);
|
|
104
110
|
});
|
|
105
111
|
subprocess.on('error', err => {
|
|
106
|
-
logger.error(extractErrorLogData(err), 'Error in server connection subprocess');
|
|
112
|
+
logger.error(extractErrorLogData(err), 'DBGM-00119 Error in server connection subprocess');
|
|
107
113
|
if (newOpened.disconnected) return;
|
|
108
114
|
this.close(conid, false);
|
|
109
115
|
});
|
|
@@ -121,7 +127,7 @@ module.exports = {
|
|
|
121
127
|
try {
|
|
122
128
|
existing.subprocess.kill();
|
|
123
129
|
} catch (err) {
|
|
124
|
-
logger.error(extractErrorLogData(err), 'Error killing subprocess');
|
|
130
|
+
logger.error(extractErrorLogData(err), 'DBGM-00120 Error killing subprocess');
|
|
125
131
|
}
|
|
126
132
|
}
|
|
127
133
|
this.opened = this.opened.filter(x => x.conid != conid);
|
|
@@ -135,7 +141,7 @@ module.exports = {
|
|
|
135
141
|
|
|
136
142
|
disconnect_meta: true,
|
|
137
143
|
async disconnect({ conid }, req) {
|
|
138
|
-
testConnectionPermission(conid, req);
|
|
144
|
+
await testConnectionPermission(conid, req);
|
|
139
145
|
await this.close(conid, true);
|
|
140
146
|
return { status: 'ok' };
|
|
141
147
|
},
|
|
@@ -144,7 +150,9 @@ module.exports = {
|
|
|
144
150
|
async listDatabases({ conid }, req) {
|
|
145
151
|
if (!conid) return [];
|
|
146
152
|
if (conid == '__model') return [];
|
|
147
|
-
|
|
153
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
154
|
+
|
|
155
|
+
await testConnectionPermission(conid, req, loadedPermissions);
|
|
148
156
|
const opened = await this.ensureOpened(conid);
|
|
149
157
|
sendToAuditLog(req, {
|
|
150
158
|
category: 'serverop',
|
|
@@ -157,12 +165,29 @@ module.exports = {
|
|
|
157
165
|
sessionGroup: 'listDatabases',
|
|
158
166
|
message: `Loaded databases for connection`,
|
|
159
167
|
});
|
|
168
|
+
|
|
169
|
+
if (!hasPermission(`all-databases`, loadedPermissions)) {
|
|
170
|
+
// filter databases by permissions
|
|
171
|
+
const databasePermissions = await loadDatabasePermissionsFromRequest(req);
|
|
172
|
+
const res = [];
|
|
173
|
+
for (const db of opened?.databases ?? []) {
|
|
174
|
+
const databasePermissionRole = getDatabasePermissionRole(db.id, db.name, databasePermissions);
|
|
175
|
+
if (databasePermissionRole != 'deny') {
|
|
176
|
+
res.push({
|
|
177
|
+
...db,
|
|
178
|
+
databasePermissionRole,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return res;
|
|
183
|
+
}
|
|
184
|
+
|
|
160
185
|
return opened?.databases ?? [];
|
|
161
186
|
},
|
|
162
187
|
|
|
163
188
|
version_meta: true,
|
|
164
189
|
async version({ conid }, req) {
|
|
165
|
-
testConnectionPermission(conid, req);
|
|
190
|
+
await testConnectionPermission(conid, req);
|
|
166
191
|
const opened = await this.ensureOpened(conid);
|
|
167
192
|
return opened?.version ?? null;
|
|
168
193
|
},
|
|
@@ -191,7 +216,7 @@ module.exports = {
|
|
|
191
216
|
try {
|
|
192
217
|
opened.subprocess.send({ msgtype: 'ping' });
|
|
193
218
|
} catch (err) {
|
|
194
|
-
logger.error(extractErrorLogData(err), 'Error pinging server connection');
|
|
219
|
+
logger.error(extractErrorLogData(err), 'DBGM-00121 Error pinging server connection');
|
|
195
220
|
this.close(conid);
|
|
196
221
|
}
|
|
197
222
|
})
|
|
@@ -202,7 +227,7 @@ module.exports = {
|
|
|
202
227
|
|
|
203
228
|
refresh_meta: true,
|
|
204
229
|
async refresh({ conid, keepOpen }, req) {
|
|
205
|
-
testConnectionPermission(conid, req);
|
|
230
|
+
await testConnectionPermission(conid, req);
|
|
206
231
|
if (!keepOpen) this.close(conid);
|
|
207
232
|
|
|
208
233
|
await this.ensureOpened(conid);
|
|
@@ -210,7 +235,7 @@ module.exports = {
|
|
|
210
235
|
},
|
|
211
236
|
|
|
212
237
|
async sendDatabaseOp({ conid, msgtype, name }, req) {
|
|
213
|
-
testConnectionPermission(conid, req);
|
|
238
|
+
await testConnectionPermission(conid, req);
|
|
214
239
|
const opened = await this.ensureOpened(conid);
|
|
215
240
|
if (!opened) {
|
|
216
241
|
return null;
|
|
@@ -244,7 +269,7 @@ module.exports = {
|
|
|
244
269
|
try {
|
|
245
270
|
conn.subprocess.send({ msgid, ...message });
|
|
246
271
|
} catch (err) {
|
|
247
|
-
logger.error(extractErrorLogData(err), 'Error sending request');
|
|
272
|
+
logger.error(extractErrorLogData(err), 'DBGM-00122 Error sending request');
|
|
248
273
|
this.close(conn.conid);
|
|
249
274
|
}
|
|
250
275
|
});
|
|
@@ -252,7 +277,7 @@ module.exports = {
|
|
|
252
277
|
},
|
|
253
278
|
|
|
254
279
|
async loadDataCore(msgtype, { conid, ...args }, req) {
|
|
255
|
-
testConnectionPermission(conid, req);
|
|
280
|
+
await testConnectionPermission(conid, req);
|
|
256
281
|
const opened = await this.ensureOpened(conid);
|
|
257
282
|
if (!opened) {
|
|
258
283
|
return null;
|
|
@@ -270,13 +295,43 @@ module.exports = {
|
|
|
270
295
|
|
|
271
296
|
serverSummary_meta: true,
|
|
272
297
|
async serverSummary({ conid }, req) {
|
|
273
|
-
testConnectionPermission(conid, req);
|
|
298
|
+
await testConnectionPermission(conid, req);
|
|
299
|
+
logger.info({ conid }, 'DBGM-00260 Processing server summary');
|
|
274
300
|
return this.loadDataCore('serverSummary', { conid });
|
|
275
301
|
},
|
|
276
302
|
|
|
303
|
+
listDatabaseProcesses_meta: true,
|
|
304
|
+
async listDatabaseProcesses(ctx, req) {
|
|
305
|
+
const { conid } = ctx;
|
|
306
|
+
// logger.info({ conid }, 'DBGM-00261 Listing processes of database server');
|
|
307
|
+
testConnectionPermission(conid, req);
|
|
308
|
+
|
|
309
|
+
const opened = await this.ensureOpened(conid);
|
|
310
|
+
if (!opened) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
if (opened.connection.isReadOnly) return false;
|
|
314
|
+
|
|
315
|
+
return this.sendRequest(opened, { msgtype: 'listDatabaseProcesses' });
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
killDatabaseProcess_meta: true,
|
|
319
|
+
async killDatabaseProcess(ctx, req) {
|
|
320
|
+
const { conid, pid } = ctx;
|
|
321
|
+
testConnectionPermission(conid, req);
|
|
322
|
+
|
|
323
|
+
const opened = await this.ensureOpened(conid);
|
|
324
|
+
if (!opened) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
if (opened.connection.isReadOnly) return false;
|
|
328
|
+
|
|
329
|
+
return this.sendRequest(opened, { msgtype: 'killDatabaseProcess', pid });
|
|
330
|
+
},
|
|
331
|
+
|
|
277
332
|
summaryCommand_meta: true,
|
|
278
333
|
async summaryCommand({ conid, command, row }, req) {
|
|
279
|
-
testConnectionPermission(conid, req);
|
|
334
|
+
await testConnectionPermission(conid, req);
|
|
280
335
|
const opened = await this.ensureOpened(conid);
|
|
281
336
|
if (!opened) {
|
|
282
337
|
return null;
|
|
@@ -12,6 +12,7 @@ const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
|
|
12
12
|
const pipeForkLogs = require('../utility/pipeForkLogs');
|
|
13
13
|
const config = require('./config');
|
|
14
14
|
const { sendToAuditLog } = require('../utility/auditlog');
|
|
15
|
+
const { testStandardPermission, testDatabaseRolePermission } = require('../utility/hasPermission');
|
|
15
16
|
|
|
16
17
|
const logger = getLogger('sessions');
|
|
17
18
|
|
|
@@ -94,7 +95,7 @@ module.exports = {
|
|
|
94
95
|
socket.emit(`session-initialize-file-${jslid}`);
|
|
95
96
|
},
|
|
96
97
|
|
|
97
|
-
handle_ping() {},
|
|
98
|
+
handle_ping() { },
|
|
98
99
|
|
|
99
100
|
create_meta: true,
|
|
100
101
|
async create({ conid, database }) {
|
|
@@ -148,10 +149,12 @@ module.exports = {
|
|
|
148
149
|
|
|
149
150
|
executeQuery_meta: true,
|
|
150
151
|
async executeQuery({ sesid, sql, autoCommit, autoDetectCharts, limitRows, frontMatter }, req) {
|
|
152
|
+
await testStandardPermission('dbops/query', req);
|
|
151
153
|
const session = this.opened.find(x => x.sesid == sesid);
|
|
152
154
|
if (!session) {
|
|
153
155
|
throw new Error('Invalid session');
|
|
154
156
|
}
|
|
157
|
+
await testDatabaseRolePermission(session.conid, session.database, 'run_script', req);
|
|
155
158
|
|
|
156
159
|
sendToAuditLog(req, {
|
|
157
160
|
category: 'dbop',
|
|
@@ -165,7 +168,7 @@ module.exports = {
|
|
|
165
168
|
message: 'Executing query',
|
|
166
169
|
});
|
|
167
170
|
|
|
168
|
-
logger.info({ sesid, sql }, 'Processing query');
|
|
171
|
+
logger.info({ sesid, sql }, 'DBGM-00019 Processing query');
|
|
169
172
|
this.dispatchMessage(sesid, 'Query execution started');
|
|
170
173
|
session.subprocess.send({
|
|
171
174
|
msgtype: 'executeQuery',
|
|
@@ -186,7 +189,7 @@ module.exports = {
|
|
|
186
189
|
throw new Error('Invalid session');
|
|
187
190
|
}
|
|
188
191
|
|
|
189
|
-
logger.info({ sesid, command }, 'Processing control command');
|
|
192
|
+
logger.info({ sesid, command }, 'DBGM-00020 Processing control command');
|
|
190
193
|
this.dispatchMessage(sesid, `${_.startCase(command)} started`);
|
|
191
194
|
session.subprocess.send({ msgtype: 'executeControlCommand', command });
|
|
192
195
|
|
|
@@ -224,7 +227,7 @@ module.exports = {
|
|
|
224
227
|
throw new Error('Invalid session');
|
|
225
228
|
}
|
|
226
229
|
|
|
227
|
-
logger.info({ sesid }, 'Starting profiler');
|
|
230
|
+
logger.info({ sesid }, 'DBGM-00021 Starting profiler');
|
|
228
231
|
session.loadingReader_jslid = jslid;
|
|
229
232
|
session.subprocess.send({ msgtype: 'startProfiler', jslid });
|
|
230
233
|
|
|
@@ -271,7 +274,7 @@ module.exports = {
|
|
|
271
274
|
try {
|
|
272
275
|
session.subprocess.send({ msgtype: 'ping' });
|
|
273
276
|
} catch (err) {
|
|
274
|
-
logger.error(extractErrorLogData(err), 'Error pinging session');
|
|
277
|
+
logger.error(extractErrorLogData(err), 'DBGM-00145 Error pinging session');
|
|
275
278
|
|
|
276
279
|
return {
|
|
277
280
|
status: 'error',
|