diff --git a/packages/api/src/controllers/cloud.js b/packages/api/src/controllers/cloud.js index 80db29e16..af4fa8c0f 100644 --- a/packages/api/src/controllers/cloud.js +++ b/packages/api/src/controllers/cloud.js @@ -10,7 +10,7 @@ const { } = require('../utility/cloudIntf'); const connections = require('./connections'); const socket = require('../utility/socket'); -const { recryptConnection, getInternalEncryptor } = require('../utility/crypting'); +const { recryptConnection, getInternalEncryptor, encryptConnection } = require('../utility/crypting'); const { getConnectionLabel, getLogger, extractErrorLogData } = require('dbgate-tools'); const logger = getLogger('cloud'); const _ = require('lodash'); @@ -113,7 +113,7 @@ module.exports = { moveConnectionCloud_meta: true, async moveConnectionCloud({ conid, folid }) { const conn = await connections.getCore({ conid }); - const folderEncryptor = getCloudFolderEncryptor(folid); + const folderEncryptor = await getCloudFolderEncryptor(folid); const recryptedConn = recryptConnection(conn, getInternalEncryptor(), folderEncryptor); const connToSend = _.omit(recryptedConn, ['_id']); const resp = await putCloudContent( @@ -125,4 +125,24 @@ module.exports = { ); return resp; }, + + saveConnection_meta: true, + async saveConnection({ folid, connection }) { + const folderEncryptor = await getCloudFolderEncryptor(folid); + const recryptedConn = encryptConnection(connection, folderEncryptor); + const resp = await putCloudContent( + folid, + undefined, + JSON.stringify(recryptedConn), + getConnectionLabel(recryptedConn), + 'connection' + ); + + const { cntid } = resp; + socket.emitChanged('cloud-content-changed'); + return { + ...recryptedConn, + _id: `cloud://${folid}/${cntid}`, + }; + }, }; diff --git a/packages/api/src/utility/crypting.js b/packages/api/src/utility/crypting.js index 171b75904..4d3e9070a 100644 --- a/packages/api/src/utility/crypting.js +++ b/packages/api/src/utility/crypting.js @@ -81,11 +81,11 @@ function decryptPasswordString(password) { return password; } -function encryptObjectPasswordField(obj, field) { +function encryptObjectPasswordField(obj, field, encryptor = null) { if (obj && obj[field] && !obj[field].startsWith('crypt:')) { return { ...obj, - [field]: 'crypt:' + getInternalEncryptor().encrypt(obj[field]), + [field]: 'crypt:' + (encryptor || getInternalEncryptor()).encrypt(obj[field]), }; } return obj; @@ -101,11 +101,11 @@ function decryptObjectPasswordField(obj, field) { return obj; } -function encryptConnection(connection) { +function encryptConnection(connection, encryptor = null) { if (connection.passwordMode != 'saveRaw') { - connection = encryptObjectPasswordField(connection, 'password'); - connection = encryptObjectPasswordField(connection, 'sshPassword'); - connection = encryptObjectPasswordField(connection, 'sshKeyfilePassword'); + connection = encryptObjectPasswordField(connection, 'password', encryptor); + connection = encryptObjectPasswordField(connection, 'sshPassword', encryptor); + connection = encryptObjectPasswordField(connection, 'sshKeyfilePassword', encryptor); } return connection; } diff --git a/packages/web/src/appobj/CloudContentAppObject.svelte b/packages/web/src/appobj/CloudContentAppObject.svelte index 7c811be1f..656626057 100644 --- a/packages/web/src/appobj/CloudContentAppObject.svelte +++ b/packages/web/src/appobj/CloudContentAppObject.svelte @@ -48,7 +48,7 @@ 0 && { text: _t('connection.moveToCloudFolder', { defaultMessage: 'Move to cloud folder' }), - submenu: passProps?.cloudContentList?.map(fld => ({ - text: fld.name, - onClick: () => { - apiCall('cloud/move-connection-cloud', { conid: data._id, folid: fld.folid }); - }, - })), + submenu: passProps?.cloudContentList + ?.filter(x => x.role == 'write' || x.role == 'admin') + ?.map(fld => ({ + text: fld.name, + onClick: () => { + apiCall('cloud/move-connection-cloud', { conid: data._id, folid: fld.folid }); + }, + })), }, ], { divider: true }, diff --git a/packages/web/src/commands/stdCommands.ts b/packages/web/src/commands/stdCommands.ts index 2432d0fcb..4c1d822de 100644 --- a/packages/web/src/commands/stdCommands.ts +++ b/packages/web/src/commands/stdCommands.ts @@ -124,6 +124,27 @@ registerCommand({ }, }); +registerCommand({ + id: 'new.connectionOnCloud', + toolbar: true, + icon: 'img cloud-connection', + toolbarName: 'Add connection on cloud', + category: 'New', + toolbarOrder: 1, + name: 'Connection on Cloud', + testEnabled: () => !getCurrentConfig()?.runAsPortal && !getCurrentConfig()?.storageDatabase, + onClick: () => { + openNewTab({ + title: 'New Connection on Cloud', + icon: 'img cloud-connection', + tabComponent: 'ConnectionTab', + props: { + saveOnCloud: true, + }, + }); + }, +}); + registerCommand({ id: 'new.connection.folder', toolbar: true, diff --git a/packages/web/src/forms/FormCloudFolderSelect.svelte b/packages/web/src/forms/FormCloudFolderSelect.svelte new file mode 100644 index 000000000..a6c215743 --- /dev/null +++ b/packages/web/src/forms/FormCloudFolderSelect.svelte @@ -0,0 +1,19 @@ + + + diff --git a/packages/web/src/icons/FontIcon.svelte b/packages/web/src/icons/FontIcon.svelte index c3a338eee..dcc1a68a5 100644 --- a/packages/web/src/icons/FontIcon.svelte +++ b/packages/web/src/icons/FontIcon.svelte @@ -270,6 +270,7 @@ 'img role': 'mdi mdi-account-group color-icon-blue', 'img admin': 'mdi mdi-security color-icon-blue', 'img auth': 'mdi mdi-account-key color-icon-blue', + 'img cloud-connection': 'mdi mdi-cloud-lock color-icon-blue', 'img add': 'mdi mdi-plus-circle color-icon-green', 'img minus': 'mdi mdi-minus-circle color-icon-red', diff --git a/packages/web/src/modals/ChooseCloudFolderModal.svelte b/packages/web/src/modals/ChooseCloudFolderModal.svelte new file mode 100644 index 000000000..955e50cd0 --- /dev/null +++ b/packages/web/src/modals/ChooseCloudFolderModal.svelte @@ -0,0 +1,40 @@ + + +{#if $cloudContentList} + x.isPrivate)?.folid }}> + + Choose cloud folder + +
{message}
+ + + + + { + closeCurrentModal(); + console.log('onConfirm', e.detail); + onConfirm(e.detail.cloudFolder); + }} + /> + + +
+
+{/if} diff --git a/packages/web/src/tabs/ConnectionTab.svelte b/packages/web/src/tabs/ConnectionTab.svelte index 8c1da2f5c..ebc5d2fa3 100644 --- a/packages/web/src/tabs/ConnectionTab.svelte +++ b/packages/web/src/tabs/ConnectionTab.svelte @@ -36,6 +36,7 @@ import ConnectionAdvancedDriverFields from '../settings/ConnectionAdvancedDriverFields.svelte'; import DatabaseLoginModal from '../modals/DatabaseLoginModal.svelte'; import { _t } from '../translations'; + import ChooseCloudFolderModal from '../modals/ChooseCloudFolderModal.svelte'; export let connection; export let tabid; @@ -44,6 +45,7 @@ export let inlineTabs = false; export let onlyTestButton; + export let saveOnCloud = false; let isTesting; let sqlConnectResult; @@ -157,26 +159,53 @@ $: currentConnection = getCurrentConnectionCore($values, driver); async function handleSave() { - let connection = getCurrentConnection(); - connection = { - ...connection, - unsaved: false, - }; - const saved = await apiCall('connections/save', connection); - $values = { - ...$values, - _id: saved._id, - unsaved: false, - }; - changeTab(tabid, tab => ({ - ...tab, - title: getConnectionLabel(saved), - props: { - ...tab.props, - conid: saved._id, - }, - })); - showSnackbarSuccess('Connection saved'); + if (saveOnCloud) { + showModal(ChooseCloudFolderModal, { + requiredRoleVariants: ['write', 'admin'], + message: 'Choose cloud folder to saved connection', + onConfirm: async folid => { + let connection = getCurrentConnection(); + const saved = await apiCall('cloud/save-connection', { folid, connection }); + if (saved?._id) { + $values = { + ...$values, + _id: saved._id, + unsaved: false, + }; + changeTab(tabid, tab => ({ + ...tab, + title: getConnectionLabel(saved), + props: { + ...tab.props, + conid: saved._id, + }, + })); + showSnackbarSuccess('Connection saved'); + } + }, + }); + } else { + let connection = getCurrentConnection(); + connection = { + ...connection, + unsaved: false, + }; + const saved = await apiCall('connections/save', connection); + $values = { + ...$values, + _id: saved._id, + unsaved: false, + }; + changeTab(tabid, tab => ({ + ...tab, + title: getConnectionLabel(saved), + props: { + ...tab.props, + conid: saved._id, + }, + })); + showSnackbarSuccess('Connection saved'); + } } async function handleConnect() { @@ -287,7 +316,9 @@ {:else if isConnected} {:else} - + {#if $values._id || !saveOnCloud} + + {/if} {#if isTesting} {:else} diff --git a/packages/web/src/widgets/PrivateCloudWidget.svelte b/packages/web/src/widgets/PrivateCloudWidget.svelte index b79066681..817bca525 100644 --- a/packages/web/src/widgets/PrivateCloudWidget.svelte +++ b/packages/web/src/widgets/PrivateCloudWidget.svelte @@ -111,6 +111,9 @@ }); }, }, + { + command: 'new.connectionOnCloud', + }, ]; }