permissions for connections

This commit is contained in:
Jan Prochazka
2022-07-17 10:03:17 +02:00
parent 55efdef181
commit 3a5301af6b
7 changed files with 119 additions and 43 deletions

View File

@@ -29,7 +29,7 @@ module.exports = {
async get(_params, req) {
const logins = getLogins();
const login = logins ? logins.find(x => x.login == (req.auth && req.auth.user)) : null;
const permissions = login ? login.permissions : null;
const permissions = login ? login.permissions : process.env.PERMISSIONS;
return {
runAsPortal: !!connections.portalConnections,

View File

@@ -13,6 +13,7 @@ const JsonLinesDatabase = require('../utility/JsonLinesDatabase');
const processArgs = require('../utility/processArgs');
const { safeJsonParse } = require('dbgate-tools');
const platformInfo = require('../utility/platformInfo');
const { connectionHasPermission, testConnectionPermission } = require('../utility/hasPermission');
function getNamedArgs() {
const res = {};
@@ -165,12 +166,12 @@ module.exports = {
},
list_meta: true,
async list() {
async list(_params, req) {
if (portalConnections) {
if (platformInfo.allowShellConnection) return portalConnections;
return portalConnections.map(maskConnection);
return portalConnections.map(maskConnection).filter(x => connectionHasPermission(x, req));
}
return this.datastore.find();
return (await this.datastore.find()).filter(x => connectionHasPermission(x, req));
},
test_meta: true,
@@ -217,16 +218,18 @@ module.exports = {
},
update_meta: true,
async update({ _id, values }) {
async update({ _id, values }, req) {
if (portalConnections) return;
testConnectionPermission(_id, req);
const res = await this.datastore.patch(_id, values);
socket.emitChanged('connection-list-changed');
return res;
},
updateDatabase_meta: true,
async updateDatabase({ conid, database, values }) {
async updateDatabase({ conid, database, values }, req) {
if (portalConnections) return;
testConnectionPermission(conid, req);
const conn = await this.datastore.get(conid);
let databases = (conn && conn.databases) || [];
if (databases.find(x => x.name == database)) {
@@ -242,8 +245,9 @@ module.exports = {
},
delete_meta: true,
async delete(connection) {
async delete(connection, req) {
if (portalConnections) return;
testConnectionPermission(connection, req);
const res = await this.datastore.remove(connection._id);
socket.emitChanged('connection-list-changed');
return res;
@@ -260,7 +264,8 @@ module.exports = {
},
get_meta: true,
async get({ conid }) {
async get({ conid }, req) {
testConnectionPermission(conid, req);
return this.getCore({ conid, mask: true });
},

View File

@@ -26,6 +26,7 @@ const generateDeploySql = require('../shell/generateDeploySql');
const { createTwoFilesPatch } = require('diff');
const diff2htmlPage = require('../utility/diff2htmlPage');
const processArgs = require('../utility/processArgs');
const { testConnectionPermission } = require('../utility/hasPermission');
module.exports = {
/** @type {import('dbgate-types').OpenedDatabaseConnection[]} */
@@ -130,7 +131,8 @@ module.exports = {
},
queryData_meta: true,
async queryData({ conid, database, sql }) {
async queryData({ conid, database, sql }, req) {
testConnectionPermission(conid, req);
console.log(`Processing query, conid=${conid}, database=${database}, sql=${sql}`);
const opened = await this.ensureOpened(conid, database);
// if (opened && opened.status && opened.status.name == 'error') {
@@ -141,14 +143,16 @@ module.exports = {
},
sqlSelect_meta: true,
async sqlSelect({ conid, database, select }) {
async sqlSelect({ conid, database, select }, req) {
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype: 'sqlSelect', select });
return res;
},
runScript_meta: true,
async runScript({ conid, database, sql }) {
async runScript({ conid, database, sql }, req) {
testConnectionPermission(conid, req);
console.log(`Processing script, conid=${conid}, database=${database}, sql=${sql}`);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype: 'runScript', sql });
@@ -156,13 +160,15 @@ module.exports = {
},
collectionData_meta: true,
async collectionData({ conid, database, options }) {
async collectionData({ conid, database, options }, req) {
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype: 'collectionData', options });
return res.result || null;
},
async loadDataCore(msgtype, { conid, database, ...args }) {
async loadDataCore(msgtype, { conid, database, ...args }, req) {
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype, ...args });
if (res.errorMessage) {
@@ -176,32 +182,38 @@ module.exports = {
},
loadKeys_meta: true,
async loadKeys({ conid, database, root, filter }) {
async loadKeys({ conid, database, root, filter }, req) {
testConnectionPermission(conid, req);
return this.loadDataCore('loadKeys', { conid, database, root, filter });
},
exportKeys_meta: true,
async exportKeys({ conid, database, options }) {
async exportKeys({ conid, database, options }, req) {
testConnectionPermission(conid, req);
return this.loadDataCore('exportKeys', { conid, database, options });
},
loadKeyInfo_meta: true,
async loadKeyInfo({ conid, database, key }) {
async loadKeyInfo({ conid, database, key }, req) {
testConnectionPermission(conid, req);
return this.loadDataCore('loadKeyInfo', { conid, database, key });
},
loadKeyTableRange_meta: true,
async loadKeyTableRange({ conid, database, key, cursor, count }) {
async loadKeyTableRange({ conid, database, key, cursor, count }, req) {
testConnectionPermission(conid, req);
return this.loadDataCore('loadKeyTableRange', { conid, database, key, cursor, count });
},
loadFieldValues_meta: true,
async loadFieldValues({ conid, database, schemaName, pureName, field, search }) {
async loadFieldValues({ conid, database, schemaName, pureName, field, search }, req) {
testConnectionPermission(conid, req);
return this.loadDataCore('loadFieldValues', { conid, database, schemaName, pureName, field, search });
},
callMethod_meta: true,
async callMethod({ conid, database, method, args }) {
async callMethod({ conid, database, method, args }, req) {
testConnectionPermission(conid, req);
return this.loadDataCore('callMethod', { conid, database, method, args });
// const opened = await this.ensureOpened(conid, database);
@@ -213,7 +225,8 @@ module.exports = {
},
updateCollection_meta: true,
async updateCollection({ conid, database, changeSet }) {
async updateCollection({ conid, database, changeSet }, req) {
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype: 'updateCollection', changeSet });
if (res.errorMessage) {
@@ -225,7 +238,14 @@ module.exports = {
},
status_meta: true,
async status({ conid, database }) {
async status({ conid, database }, req) {
if (!conid) {
return {
name: 'error',
message: 'No connection',
};
}
testConnectionPermission(conid, req);
const existing = this.opened.find(x => x.conid == conid && x.database == database);
if (existing) {
return {
@@ -247,7 +267,8 @@ module.exports = {
},
ping_meta: true,
async ping({ conid, database }) {
async ping({ conid, database }, req) {
testConnectionPermission(conid, req);
let existing = this.opened.find(x => x.conid == conid && x.database == database);
if (existing) {
@@ -263,7 +284,8 @@ module.exports = {
},
refresh_meta: true,
async refresh({ conid, database, keepOpen }) {
async refresh({ conid, database, keepOpen }, req) {
testConnectionPermission(conid, req);
if (!keepOpen) this.close(conid, database);
await this.ensureOpened(conid, database);
@@ -271,7 +293,8 @@ module.exports = {
},
syncModel_meta: true,
async syncModel({ conid, database, isFullRefresh }) {
async syncModel({ conid, database, isFullRefresh }, req) {
testConnectionPermission(conid, req);
const conn = await this.ensureOpened(conid, database);
conn.subprocess.send({ msgtype: 'syncModel', isFullRefresh });
return { status: 'ok' };
@@ -301,13 +324,15 @@ module.exports = {
},
disconnect_meta: true,
async disconnect({ conid, database }) {
async disconnect({ conid, database }, req) {
testConnectionPermission(conid, req);
await this.close(conid, database, true);
return { status: 'ok' };
},
structure_meta: true,
async structure({ conid, database }) {
async structure({ conid, database }, req) {
testConnectionPermission(conid, req);
if (conid == '__model') {
const model = await importDbModel(database);
return model;
@@ -324,14 +349,19 @@ module.exports = {
},
serverVersion_meta: true,
async serverVersion({ conid, database }) {
async serverVersion({ conid, database }, req) {
if (!conid) {
return null;
}
testConnectionPermission(conid, req);
if (!conid) return null;
const opened = await this.ensureOpened(conid, database);
return opened.serverVersion || null;
},
sqlPreview_meta: true,
async sqlPreview({ conid, database, objects, options }) {
async sqlPreview({ conid, database, objects, options }, req) {
testConnectionPermission(conid, req);
// wait for structure
await this.structure({ conid, database });
@@ -341,7 +371,8 @@ module.exports = {
},
exportModel_meta: true,
async exportModel({ conid, database }) {
async exportModel({ conid, database }, req) {
testConnectionPermission(conid, req);
const archiveFolder = await archive.getNewArchiveFolder({ database });
await fs.mkdir(path.join(archivedir(), archiveFolder));
const model = await this.structure({ conid, database });
@@ -351,7 +382,8 @@ module.exports = {
},
generateDeploySql_meta: true,
async generateDeploySql({ conid, database, archiveFolder }) {
async generateDeploySql({ conid, database, archiveFolder }, req) {
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, {
msgtype: 'generateDeploySql',

View File

@@ -7,6 +7,7 @@ const { handleProcessCommunication } = require('../utility/processComm');
const lock = new AsyncLock();
const config = require('./config');
const processArgs = require('../utility/processArgs');
const { testConnectionPermission } = require('../utility/hasPermission');
module.exports = {
opened: [],
@@ -90,19 +91,22 @@ module.exports = {
},
disconnect_meta: true,
async disconnect({ conid }) {
async disconnect({ conid }, req) {
testConnectionPermission(conid, req);
await this.close(conid, true);
return { status: 'ok' };
},
listDatabases_meta: true,
async listDatabases({ conid }) {
async listDatabases({ conid }, req) {
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid);
return opened.databases;
},
version_meta: true,
async version({ conid }) {
async version({ conid }, req) {
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid);
return opened.version;
},
@@ -132,7 +136,8 @@ module.exports = {
},
refresh_meta: true,
async refresh({ conid, keepOpen }) {
async refresh({ conid, keepOpen }, req) {
testConnectionPermission(conid, req);
if (!keepOpen) this.close(conid);
await this.ensureOpened(conid);
@@ -140,7 +145,8 @@ module.exports = {
},
createDatabase_meta: true,
async createDatabase({ conid, name }) {
async createDatabase({ conid, name }, req) {
testConnectionPermission(conid, req);
const opened = await this.ensureOpened(conid);
if (opened.connection.isReadOnly) return false;
opened.subprocess.send({ msgtype: 'createDatabase', name });

View File

@@ -4,12 +4,21 @@ const _ = require('lodash');
const userPermissions = {};
function hasPermission(tested, req) {
if (!req) {
// request object not available, allow all
return true;
}
const { user } = (req && req.auth) || {};
const key = user || '';
const logins = getLogins();
if (!userPermissions[key] && logins) {
const login = logins.find(x => x.login == user);
userPermissions[key] = compilePermissions(login ? login.permissions : null);
if (!userPermissions[key]) {
if (logins) {
const login = logins.find(x => x.login == user);
userPermissions[key] = compilePermissions(login ? login.permissions : null);
} else {
userPermissions[key] = compilePermissions(process.env.PERMISSIONS);
}
}
return testPermission(tested, userPermissions[key]);
}
@@ -50,7 +59,26 @@ function getLogins() {
return loginsCache;
}
function connectionHasPermission(connection, req) {
if (!connection) {
return true;
}
if (_.isString(connection)) {
return hasPermission(`connections/${connection}`, req);
} else {
return hasPermission(`connections/${connection._id}`, req);
}
}
function testConnectionPermission(connection, req) {
if (!connectionHasPermission(connection, req)) {
throw new Error('Connection permission not granted');
}
}
module.exports = {
hasPermission,
getLogins,
connectionHasPermission,
testConnectionPermission,
};

View File

@@ -47,7 +47,6 @@ module.exports = function useController(app, electron, route, controller) {
let method = 'post';
let raw = false;
let rawParams = false;
// if (_.isString(meta)) {
// method = meta;
@@ -55,7 +54,6 @@ module.exports = function useController(app, electron, route, controller) {
if (_.isPlainObject(meta)) {
method = meta.method;
raw = meta.raw;
rawParams = meta.rawParams;
}
if (raw) {
@@ -67,9 +65,7 @@ module.exports = function useController(app, electron, route, controller) {
// controller._init_called = true;
// }
try {
let params = [{ ...req.body, ...req.query }, req];
if (rawParams) params = [req, res];
const data = await controller[key](...params);
const data = await controller[key]({ ...req.body, ...req.query }, req);
res.json(data);
} catch (e) {
console.log(e);