From 1801bef019cee77a99df7e3822145ad952465abb Mon Sep 17 00:00:00 2001 From: Christian Ratz <2630316+digitalgopnik@users.noreply.github.com> Date: Mon, 5 Jul 2021 09:30:52 +0200 Subject: [PATCH] feat: SSH Tunnel functionality (#81) * added ssh-tunnel-functionality for mysql-connections * remove autoformat-stuff * added identity for using ssh-key * added identity to mysqlclient to use sshkey * removed debug console.log * added ssh-tunnel-functionality for postgresqlclient * changed naming to sshKey for sshKey-input * refactoring code * fix lint * set dbConfig.ssl to null initially --- package.json | 1 + src/main/ipc-handlers/connection.js | 28 ++++- src/main/libs/ClientsFactory.js | 4 + src/main/libs/clients/MySQLClient.js | 29 ++++- src/main/libs/clients/PostgreSQLClient.js | 28 ++++- .../components/ModalEditConnection.vue | 100 +++++++++++++++- .../components/ModalNewConnection.vue | 109 +++++++++++++++++- src/renderer/i18n/de-DE.js | 6 +- src/renderer/i18n/en-US.js | 2 + src/renderer/i18n/fr-FR.js | 6 +- src/renderer/i18n/it-IT.js | 6 +- src/renderer/i18n/pt-BR.js | 6 +- src/renderer/scss/themes/dark-theme.scss | 6 +- 13 files changed, 308 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index f0d2feba..8fd8ed9b 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "source-map-support": "^0.5.16", "spectre.css": "^0.5.9", "sql-formatter": "^4.0.2", + "ssh2-promise": "^0.1.7", "v-mask": "^2.2.4", "vue-i18n": "^8.24.4", "vuedraggable": "^2.24.3", diff --git a/src/main/ipc-handlers/connection.js b/src/main/ipc-handlers/connection.js index 0db0b5fe..1731e2a9 100644 --- a/src/main/ipc-handlers/connection.js +++ b/src/main/ipc-handlers/connection.js @@ -24,13 +24,23 @@ export default connections => { }; } - const connection = ClientsFactory.getConnection({ - client: conn.client, - params - }); + if (conn.ssh) { + params.ssh = { + host: conn.sshHost, + username: conn.sshUser, + password: conn.sshPass, + port: conn.sshPort ? conn.sshPort : 22, + identity: conn.sshKey + }; + } try { + const connection = await ClientsFactory.getConnection({ + client: conn.client, + params + }); await connection.connect(); + await connection.select('1+1').run(); connection.destroy(); @@ -66,6 +76,16 @@ export default connections => { }; } + if (conn.ssh) { + params.ssh = { + host: conn.sshHost, + username: conn.sshUser, + password: conn.sshPass, + port: conn.sshPort ? conn.sshPort : 22, + identity: conn.sshKey + }; + } + try { const connection = ClientsFactory.getConnection({ client: conn.client, diff --git a/src/main/libs/ClientsFactory.js b/src/main/libs/ClientsFactory.js index cb087382..802d3faa 100644 --- a/src/main/libs/ClientsFactory.js +++ b/src/main/libs/ClientsFactory.js @@ -12,6 +12,10 @@ export class ClientsFactory { * @param {String} args.params.host * @param {Number} args.params.port * @param {String} args.params.password + * @param {String} args.params.ssh.host + * @param {String} args.params.ssh.username + * @param {String} args.params.ssh.password + * @param {Number} args.params.ssh.port * @param {Number=} args.poolSize * @returns Database Connection * @memberof ClientsFactory diff --git a/src/main/libs/clients/MySQLClient.js b/src/main/libs/clients/MySQLClient.js index 3ecc7ef7..44f81c8b 100644 --- a/src/main/libs/clients/MySQLClient.js +++ b/src/main/libs/clients/MySQLClient.js @@ -2,6 +2,7 @@ import mysql from 'mysql2/promise'; import { AntaresCore } from '../AntaresCore'; import dataTypes from 'common/data-types/mysql'; +import * as SSH2Promise from 'ssh2-promise'; export class MySQLClient extends AntaresCore { constructor (args) { @@ -104,11 +105,32 @@ export class MySQLClient extends AntaresCore { async connect () { delete this._params.application_name; - if (!this._poolSize) - this._connection = await mysql.createConnection(this._params); + const dbConfig = { + host: this._params.host, + port: this._params.port, + user: this._params.user, + password: this._params.password, + ssl: null + }; + + if (this._params.database?.length) dbConfig.database = this._params.database; + + if (this._params.ssl) dbConfig.ssl = { ...this._params.ssl }; + + if (this._params.ssh) { + this._ssh = new SSH2Promise({ ...this._params.ssh }); + + this._tunnel = await this._ssh.addTunnel({ + remoteAddr: this._params.host, + remotePort: this._params.port + }); + dbConfig.port = this._tunnel.localPort; + } + + if (!this._poolSize) this._connection = await mysql.createConnection(dbConfig); else { this._connection = mysql.createPool({ - ...this._params, + ...dbConfig, connectionLimit: this._poolSize, typeCast: (field, next) => { if (field.type === 'DATETIME') @@ -125,6 +147,7 @@ export class MySQLClient extends AntaresCore { */ destroy () { this._connection.end(); + if (this._ssh) this._ssh.close(); } /** diff --git a/src/main/libs/clients/PostgreSQLClient.js b/src/main/libs/clients/PostgreSQLClient.js index a3a05c78..ba0b18d1 100644 --- a/src/main/libs/clients/PostgreSQLClient.js +++ b/src/main/libs/clients/PostgreSQLClient.js @@ -3,6 +3,7 @@ import { Pool, Client, types } from 'pg'; import { parse } from 'pgsql-ast-parser'; import { AntaresCore } from '../AntaresCore'; import dataTypes from 'common/data-types/postgresql'; +import * as SSH2Promise from 'ssh2-promise'; function pgToString (value) { return value.toString(); @@ -51,13 +52,35 @@ export class PostgreSQLClient extends AntaresCore { * @memberof PostgreSQLClient */ async connect () { + const dbConfig = { + host: this._params.host, + port: this._params.port, + user: this._params.user, + password: this._params.password, + ssl: null + }; + + if (this._params.database?.length) dbConfig.database = this._params.database; + + if (this._params.ssl) dbConfig.ssl = { ...this._params.ssl }; + + if (this._params.ssh) { + this._ssh = new SSH2Promise({ ...this._params.ssh }); + + this._tunnel = await this._ssh.addTunnel({ + remoteAddr: this._params.host, + remotePort: this._params.port + }); + dbConfig.port = this._tunnel.localPort; + } + if (!this._poolSize) { - const client = new Client(this._params); + const client = new Client(dbConfig); await client.connect(); this._connection = client; } else { - const pool = new Pool({ ...this._params, max: this._poolSize }); + const pool = new Pool({ ...dbConfig, max: this._poolSize }); this._connection = pool; } } @@ -67,6 +90,7 @@ export class PostgreSQLClient extends AntaresCore { */ destroy () { this._connection.end(); + if (this._ssh) this._ssh.close(); } /** diff --git a/src/renderer/components/ModalEditConnection.vue b/src/renderer/components/ModalEditConnection.vue index 4501d338..0236c6dd 100644 --- a/src/renderer/components/ModalEditConnection.vue +++ b/src/renderer/components/ModalEditConnection.vue @@ -28,6 +28,13 @@ > {{ $t('word.ssl') }} +
  • + {{ $t('word.sshTunnel') }} +
  • @@ -208,7 +215,6 @@ />
    -
    @@ -231,6 +237,95 @@ :status="toast.status" />
    +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    + +
    @@ -213,7 +220,6 @@ />
    -
    @@ -236,6 +242,95 @@ :status="toast.status" />
    +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    + +