safe read-only when using shell scripts

This commit is contained in:
Jan Prochazka
2022-03-19 10:09:27 +01:00
parent ff661ec89b
commit 8824262395
16 changed files with 80 additions and 34 deletions

View File

@@ -33,6 +33,7 @@ module.exports = {
runAsPortal: !!connections.portalConnections, runAsPortal: !!connections.portalConnections,
singleDatabase: connections.singleDatabase, singleDatabase: connections.singleDatabase,
hideAppEditor: !!process.env.HIDE_APP_EDITOR, hideAppEditor: !!process.env.HIDE_APP_EDITOR,
allowShellConnection: platformInfo.allowShellConnection,
permissions, permissions,
...currentVersion, ...currentVersion,
}; };

View File

@@ -20,7 +20,7 @@ function start() {
if (handleProcessCommunication(connection)) return; if (handleProcessCommunication(connection)) return;
try { try {
const driver = requireEngineDriver(connection); const driver = requireEngineDriver(connection);
const conn = await connectUtility(driver, connection); const conn = await connectUtility(driver, connection, 'app');
const res = await driver.getVersion(conn); const res = await driver.getVersion(conn);
process.send({ msgtype: 'connected', ...res }); process.send({ msgtype: 'connected', ...res });
} catch (e) { } catch (e) {

View File

@@ -108,7 +108,7 @@ async function handleConnect({ connection, structure, globalSettings }) {
if (!structure) setStatusName('pending'); if (!structure) setStatusName('pending');
const driver = requireEngineDriver(storedConnection); const driver = requireEngineDriver(storedConnection);
systemConnection = await checkedAsyncCall(connectUtility(driver, storedConnection)); systemConnection = await checkedAsyncCall(connectUtility(driver, storedConnection, 'app'));
await checkedAsyncCall(readVersion()); await checkedAsyncCall(readVersion());
if (structure) { if (structure) {
analysedStructure = structure; analysedStructure = structure;

View File

@@ -58,11 +58,14 @@ async function handleConnect(connection) {
const driver = requireEngineDriver(storedConnection); const driver = requireEngineDriver(storedConnection);
try { try {
systemConnection = await connectUtility(driver, storedConnection); systemConnection = await connectUtility(driver, storedConnection, 'app');
readVersion(); readVersion();
handleRefresh(); handleRefresh();
if (extractBoolSettingsValue(globalSettings, 'connection.autoRefresh', false)) { if (extractBoolSettingsValue(globalSettings, 'connection.autoRefresh', false)) {
setInterval(handleRefresh, extractIntSettingsValue(globalSettings, 'connection.autoRefreshInterval', 30, 5, 3600) * 1000); setInterval(
handleRefresh,
extractIntSettingsValue(globalSettings, 'connection.autoRefreshInterval', 30, 5, 3600) * 1000
);
} }
} catch (err) { } catch (err) {
setStatus({ setStatus({
@@ -80,7 +83,7 @@ function handlePing() {
async function handleCreateDatabase({ name }) { async function handleCreateDatabase({ name }) {
const driver = requireEngineDriver(storedConnection); const driver = requireEngineDriver(storedConnection);
systemConnection = await connectUtility(driver, storedConnection); systemConnection = await connectUtility(driver, storedConnection, 'app');
console.log(`RUNNING SCRIPT: CREATE DATABASE ${driver.dialect.quoteIdentifier(name)}`); console.log(`RUNNING SCRIPT: CREATE DATABASE ${driver.dialect.quoteIdentifier(name)}`);
if (driver.createDatabase) { if (driver.createDatabase) {
await driver.createDatabase(systemConnection, name); await driver.createDatabase(systemConnection, name);

View File

@@ -181,7 +181,7 @@ async function handleConnect(connection) {
storedConnection = connection; storedConnection = connection;
const driver = requireEngineDriver(storedConnection); const driver = requireEngineDriver(storedConnection);
systemConnection = await connectUtility(driver, storedConnection); systemConnection = await connectUtility(driver, storedConnection, 'app');
for (const [resolve] of afterConnectCallbacks) { for (const [resolve] of afterConnectCallbacks) {
resolve(); resolve();
} }

View File

@@ -5,7 +5,7 @@ async function executeQuery({ connection = undefined, systemConnection = undefin
console.log(`Execute query ${sql}`); console.log(`Execute query ${sql}`);
if (!driver) driver = requireEngineDriver(connection); if (!driver) driver = requireEngineDriver(connection);
const pool = systemConnection || (await connectUtility(driver, connection)); const pool = systemConnection || (await connectUtility(driver, connection, 'write'));
console.log(`Connected.`); console.log(`Connected.`);
await driver.script(pool, sql); await driver.script(pool, sql);

View File

@@ -21,7 +21,7 @@ async function generateDeploySql({
}) { }) {
if (!driver) driver = requireEngineDriver(connection); if (!driver) driver = requireEngineDriver(connection);
const pool = systemConnection || (await connectUtility(driver, connection)); const pool = systemConnection || (await connectUtility(driver, connection, 'read'));
if (!analysedStructure) { if (!analysedStructure) {
analysedStructure = await driver.analyseFull(pool); analysedStructure = await driver.analyseFull(pool);
} }

View File

@@ -6,7 +6,7 @@ async function queryReader({ connection, sql }) {
console.log(`Reading query ${sql}`); console.log(`Reading query ${sql}`);
const driver = requireEngineDriver(connection); const driver = requireEngineDriver(connection);
const pool = await connectUtility(driver, connection); const pool = await connectUtility(driver, connection, 'script');
console.log(`Connected.`); console.log(`Connected.`);
return await driver.readQuery(pool, sql); return await driver.readQuery(pool, sql);
} }

View File

@@ -4,7 +4,7 @@ const connectUtility = require('../utility/connectUtility');
async function tableReader({ connection, pureName, schemaName }) { async function tableReader({ connection, pureName, schemaName }) {
const driver = requireEngineDriver(connection); const driver = requireEngineDriver(connection);
const pool = await connectUtility(driver, connection); const pool = await connectUtility(driver, connection, 'read');
console.log(`Connected.`); console.log(`Connected.`);
const fullName = { pureName, schemaName }; const fullName = { pureName, schemaName };

View File

@@ -8,7 +8,7 @@ async function tableWriter({ connection, schemaName, pureName, driver, systemCon
if (!driver) { if (!driver) {
driver = requireEngineDriver(connection); driver = requireEngineDriver(connection);
} }
const pool = systemConnection || (await connectUtility(driver, connection)); const pool = systemConnection || (await connectUtility(driver, connection, 'write'));
console.log(`Connected.`); console.log(`Connected.`);
return await driver.writeTable(pool, { schemaName, pureName }, options); return await driver.writeTable(pool, { schemaName, pureName }, options);

View File

@@ -4,11 +4,47 @@ const fs = require('fs-extra');
const { decryptConnection } = require('./crypting'); const { decryptConnection } = require('./crypting');
const { getSshTunnel } = require('./sshTunnel'); const { getSshTunnel } = require('./sshTunnel');
const { getSshTunnelProxy } = require('./sshTunnelProxy'); const { getSshTunnelProxy } = require('./sshTunnelProxy');
const platformInfo = require('../utility/platformInfo');
const connections = require('../controllers/connections');
async function loadConnection(driver, storedConnection, connectionMode) {
const { allowShellConnection } = platformInfo;
if (connectionMode == 'app') {
return storedConnection;
}
if (storedConnection._id || !allowShellConnection) {
if (!storedConnection._id) {
throw new Error('Missing connection _id');
}
await connections._init();
const loaded = await connections.get({ conid: storedConnection._id });
const loadedWithDb = {
...loaded,
database: storedConnection.database,
};
if (loaded.isReadOnly) {
if (connectionMode == 'read') return loadedWithDb;
if (connectionMode == 'write') throw new Error('Cannot wwrite readonly connection');
if (connectionMode == 'script') {
if (driver.readOnlySessions) return loadedWithDb;
throw new Error('Cannot wwrite readonly connection');
}
}
return loadedWithDb;
}
return storedConnection;
}
async function connectUtility(driver, storedConnection, connectionMode) {
const connectionLoaded = await loadConnection(driver, storedConnection, connectionMode);
async function connectUtility(driver, storedConnection) {
const connection = { const connection = {
database: storedConnection.defaultDatabase, database: connectionLoaded.defaultDatabase,
...decryptConnection(storedConnection), ...decryptConnection(connectionLoaded),
}; };
if (!connection.port && driver.defaultPort) connection.port = driver.defaultPort.toString(); if (!connection.port && driver.defaultPort) connection.port = driver.defaultPort.toString();

View File

@@ -39,6 +39,7 @@ const platformInfo = {
environment: process.env.NODE_ENV, environment: process.env.NODE_ENV,
platform, platform,
runningInWebpack: !!process.env.WEBPACK_DEV_SERVER_URL, runningInWebpack: !!process.env.WEBPACK_DEV_SERVER_URL,
allowShellConnection: !!process.env.SHELL_CONNECTION || !!isElectron(),
defaultKeyfile: path.join(os.homedir(), '.ssh/id_rsa'), defaultKeyfile: path.join(os.homedir(), '.ssh/id_rsa'),
}; };

View File

@@ -417,10 +417,7 @@
{ {
functionName: menu.functionName, functionName: menu.functionName,
props: { props: {
connection: { connection: extractShellConnection(coninfo, data.database),
..._.omit(coninfo, ['_id', 'displayName']),
..._.pick(data, ['database']),
},
..._.pick(data, ['pureName', 'schemaName']), ..._.pick(data, ['pureName', 'schemaName']),
}, },
}, },
@@ -629,6 +626,7 @@
import ConfirmModal from '../modals/ConfirmModal.svelte'; import ConfirmModal from '../modals/ConfirmModal.svelte';
import { apiCall } from '../utility/api'; import { apiCall } from '../utility/api';
import InputTextModal from '../modals/InputTextModal.svelte'; import InputTextModal from '../modals/InputTextModal.svelte';
import { extractShellConnection } from '../impexp/createImpExpScript';
export let data; export let data;
export let passProps; export let passProps;

View File

@@ -112,6 +112,7 @@
import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte'; import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte';
import registerCommand from '../commands/registerCommand'; import registerCommand from '../commands/registerCommand';
import ErrorInfo from '../elements/ErrorInfo.svelte'; import ErrorInfo from '../elements/ErrorInfo.svelte';
import { extractShellConnection } from '../impexp/createImpExpScript';
import ConfirmNoSqlModal from '../modals/ConfirmNoSqlModal.svelte'; import ConfirmNoSqlModal from '../modals/ConfirmNoSqlModal.svelte';
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte'; import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
import ImportExportModal from '../modals/ImportExportModal.svelte'; import ImportExportModal from '../modals/ImportExportModal.svelte';
@@ -202,10 +203,7 @@
{ {
functionName: 'queryReader', functionName: 'queryReader',
props: { props: {
connection: { connection: extractShellConnection(coninfo, database),
..._.omit(coninfo, ['_id', 'displayName']),
database,
},
sql: getExportQuery(), sql: getExportQuery(),
}, },
}, },

View File

@@ -69,6 +69,7 @@
import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte'; import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte';
import registerCommand from '../commands/registerCommand'; import registerCommand from '../commands/registerCommand';
import { extractShellConnection } from '../impexp/createImpExpScript';
import ImportExportModal from '../modals/ImportExportModal.svelte'; import ImportExportModal from '../modals/ImportExportModal.svelte';
import { showModal } from '../modals/modalTools'; import { showModal } from '../modals/modalTools';
import { apiCall } from '../utility/api'; import { apiCall } from '../utility/api';
@@ -187,10 +188,7 @@
{ {
functionName: 'queryReader', functionName: 'queryReader',
props: { props: {
connection: { connection: extractShellConnection(coninfo, database),
..._.omit(coninfo, ['_id', 'displayName']),
database,
},
sql: display.getExportQuery(), sql: display.getExportQuery(),
}, },
}, },

View File

@@ -4,6 +4,7 @@ import getAsArray from '../utility/getAsArray';
import { getConnectionInfo } from '../utility/metadataLoaders'; import { getConnectionInfo } from '../utility/metadataLoaders';
import { findEngineDriver, findObjectLike } from 'dbgate-tools'; import { findEngineDriver, findObjectLike } from 'dbgate-tools';
import { findFileFormat } from '../plugins/fileformats'; import { findFileFormat } from '../plugins/fileformats';
import { getCurrentConfig } from '../stores';
export function getTargetName(extensions, source, values) { export function getTargetName(extensions, source, values) {
const key = `targetName_${source}`; const key = `targetName_${source}`;
@@ -33,17 +34,27 @@ function extractDriverApiParameters(values, direction, driver) {
return _.fromPairs(pairs); return _.fromPairs(pairs);
} }
export function extractShellConnection(connection, database) {
const config = getCurrentConfig();
return config.allowShellConnection
? {
..._.omit(connection, ['_id', 'displayName', 'databases', 'connectionColor']),
database,
}
: {
_id: connection._id,
engine: connection.engine,
database,
};
}
async function getConnection(extensions, storageType, conid, database) { async function getConnection(extensions, storageType, conid, database) {
if (storageType == 'database' || storageType == 'query') { if (storageType == 'database' || storageType == 'query') {
const conn = await getConnectionInfo({ conid }); const conn = await getConnectionInfo({ conid });
const driver = findEngineDriver(conn, extensions); const driver = findEngineDriver(conn, extensions);
return [ const connection = extractShellConnection(conn, database);
{ return [connection, driver];
..._.omit(conn, ['_id', 'displayName']),
database,
},
driver,
];
} }
return [null, null]; return [null, null];
} }
@@ -201,7 +212,7 @@ export default async function createImpExpScript(extensions, values, addEditorIn
// @ts-ignore // @ts-ignore
script.assign(targetVar, ...getTargetExpr(extensions, sourceName, values, targetConnection, targetDriver)); script.assign(targetVar, ...getTargetExpr(extensions, sourceName, values, targetConnection, targetDriver));
const colmap = normalizeExportColumnMap(values[`columns_${sourceName}`] ); const colmap = normalizeExportColumnMap(values[`columns_${sourceName}`]);
let colmapVar = null; let colmapVar = null;
if (colmap) { if (colmap) {