mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-19 01:26:01 +00:00
SYNC: Merge pull request #8 from dbgate/feature/db-table-permissions
This commit is contained in:
@@ -167,7 +167,7 @@ await dbgateApi.deployDb(${JSON.stringify(
|
||||
isProApp() && { text: 'Data deployer', onClick: handleOpenDataDeployTab },
|
||||
$currentDatabase && [
|
||||
{ text: 'Generate deploy DB SQL', onClick: handleGenerateDeploySql },
|
||||
{ text: 'Shell: Deploy DB', onClick: handleGenerateDeployScript },
|
||||
hasPermission(`run-shell-script`) && { text: 'Shell: Deploy DB', onClick: handleGenerateDeployScript },
|
||||
],
|
||||
data.name != 'default' &&
|
||||
isProApp() &&
|
||||
|
||||
@@ -382,7 +382,8 @@
|
||||
$extensions,
|
||||
$currentDatabase,
|
||||
$apps,
|
||||
$openedSingleDatabaseConnections
|
||||
$openedSingleDatabaseConnections,
|
||||
data.databasePermissionRole,
|
||||
),
|
||||
],
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@
|
||||
$extensions,
|
||||
$currentDatabase,
|
||||
$apps,
|
||||
$openedSingleDatabaseConnections
|
||||
$openedSingleDatabaseConnections,
|
||||
databasePermissionRole
|
||||
) {
|
||||
const apps = filterAppsForDatabase(connection, name, $apps);
|
||||
const handleNewQuery = () => {
|
||||
@@ -412,11 +413,12 @@ await dbgateApi.executeQuery(${JSON.stringify(
|
||||
driver?.databaseEngineTypes?.includes('sql') || driver?.databaseEngineTypes?.includes('document');
|
||||
|
||||
return [
|
||||
hasPermission(`dbops/query`) && {
|
||||
onClick: handleNewQuery,
|
||||
text: _t('database.newQuery', { defaultMessage: 'New query' }),
|
||||
isNewQuery: true,
|
||||
},
|
||||
hasPermission(`dbops/query`) &&
|
||||
isAllowedDatabaseRunScript(databasePermissionRole) && {
|
||||
onClick: handleNewQuery,
|
||||
text: _t('database.newQuery', { defaultMessage: 'New query' }),
|
||||
isNewQuery: true,
|
||||
},
|
||||
hasPermission(`dbops/model/edit`) &&
|
||||
!connection.isReadOnly &&
|
||||
driver?.databaseEngineTypes?.includes('sql') && {
|
||||
@@ -545,12 +547,13 @@ await dbgateApi.executeQuery(${JSON.stringify(
|
||||
{ divider: true },
|
||||
|
||||
driver?.databaseEngineTypes?.includes('sql') &&
|
||||
hasPermission(`run-shell-script`) &&
|
||||
hasPermission(`dbops/dropdb`) && {
|
||||
onClick: handleGenerateDropAllObjectsScript,
|
||||
text: _t('database.shellDropAllObjects', { defaultMessage: 'Shell: Drop all objects' }),
|
||||
},
|
||||
|
||||
{
|
||||
hasPermission(`run-shell-script`) && {
|
||||
onClick: handleGenerateRunScript,
|
||||
text: _t('database.shellRunScript', { defaultMessage: 'Shell: Run script' }),
|
||||
},
|
||||
@@ -625,7 +628,7 @@ await dbgateApi.executeQuery(${JSON.stringify(
|
||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||
import { closeMultipleTabs } from '../tabpanel/TabsPanel.svelte';
|
||||
import NewCollectionModal from '../modals/NewCollectionModal.svelte';
|
||||
import hasPermission from '../utility/hasPermission';
|
||||
import hasPermission, { isAllowedDatabaseRunScript } from '../utility/hasPermission';
|
||||
import { openImportExportTab } from '../utility/importExportTools';
|
||||
import newTable from '../tableeditor/newTable';
|
||||
import { loadSchemaList, switchCurrentDatabase } from '../utility/common';
|
||||
@@ -636,6 +639,7 @@ await dbgateApi.executeQuery(${JSON.stringify(
|
||||
import { getNumberIcon } from '../icons/FontIcon.svelte';
|
||||
import { getDatabaseClickActionSetting } from '../settings/settingsTools';
|
||||
import { _t } from '../translations';
|
||||
import { dataGridRowHeight } from '../datagrid/DataGridRowHeightMeter.svelte';
|
||||
|
||||
export let data;
|
||||
export let passProps;
|
||||
@@ -647,7 +651,8 @@ await dbgateApi.executeQuery(${JSON.stringify(
|
||||
$extensions,
|
||||
$currentDatabase,
|
||||
$apps,
|
||||
$openedSingleDatabaseConnections
|
||||
$openedSingleDatabaseConnections,
|
||||
data.databasePermissionRole
|
||||
);
|
||||
}
|
||||
|
||||
@@ -697,6 +702,9 @@ await dbgateApi.executeQuery(${JSON.stringify(
|
||||
).length
|
||||
)
|
||||
: ''}
|
||||
statusIconBefore={data.databasePermissionRole == 'read_content' || data.databasePermissionRole == 'view'
|
||||
? 'icon lock'
|
||||
: null}
|
||||
menu={createMenu}
|
||||
showPinnedInsteadOfUnpin={passProps?.showPinnedInsteadOfUnpin}
|
||||
onPin={isPinned ? null : () => pinnedDatabases.update(list => [...list, data])}
|
||||
|
||||
@@ -703,15 +703,29 @@
|
||||
}
|
||||
|
||||
function createMenus(objectTypeField, driver, data): ReturnType<typeof createMenusCore> {
|
||||
return createMenusCore(objectTypeField, driver, data).filter(x => {
|
||||
if (x.scriptTemplate) {
|
||||
return hasPermission(`dbops/sql-template/${x.scriptTemplate}`);
|
||||
const coreMenus = createMenusCore(objectTypeField, driver, data);
|
||||
|
||||
const filteredSumenus = coreMenus.map(item => {
|
||||
if (!item.submenu) {
|
||||
return item;
|
||||
}
|
||||
if (x.sqlGeneratorProps) {
|
||||
return hasPermission(`dbops/sql-generator`);
|
||||
}
|
||||
return true;
|
||||
return {
|
||||
...item,
|
||||
submenu: item.submenu.filter(x => {
|
||||
if (x.scriptTemplate) {
|
||||
return hasPermission(`dbops/sql-template/${x.scriptTemplate}`);
|
||||
}
|
||||
if (x.sqlGeneratorProps) {
|
||||
return hasPermission(`dbops/sql-generator`);
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const filteredNoEmptySubmenus = filteredSumenus.filter(x => !x.submenu || x.submenu.length > 0);
|
||||
|
||||
return filteredNoEmptySubmenus;
|
||||
}
|
||||
|
||||
function getObjectTitle(connection, schemaName, pureName) {
|
||||
@@ -1062,6 +1076,7 @@
|
||||
: null}
|
||||
extInfo={getExtInfo(data)}
|
||||
isChoosed={matchDatabaseObjectAppObject($selectedDatabaseObjectAppObject, data)}
|
||||
statusIconBefore={data.tablePermissionRole == 'read' ? 'icon lock' : null}
|
||||
on:click={() => handleObjectClick(data, 'leftClick')}
|
||||
on:middleclick={() => handleObjectClick(data, 'middleClick')}
|
||||
on:dblclick={() => handleObjectClick(data, 'dblClick')}
|
||||
|
||||
@@ -322,6 +322,7 @@ registerCommand({
|
||||
toolbar: true,
|
||||
toolbarName: 'New table',
|
||||
testEnabled: () => {
|
||||
if (!hasPermission('dbops/model/edit')) return false;
|
||||
const driver = findEngineDriver(get(currentDatabase)?.connection, getExtensions());
|
||||
return !!get(currentDatabase) && driver?.databaseEngineTypes?.includes('sql');
|
||||
},
|
||||
@@ -671,7 +672,7 @@ registerCommand({
|
||||
name: 'Export database',
|
||||
toolbar: true,
|
||||
icon: 'icon export',
|
||||
testEnabled: () => getCurrentDatabase() != null,
|
||||
testEnabled: () => getCurrentDatabase() != null && hasPermission(`dbops/export`),
|
||||
onClick: () => {
|
||||
openImportExportTab({
|
||||
targetStorageType: getDefaultFileFormat(getExtensions()).storageType,
|
||||
@@ -691,7 +692,8 @@ if (isProApp()) {
|
||||
icon: 'icon compare',
|
||||
testEnabled: () =>
|
||||
getCurrentDatabase() != null &&
|
||||
findEngineDriver(getCurrentDatabase()?.connection, getExtensions())?.databaseEngineTypes?.includes('sql'),
|
||||
findEngineDriver(getCurrentDatabase()?.connection, getExtensions())?.databaseEngineTypes?.includes('sql')
|
||||
&& hasPermission(`dbops/export`),
|
||||
onClick: () => {
|
||||
openNewTab(
|
||||
{
|
||||
|
||||
@@ -77,7 +77,10 @@
|
||||
{ showHintColumns: getBoolSettingsValue('dataGrid.showHintColumns', true) },
|
||||
$serverVersion,
|
||||
table => getDictionaryDescription(table, conid, database, $apps, $connections),
|
||||
forceReadOnly || $connection?.isReadOnly,
|
||||
forceReadOnly ||
|
||||
$connection?.isReadOnly ||
|
||||
extendedDbInfo?.tables?.find(x => x.pureName == pureName && x.schemaName == schemaName)
|
||||
?.tablePermissionRole == 'read',
|
||||
isRawMode,
|
||||
$settingsValue
|
||||
)
|
||||
|
||||
@@ -74,6 +74,8 @@
|
||||
'icon arrow-link': 'mdi mdi-arrow-top-right-thick',
|
||||
'icon reset': 'mdi mdi-cancel',
|
||||
'icon send': 'mdi mdi-send',
|
||||
'icon regex': 'mdi mdi-regex',
|
||||
'icon list': 'mdi mdi-format-list-bulleted-triangle',
|
||||
|
||||
'icon window-restore': 'mdi mdi-window-restore',
|
||||
'icon window-maximize': 'mdi mdi-window-maximize',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import runCommand from '../commands/runCommand';
|
||||
import newQuery from '../query/newQuery';
|
||||
import { commandsCustomized, selectedWidget } from '../stores';
|
||||
import hasPermission from '../utility/hasPermission';
|
||||
import { isProApp } from '../utility/proTools';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
@@ -19,6 +20,7 @@
|
||||
newQuery({ multiTabIndex });
|
||||
},
|
||||
testid: 'NewObjectModal_query',
|
||||
testEnabled: () => hasPermission('dbops/query'),
|
||||
},
|
||||
{
|
||||
icon: 'icon connection',
|
||||
@@ -114,7 +116,7 @@
|
||||
isProFeature: true,
|
||||
disabledMessage: 'Database chat is not available for current database',
|
||||
testid: 'NewObjectModal_databaseChat',
|
||||
}
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
@@ -122,7 +124,11 @@
|
||||
<div class="create-header">Create new</div>
|
||||
<div class="wrapper">
|
||||
{#each NEW_ITEMS as item}
|
||||
{@const enabled = item.command ? $commandsCustomized[item.command]?.enabled : true}
|
||||
{@const enabled = item.command
|
||||
? $commandsCustomized[item.command]?.enabled
|
||||
: item.testEnabled
|
||||
? item.testEnabled()
|
||||
: true}
|
||||
<NewObjectButton
|
||||
icon={item.icon}
|
||||
title={item.title}
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
execute: true,
|
||||
toggleComment: true,
|
||||
findReplace: true,
|
||||
executeAdditionalCondition: () => getCurrentEditor()?.hasConnection(),
|
||||
executeAdditionalCondition: () => getCurrentEditor()?.hasConnection() && hasPermission('dbops/query'),
|
||||
copyPaste: true,
|
||||
});
|
||||
registerCommand({
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
import invalidateCommands from '../commands/invalidateCommands';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
|
||||
import { useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
|
||||
import { getTableInfo, useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
|
||||
import { scriptToSql } from 'dbgate-sqltree';
|
||||
import { extensions, lastUsedDefaultActions } from '../stores';
|
||||
import ConfirmSqlModal from '../modals/ConfirmSqlModal.svelte';
|
||||
@@ -156,30 +156,47 @@
|
||||
}
|
||||
}
|
||||
|
||||
export function save() {
|
||||
export async function save() {
|
||||
const driver = findEngineDriver($connection, $extensions);
|
||||
const tablePermissionRole = (await getTableInfo({ conid, database, schemaName, pureName }))?.tablePermissionRole;
|
||||
|
||||
const script = driver.createSaveChangeSetScript($changeSetStore?.value, $dbinfo, () =>
|
||||
changeSetToSql($changeSetStore?.value, $dbinfo, driver.dialect)
|
||||
);
|
||||
|
||||
const deleteCascades = getDeleteCascades($changeSetStore?.value, $dbinfo);
|
||||
const sql = scriptToSql(driver, script);
|
||||
const deleteCascadesScripts = _.map(deleteCascades, ({ title, commands }) => ({
|
||||
title,
|
||||
script: scriptToSql(driver, commands),
|
||||
}));
|
||||
// console.log('deleteCascadesScripts', deleteCascadesScripts);
|
||||
if (getBoolSettingsValue('skipConfirm.tableDataSave', false) && !deleteCascadesScripts?.length) {
|
||||
handleConfirmSql(sql);
|
||||
} else {
|
||||
showModal(ConfirmSqlModal, {
|
||||
sql,
|
||||
onConfirm: confirmedSql => handleConfirmSql(confirmedSql),
|
||||
engine: driver.engine,
|
||||
deleteCascadesScripts,
|
||||
skipConfirmSettingKey: deleteCascadesScripts?.length ? null : 'skipConfirm.tableDataSave',
|
||||
if (tablePermissionRole == 'create_update_delete' || tablePermissionRole == 'update_only') {
|
||||
const resp = await apiCall('database-connections/save-table-data', {
|
||||
conid,
|
||||
database,
|
||||
changeSet: $changeSetStore?.value,
|
||||
});
|
||||
const { errorMessage } = resp || {};
|
||||
if (errorMessage) {
|
||||
showModal(ErrorMessageModal, { title: 'Error when saving', message: errorMessage });
|
||||
} else {
|
||||
dispatchChangeSet({ type: 'reset', value: createChangeSet() });
|
||||
cache.update(reloadDataCacheFunc);
|
||||
showSnackbarSuccess('Saved to database');
|
||||
}
|
||||
} else {
|
||||
const script = driver.createSaveChangeSetScript($changeSetStore?.value, $dbinfo, () =>
|
||||
changeSetToSql($changeSetStore?.value, $dbinfo, driver.dialect)
|
||||
);
|
||||
|
||||
const deleteCascades = getDeleteCascades($changeSetStore?.value, $dbinfo);
|
||||
const sql = scriptToSql(driver, script);
|
||||
const deleteCascadesScripts = _.map(deleteCascades, ({ title, commands }) => ({
|
||||
title,
|
||||
script: scriptToSql(driver, commands),
|
||||
}));
|
||||
// console.log('deleteCascadesScripts', deleteCascadesScripts);
|
||||
if (getBoolSettingsValue('skipConfirm.tableDataSave', false) && !deleteCascadesScripts?.length) {
|
||||
handleConfirmSql(sql);
|
||||
} else {
|
||||
showModal(ConfirmSqlModal, {
|
||||
sql,
|
||||
onConfirm: confirmedSql => handleConfirmSql(confirmedSql),
|
||||
engine: driver.engine,
|
||||
deleteCascadesScripts,
|
||||
skipConfirmSettingKey: deleteCascadesScripts?.length ? null : 'skipConfirm.tableDataSave',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,3 +22,8 @@ export function subscribePermissionCompiler() {
|
||||
export function setConfigForPermissions(config) {
|
||||
compiled = compilePermissions(config?.permissions || []);
|
||||
}
|
||||
|
||||
export function isAllowedDatabaseRunScript(databasePermissionRole) {
|
||||
return !databasePermissionRole || databasePermissionRole == 'run_script';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user