encrypting cloud content

This commit is contained in:
SPRINX0\prochazka
2025-05-22 15:48:59 +02:00
parent 1b8bb0c1fd
commit f4a879a452
9 changed files with 89 additions and 34 deletions

View File

@@ -140,8 +140,8 @@ module.exports = {
createCloudLoginSession_meta: true, createCloudLoginSession_meta: true,
async createCloudLoginSession({ client }) { async createCloudLoginSession({ client }) {
const res = await createDbGateIdentitySession(client); const res = await createDbGateIdentitySession(client);
startCloudTokenChecking(res.sid, token => { startCloudTokenChecking(res.sid, tokenHolder => {
socket.emit('got-cloud-token', { token }); socket.emit('got-cloud-token', tokenHolder);
}); });
return res; return res;
}, },

View File

@@ -6,6 +6,7 @@ const {
callCloudApiPost, callCloudApiPost,
getCloudFolderEncryptor, getCloudFolderEncryptor,
getCloudContent, getCloudContent,
putCloudContent,
} = require('../utility/cloudIntf'); } = require('../utility/cloudIntf');
const connections = require('./connections'); const connections = require('./connections');
const socket = require('../utility/socket'); const socket = require('../utility/socket');
@@ -55,7 +56,7 @@ module.exports = {
putContent_meta: true, putContent_meta: true,
async putContent({ folid, cntid, content, name, type }) { async putContent({ folid, cntid, content, name, type }) {
await callCloudApiPost(`put-content`, { folid, cntid, content, name, type }); putCloudContent(folid, cntid, content, name, type);
socket.emitChanged('cloud-content-changed'); socket.emitChanged('cloud-content-changed');
return { return {
status: 'ok', status: 'ok',

View File

@@ -64,7 +64,7 @@ function startCloudTokenChecking(sid, callback) {
if (resp.data.status == 'ok') { if (resp.data.status == 'ok') {
clearInterval(interval); clearInterval(interval);
callback(resp.data.token); callback(resp.data);
} }
} catch (err) { } catch (err) {
logger.error(extractErrorLogData(err), 'Error checking cloud token'); logger.error(extractErrorLogData(err), 'Error checking cloud token');
@@ -123,17 +123,26 @@ async function collectCloudFilesSearchTags() {
return res; return res;
} }
async function getCloudSigninHeaders() { async function getCloudSigninHolder() {
const settingsValue = await config.getSettings(); const settingsValue = await config.getSettings();
const value = settingsValue['cloudSigninToken']; const holder = settingsValue['cloudSigninTokenHolder'];
if (value) { return holder;
}
async function getCloudSigninHeaders(holder = null) {
if (!holder) {
holder = await getCloudSigninHolder();
}
if (holder) {
return { return {
'x-cloud-login': value, 'x-cloud-login': holder.token,
}; };
} }
return null; return null;
} }
let cloudFilesWereUpdated = false;
async function updateCloudFiles() { async function updateCloudFiles() {
let lastCloudFilesTags; let lastCloudFilesTags;
try { try {
@@ -151,7 +160,9 @@ async function updateCloudFiles() {
logger.info({ tags, lastCheckedTm }, 'Downloading cloud files'); logger.info({ tags, lastCheckedTm }, 'Downloading cloud files');
const resp = await axios.default.get( const resp = await axios.default.get(
`${DBGATE_CLOUD_URL}/public-cloud-updates?lastCheckedTm=${lastCheckedTm}&tags=${tags}`, `${DBGATE_CLOUD_URL}/public-cloud-updates?lastCheckedTm=${lastCheckedTm}&tags=${tags}&isRefresh=${
cloudFilesWereUpdated ? 1 : 0
}`,
{ {
headers: { headers: {
...getLicenseHttpHeaders(), ...getLicenseHttpHeaders(),
@@ -159,6 +170,7 @@ async function updateCloudFiles() {
}, },
} }
); );
cloudFilesWereUpdated = true;
logger.info(`Downloaded ${resp.data.length} cloud files`); logger.info(`Downloaded ${resp.data.length} cloud files`);
@@ -210,26 +222,33 @@ async function refreshPublicFiles() {
} }
} }
async function callCloudApiGet(endpoint) { async function callCloudApiGet(endpoint, signinHolder = null, additionalHeaders = {}) {
const signinHeaders = await getCloudSigninHeaders(); if (!signinHolder) {
if (!signinHeaders) { signinHolder = await getCloudSigninHolder();
}
if (!signinHolder) {
return null; return null;
} }
const signinHeaders = await getCloudSigninHeaders(signinHolder);
const resp = await axios.default.get(`${DBGATE_CLOUD_URL}/${endpoint}`, { const resp = await axios.default.get(`${DBGATE_CLOUD_URL}/${endpoint}`, {
headers: { headers: {
...getLicenseHttpHeaders(), ...getLicenseHttpHeaders(),
...signinHeaders, ...signinHeaders,
...additionalHeaders,
}, },
}); });
return resp.data; return resp.data;
} }
async function callCloudApiPost(endpoint, body) { async function callCloudApiPost(endpoint, body, signinHolder = null) {
const signinHeaders = await getCloudSigninHeaders(); if (!signinHolder) {
if (!signinHeaders) { signinHolder = await getCloudSigninHolder();
}
if (!signinHolder) {
return null; return null;
} }
const signinHeaders = await getCloudSigninHeaders(signinHolder);
const resp = await axios.default.post(`${DBGATE_CLOUD_URL}/${endpoint}`, body, { const resp = await axios.default.post(`${DBGATE_CLOUD_URL}/${endpoint}`, body, {
headers: { headers: {
@@ -249,8 +268,45 @@ async function getCloudFolderEncryptor(folid) {
} }
async function getCloudContent(folid, cntid) { async function getCloudContent(folid, cntid) {
const { content, name, type } = await callCloudApiGet(`content/${folid}/${cntid}`); const signinHolder = await getCloudSigninHolder();
return { content, name, type }; if (!signinHolder) {
throw new Error('No signed in');
}
const encryptor = simpleEncryptor.createEncryptor(signinHolder.encryptionKey);
const { content, name, type } = await callCloudApiGet(`content/${folid}/${cntid}`, signinHolder, {
'x-kehid': signinHolder.kehid,
});
return {
content: encryptor.decrypt(content),
name,
type,
};
}
async function putCloudContent(folid, cntid, content, name, type) {
const signinHolder = await getCloudSigninHolder();
if (!signinHolder) {
throw new Error('No signed in');
}
const encryptor = simpleEncryptor.createEncryptor(signinHolder.encryptionKey);
await callCloudApiPost(
`put-content`,
{
folid,
cntid,
name,
type,
kehid: signinHolder.kehid,
content: encryptor.encrypt(content),
},
signinHolder
);
socket.emitChanged('cloud-content-changed');
} }
const cloudConnectionCache = {}; const cloudConnectionCache = {};
@@ -278,4 +334,5 @@ module.exports = {
getCloudFolderEncryptor, getCloudFolderEncryptor,
getCloudContent, getCloudContent,
loadCachedCloudConnection, loadCachedCloudConnection,
putCloudContent,
}; };

View File

@@ -108,7 +108,7 @@
import _ from 'lodash'; import _ from 'lodash';
import AppObjectCore from './AppObjectCore.svelte'; import AppObjectCore from './AppObjectCore.svelte';
import { import {
cloudSigninToken, cloudSigninTokenHolder,
currentDatabase, currentDatabase,
DEFAULT_CONNECTION_SEARCH_SETTINGS, DEFAULT_CONNECTION_SEARCH_SETTINGS,
expandedConnections, expandedConnections,
@@ -334,7 +334,7 @@
onClick: handleDuplicate, onClick: handleDuplicate,
}, },
!$openedConnections.includes(data._id) && !$openedConnections.includes(data._id) &&
$cloudSigninToken && $cloudSigninTokenHolder &&
passProps?.cloudContentList?.length > 0 && { passProps?.cloudContentList?.length > 0 && {
text: _t('connection.moveToCloudFolder', { defaultMessage: 'Move to cloud folder' }), text: _t('connection.moveToCloudFolder', { defaultMessage: 'Move to cloud folder' }),
submenu: passProps?.cloudContentList?.map(fld => ({ submenu: passProps?.cloudContentList?.map(fld => ({

View File

@@ -1,5 +1,5 @@
import { import {
cloudSigninToken, cloudSigninTokenHolder,
currentDatabase, currentDatabase,
currentTheme, currentTheme,
emptyConnectionGroupNames, emptyConnectionGroupNames,
@@ -668,7 +668,7 @@ registerCommand({
category: 'Cloud', category: 'Cloud',
name: 'Logout', name: 'Logout',
onClick: () => { onClick: () => {
cloudSigninToken.set(null); cloudSigninTokenHolder.set(null);
}, },
}); });

View File

@@ -182,14 +182,10 @@ export const focusedConnectionOrDatabase = writable<{ conid: string; database?:
export const focusedTreeDbKey = writable<{ key: string; root: string; type: string; text: string }>(null); export const focusedTreeDbKey = writable<{ key: string; root: string; type: string; text: string }>(null);
export const cloudSigninToken = writableSettingsValue(null, 'cloudSigninToken'); export const cloudSigninTokenHolder = writableSettingsValue(null, 'cloudSigninTokenHolder');
export const cloudConnectionsStore = writable({}); export const cloudConnectionsStore = writable({});
// export const cloudSigninToken = getElectron()
// ? writableSettingsValue(null, 'cloudSigninToken')
// : writableWithStorage(null, 'cloudSigninToken');
export const DEFAULT_OBJECT_SEARCH_SETTINGS = { export const DEFAULT_OBJECT_SEARCH_SETTINGS = {
pureName: true, pureName: true,
schemaName: false, schemaName: false,

View File

@@ -14,7 +14,7 @@ import { batchDispatchCacheTriggers, dispatchCacheChange } from './cache';
import { isAdminPage, isOneOfPage } from './pageDefs'; import { isAdminPage, isOneOfPage } from './pageDefs';
import { openWebLink } from './simpleTools'; import { openWebLink } from './simpleTools';
import { serializeJsTypesReplacer } from 'dbgate-tools'; import { serializeJsTypesReplacer } from 'dbgate-tools';
import { cloudSigninToken } from '../stores'; import { cloudSigninTokenHolder } from '../stores';
export const strmid = uuidv1(); export const strmid = uuidv1();
@@ -281,8 +281,9 @@ export function installNewVolatileConnectionListener() {
} }
export function installNewCloudTokenListener() { export function installNewCloudTokenListener() {
apiOn('got-cloud-token', async ({ token }) => { apiOn('got-cloud-token', async tokenHolder => {
cloudSigninToken.set(token); console.log('HOLDER', tokenHolder);
cloudSigninTokenHolder.set(tokenHolder);
}); });
} }

View File

@@ -16,7 +16,7 @@
import { apiCall } from '../utility/api'; import { apiCall } from '../utility/api';
import { import {
cloudConnectionsStore, cloudConnectionsStore,
cloudSigninToken, cloudSigninTokenHolder,
currentDatabase, currentDatabase,
expandedConnections, expandedConnections,
openedConnections, openedConnections,
@@ -82,7 +82,7 @@
name="privateCloud" name="privateCloud"
height="50%" height="50%"
storageName="privateCloudItems" storageName="privateCloudItems"
skip={!$cloudSigninToken} skip={!$cloudSigninTokenHolder}
> >
<WidgetsInnerContainer> <WidgetsInnerContainer>
<SearchBoxWrapper> <SearchBoxWrapper>

View File

@@ -9,7 +9,7 @@
visibleHamburgerMenuWidget, visibleHamburgerMenuWidget,
lockedDatabaseMode, lockedDatabaseMode,
getCurrentConfig, getCurrentConfig,
cloudSigninToken, cloudSigninTokenHolder,
} from '../stores'; } from '../stores';
import mainMenuDefinition from '../../../../app/src/mainMenuDefinition'; import mainMenuDefinition from '../../../../app/src/mainMenuDefinition';
import hasPermission from '../utility/hasPermission'; import hasPermission from '../utility/hasPermission';
@@ -149,7 +149,7 @@
{#each widgets {#each widgets
.filter(x => x && hasPermission(`widgets/${x.name}`)) .filter(x => x && hasPermission(`widgets/${x.name}`))
.filter(x => !x.isPremiumPromo || !isProApp()) .filter(x => !x.isPremiumPromo || !isProApp())
.filter(x => x.name != 'cloud-private' || $cloudSigninToken) as item} .filter(x => x.name != 'cloud-private' || $cloudSigninTokenHolder) as item}
<div <div
class="wrapper" class="wrapper"
class:selected={item.name == $visibleSelectedWidget} class:selected={item.name == $visibleSelectedWidget}
@@ -176,7 +176,7 @@
<FontIcon icon={$lockedDatabaseMode ? 'icon locked-database-mode' : 'icon unlocked-database-mode'} /> <FontIcon icon={$lockedDatabaseMode ? 'icon locked-database-mode' : 'icon unlocked-database-mode'} />
</div> --> </div> -->
{#if $cloudSigninToken} {#if $cloudSigninTokenHolder}
<div <div
class="wrapper" class="wrapper"
on:click={handleCloudAccountMenu} on:click={handleCloudAccountMenu}