mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-20 01:55:59 +00:00
SYNC: Merge branch 'feature/audit-logs'
This commit is contained in:
committed by
Diflow
parent
e3c6d05a0a
commit
90bbdd563b
@@ -20,6 +20,7 @@ const {
|
||||
readCloudTestTokenHolder,
|
||||
} = require('../utility/cloudIntf');
|
||||
const socket = require('../utility/socket');
|
||||
const { sendToAuditLog } = require('../utility/auditlog');
|
||||
|
||||
const logger = getLogger('auth');
|
||||
|
||||
@@ -92,12 +93,12 @@ function authMiddleware(req, res, next) {
|
||||
|
||||
module.exports = {
|
||||
oauthToken_meta: true,
|
||||
async oauthToken(params) {
|
||||
async oauthToken(params, req) {
|
||||
const { amoid } = params;
|
||||
return getAuthProviderById(amoid).oauthToken(params);
|
||||
return getAuthProviderById(amoid).oauthToken(params, req);
|
||||
},
|
||||
login_meta: true,
|
||||
async login(params) {
|
||||
async login(params, req) {
|
||||
const { amoid, login, password, isAdminPage } = params;
|
||||
|
||||
if (isAdminPage) {
|
||||
@@ -107,6 +108,15 @@ module.exports = {
|
||||
adminPassword = decryptPasswordString(adminConfig?.adminPassword);
|
||||
}
|
||||
if (adminPassword && adminPassword == password) {
|
||||
sendToAuditLog(req, {
|
||||
category: 'auth',
|
||||
component: 'AuthController',
|
||||
action: 'login',
|
||||
event: 'login.admin',
|
||||
severity: 'info',
|
||||
message: 'Administration login successful',
|
||||
});
|
||||
|
||||
return {
|
||||
accessToken: jwt.sign(
|
||||
{
|
||||
@@ -122,10 +132,19 @@ module.exports = {
|
||||
};
|
||||
}
|
||||
|
||||
sendToAuditLog(req, {
|
||||
category: 'auth',
|
||||
component: 'AuthController',
|
||||
action: 'loginFail',
|
||||
event: 'login.adminFailed',
|
||||
severity: 'warn',
|
||||
message: 'Administraton login failed',
|
||||
});
|
||||
|
||||
return { error: 'Login failed' };
|
||||
}
|
||||
|
||||
return getAuthProviderById(amoid).login(login, password);
|
||||
return getAuthProviderById(amoid).login(login, password, undefined, req);
|
||||
},
|
||||
|
||||
getProviders_meta: true,
|
||||
|
||||
@@ -29,6 +29,7 @@ const {
|
||||
} = require('../utility/crypting');
|
||||
|
||||
const lock = new AsyncLock();
|
||||
let cachedSettingsValue = null;
|
||||
|
||||
module.exports = {
|
||||
// settingsValue: {},
|
||||
@@ -145,6 +146,13 @@ module.exports = {
|
||||
return res;
|
||||
},
|
||||
|
||||
async getCachedSettings() {
|
||||
if (!cachedSettingsValue) {
|
||||
cachedSettingsValue = await this.loadSettings();
|
||||
}
|
||||
return cachedSettingsValue;
|
||||
},
|
||||
|
||||
deleteSettings_meta: true,
|
||||
async deleteSettings() {
|
||||
await fs.unlink(path.join(datadir(), processArgs.runE2eTests ? 'settings-e2etests.json' : 'settings.json'));
|
||||
@@ -258,6 +266,7 @@ module.exports = {
|
||||
updateSettings_meta: true,
|
||||
async updateSettings(values, req) {
|
||||
if (!hasPermission(`settings/change`, req)) return false;
|
||||
cachedSettingsValue = null;
|
||||
|
||||
const res = await lock.acquire('settings', async () => {
|
||||
const currentValue = await this.loadSettings();
|
||||
@@ -304,7 +313,7 @@ module.exports = {
|
||||
const resp = await axios.default.get('https://raw.githubusercontent.com/dbgate/dbgate/master/CHANGELOG.md');
|
||||
return resp.data;
|
||||
} catch (err) {
|
||||
return ''
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -536,14 +536,14 @@ module.exports = {
|
||||
},
|
||||
|
||||
dbloginAuthToken_meta: true,
|
||||
async dbloginAuthToken({ amoid, code, conid, redirectUri, sid }) {
|
||||
async dbloginAuthToken({ amoid, code, conid, redirectUri, sid }, req) {
|
||||
try {
|
||||
const connection = await this.getCore({ conid });
|
||||
const driver = requireEngineDriver(connection);
|
||||
const accessToken = await driver.getAuthTokenFromCode(connection, { code, redirectUri, sid });
|
||||
const volatile = await this.saveVolatile({ conid, accessToken });
|
||||
const authProvider = getAuthProviderById(amoid);
|
||||
const resp = await authProvider.login(null, null, { conid: volatile._id });
|
||||
const resp = await authProvider.login(null, null, { conid: volatile._id }, req);
|
||||
return resp;
|
||||
} catch (err) {
|
||||
logger.error(extractErrorLogData(err), 'Error getting DB token');
|
||||
@@ -552,18 +552,18 @@ module.exports = {
|
||||
},
|
||||
|
||||
dbloginAuth_meta: true,
|
||||
async dbloginAuth({ amoid, conid, user, password }) {
|
||||
async dbloginAuth({ amoid, conid, user, password }, req) {
|
||||
if (user || password) {
|
||||
const saveResp = await this.saveVolatile({ conid, user, password, test: true });
|
||||
if (saveResp.msgtype == 'connected') {
|
||||
const loginResp = await getAuthProviderById(amoid).login(user, password, { conid: saveResp._id });
|
||||
const loginResp = await getAuthProviderById(amoid).login(user, password, { conid: saveResp._id }, req);
|
||||
return loginResp;
|
||||
}
|
||||
return saveResp;
|
||||
}
|
||||
|
||||
// user and password is stored in connection, volatile connection is not needed
|
||||
const loginResp = await getAuthProviderById(amoid).login(null, null, { conid });
|
||||
const loginResp = await getAuthProviderById(amoid).login(null, null, { conid }, req);
|
||||
return loginResp;
|
||||
},
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ const { decryptConnection } = require('../utility/crypting');
|
||||
const { getSshTunnel } = require('../utility/sshTunnel');
|
||||
const sessions = require('./sessions');
|
||||
const jsldata = require('./jsldata');
|
||||
const { sendToAuditLog } = require('../utility/auditlog');
|
||||
|
||||
const logger = getLogger('databaseConnections');
|
||||
|
||||
@@ -83,8 +84,11 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
handle_response(conid, database, { msgid, ...response }) {
|
||||
const [resolve, reject] = this.requests[msgid];
|
||||
const [resolve, reject, additionalData] = this.requests[msgid];
|
||||
resolve(response);
|
||||
if (additionalData?.auditLogger) {
|
||||
additionalData?.auditLogger(response);
|
||||
}
|
||||
delete this.requests[msgid];
|
||||
},
|
||||
handle_status(conid, database, { status }) {
|
||||
@@ -215,10 +219,10 @@ module.exports = {
|
||||
},
|
||||
|
||||
/** @param {import('dbgate-types').OpenedDatabaseConnection} conn */
|
||||
sendRequest(conn, message) {
|
||||
sendRequest(conn, message, additionalData = {}) {
|
||||
const msgid = crypto.randomUUID();
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
this.requests[msgid] = [resolve, reject];
|
||||
this.requests[msgid] = [resolve, reject, additionalData];
|
||||
try {
|
||||
conn.subprocess.send({ msgid, ...message });
|
||||
} catch (err) {
|
||||
@@ -242,10 +246,35 @@ module.exports = {
|
||||
},
|
||||
|
||||
sqlSelect_meta: true,
|
||||
async sqlSelect({ conid, database, select }, req) {
|
||||
async sqlSelect({ conid, database, select, auditLogSessionGroup }, req) {
|
||||
testConnectionPermission(conid, req);
|
||||
const opened = await this.ensureOpened(conid, database);
|
||||
const res = await this.sendRequest(opened, { msgtype: 'sqlSelect', select });
|
||||
const res = await this.sendRequest(
|
||||
opened,
|
||||
{ msgtype: 'sqlSelect', select },
|
||||
{
|
||||
auditLogger:
|
||||
auditLogSessionGroup && select?.from?.name?.pureName
|
||||
? response => {
|
||||
sendToAuditLog(req, {
|
||||
category: 'dbop',
|
||||
component: 'DatabaseConnectionsController',
|
||||
event: 'sql.select',
|
||||
action: 'select',
|
||||
severity: 'info',
|
||||
conid,
|
||||
database,
|
||||
schemaName: select?.from?.name?.schemaName,
|
||||
pureName: select?.from?.name?.pureName,
|
||||
sumint1: response?.rows?.length,
|
||||
sessionParam: `${select?.from?.name?.schemaName || '0'}::${select?.from?.name?.pureName}`,
|
||||
sessionGroup: auditLogSessionGroup,
|
||||
message: `Loaded table data from ${select?.from?.name?.pureName}`,
|
||||
});
|
||||
}
|
||||
: null,
|
||||
}
|
||||
);
|
||||
return res;
|
||||
},
|
||||
|
||||
@@ -492,6 +521,20 @@ module.exports = {
|
||||
}
|
||||
|
||||
const opened = await this.ensureOpened(conid, database);
|
||||
|
||||
sendToAuditLog(req, {
|
||||
category: 'dbop',
|
||||
component: 'DatabaseConnectionsController',
|
||||
action: 'structure',
|
||||
event: 'dbStructure.get',
|
||||
severity: 'info',
|
||||
conid,
|
||||
database,
|
||||
sessionParam: `${conid}::${database}`,
|
||||
sessionGroup: 'getStructure',
|
||||
message: `Loaded database structure for ${database}`
|
||||
});
|
||||
|
||||
return opened.structure;
|
||||
// const existing = this.opened.find((x) => x.conid == conid && x.database == database);
|
||||
// if (existing) return existing.status;
|
||||
|
||||
@@ -20,6 +20,7 @@ const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const processArgs = require('../utility/processArgs');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
const { checkSecureDirectories, checkSecureDirectoriesInScript } = require('../utility/security');
|
||||
const { sendToAuditLog, logJsonRunnerScript } = require('../utility/auditlog');
|
||||
const logger = getLogger('runners');
|
||||
|
||||
function extractPlugins(script) {
|
||||
@@ -270,7 +271,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
start_meta: true,
|
||||
async start({ script }) {
|
||||
async start({ script }, req) {
|
||||
const runid = crypto.randomUUID();
|
||||
|
||||
if (script.type == 'json') {
|
||||
@@ -280,14 +281,36 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
|
||||
logJsonRunnerScript(req, script);
|
||||
|
||||
const js = await jsonScriptToJavascript(script);
|
||||
return this.startCore(runid, scriptTemplate(js, false));
|
||||
}
|
||||
|
||||
if (!platformInfo.allowShellScripting) {
|
||||
sendToAuditLog(req, {
|
||||
category: 'shell',
|
||||
component: 'RunnersController',
|
||||
event: 'script.runFailed',
|
||||
action: 'script',
|
||||
severity: 'warn',
|
||||
detail: script,
|
||||
message: 'Scripts are not allowed',
|
||||
});
|
||||
|
||||
return { errorMessage: 'Shell scripting is not allowed' };
|
||||
}
|
||||
|
||||
sendToAuditLog(req, {
|
||||
category: 'shell',
|
||||
component: 'RunnersController',
|
||||
event: 'script.run.shell',
|
||||
action: 'script',
|
||||
severity: 'info',
|
||||
detail: script,
|
||||
message: 'Running JS script',
|
||||
});
|
||||
|
||||
return this.startCore(runid, scriptTemplate(script, false));
|
||||
},
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ const { testConnectionPermission } = require('../utility/hasPermission');
|
||||
const { MissingCredentialsError } = require('../utility/exceptions');
|
||||
const pipeForkLogs = require('../utility/pipeForkLogs');
|
||||
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
const { sendToAuditLog } = require('../utility/auditlog');
|
||||
|
||||
const logger = getLogger('serverConnection');
|
||||
|
||||
@@ -145,6 +146,17 @@ module.exports = {
|
||||
if (conid == '__model') return [];
|
||||
testConnectionPermission(conid, req);
|
||||
const opened = await this.ensureOpened(conid);
|
||||
sendToAuditLog(req, {
|
||||
category: 'serverop',
|
||||
component: 'ServerConnectionsController',
|
||||
action: 'listDatabases',
|
||||
event: 'databases.list',
|
||||
severity: 'info',
|
||||
conid,
|
||||
sessionParam: `${conid}`,
|
||||
sessionGroup: 'listDatabases',
|
||||
message: `Loaded databases for connection`,
|
||||
});
|
||||
return opened?.databases ?? [];
|
||||
},
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ const { appdir } = require('../utility/directories');
|
||||
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
||||
const pipeForkLogs = require('../utility/pipeForkLogs');
|
||||
const config = require('./config');
|
||||
const { sendToAuditLog } = require('../utility/auditlog');
|
||||
|
||||
const logger = getLogger('sessions');
|
||||
|
||||
@@ -146,12 +147,24 @@ module.exports = {
|
||||
},
|
||||
|
||||
executeQuery_meta: true,
|
||||
async executeQuery({ sesid, sql, autoCommit, autoDetectCharts, limitRows, frontMatter }) {
|
||||
async executeQuery({ sesid, sql, autoCommit, autoDetectCharts, limitRows, frontMatter }, req) {
|
||||
const session = this.opened.find(x => x.sesid == sesid);
|
||||
if (!session) {
|
||||
throw new Error('Invalid session');
|
||||
}
|
||||
|
||||
sendToAuditLog(req, {
|
||||
category: 'dbop',
|
||||
component: 'SessionController',
|
||||
action: 'executeQuery',
|
||||
event: 'query.execute',
|
||||
severity: 'info',
|
||||
detail: sql,
|
||||
conid: session.conid,
|
||||
database: session.database,
|
||||
message: 'Executing query',
|
||||
});
|
||||
|
||||
logger.info({ sesid, sql }, 'Processing query');
|
||||
this.dispatchMessage(sesid, 'Query execution started');
|
||||
session.subprocess.send({
|
||||
|
||||
Reference in New Issue
Block a user