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.
Files changed (62) hide show
  1. package/package.json +6 -6
  2. package/src/auth/authProvider.js +14 -2
  3. package/src/auth/storageAuthProvider.js +89 -22
  4. package/src/controllers/archive.js +1 -1
  5. package/src/controllers/auth.js +3 -2
  6. package/src/controllers/cloud.js +1 -1
  7. package/src/controllers/config.js +8 -5
  8. package/src/controllers/connections.js +12 -11
  9. package/src/controllers/databaseConnections.js +148 -83
  10. package/src/controllers/files.js +49 -19
  11. package/src/controllers/plugins.js +7 -4
  12. package/src/controllers/runners.js +10 -6
  13. package/src/controllers/scheduler.js +4 -3
  14. package/src/controllers/serverConnections.js +69 -14
  15. package/src/controllers/sessions.js +8 -5
  16. package/src/controllers/storage.js +81 -51
  17. package/src/controllers/storageDb.js +118 -4
  18. package/src/controllers/uploads.js +2 -2
  19. package/src/currentVersion.js +2 -2
  20. package/src/index.js +36 -5
  21. package/src/main.js +59 -20
  22. package/src/proc/databaseConnectionProcess.js +45 -13
  23. package/src/proc/serverConnectionProcess.js +32 -6
  24. package/src/proc/sessionProcess.js +2 -2
  25. package/src/proc/sshForwardProcess.js +1 -1
  26. package/src/shell/archiveWriter.js +1 -1
  27. package/src/shell/copyStream.js +1 -1
  28. package/src/shell/executeQuery.js +3 -3
  29. package/src/shell/importDatabase.js +3 -3
  30. package/src/shell/jsonLinesReader.js +1 -1
  31. package/src/shell/jsonLinesWriter.js +1 -1
  32. package/src/shell/jsonReader.js +1 -1
  33. package/src/shell/jsonWriter.js +1 -1
  34. package/src/shell/loadDatabase.js +2 -2
  35. package/src/shell/modifyJsonLinesReader.js +1 -1
  36. package/src/shell/queryReader.js +1 -1
  37. package/src/shell/requirePlugin.js +6 -1
  38. package/src/shell/runScript.js +1 -1
  39. package/src/shell/sqlDataWriter.js +1 -1
  40. package/src/shell/tableReader.js +3 -3
  41. package/src/shell/tableWriter.js +1 -1
  42. package/src/shell/unzipDirectory.js +4 -4
  43. package/src/shell/zipDirectory.js +3 -3
  44. package/src/shell/zipJsonLinesData.js +3 -3
  45. package/src/storageModel.js +726 -105
  46. package/src/utility/DatastoreProxy.js +3 -3
  47. package/src/utility/JsonLinesDatastore.js +4 -2
  48. package/src/utility/appLogStore.js +119 -0
  49. package/src/utility/auditlog.js +1 -1
  50. package/src/utility/authProxy.js +4 -4
  51. package/src/utility/checkLicense.js +10 -4
  52. package/src/utility/childProcessChecker.js +1 -1
  53. package/src/utility/cloudIntf.js +5 -5
  54. package/src/utility/cloudUpgrade.js +4 -4
  55. package/src/utility/connectUtility.js +1 -1
  56. package/src/utility/directories.js +2 -2
  57. package/src/utility/extractSingleFileFromZip.js +3 -3
  58. package/src/utility/hasPermission.js +286 -71
  59. package/src/utility/loadModelTransform.js +1 -1
  60. package/src/utility/sshTunnel.js +7 -7
  61. package/src/utility/sshTunnelProxy.js +1 -1
  62. package/src/utility/useController.js +3 -3
@@ -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
- if (!hasPermission(`files/${folder}/read`, req)) return [];
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`, req)) continue;
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
- if (!hasPermission(`files/${folder}/write`, req)) return false;
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
- if (!hasPermission(`files/${folder}/write`, req)) return false;
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`, req)) return false;
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
- if (!hasPermission(`files/${folder}/read`, req)) return null;
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`, req)) return false;
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`, req)) return false;
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`, req)) return false;
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
- if (!hasPermission(`files/favorites/read`, req)) return [];
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`, req)) return false;
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`, req)) return false;
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`, req)) return false;
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(`${fileName} doesn't have one of supported extensions: ${FOLDERS.join(', ')}`);
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
- if (!hasPermission(`files/${folder}/read`, req)) return false;
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
- 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) {
@@ -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)) logger.log(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
- 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;
@@ -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 { testConnectionPermission } = require('../utility/hasPermission');
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
- testConnectionPermission(conid, req);
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',