= Omit<
inline?: boolean;
children?: string;
portal?: boolean;
+ overflowPadding?: number;
headerItems?: TValue[];
footerItems?: TValue[];
};
@@ -71,6 +72,7 @@ export const Select: ISelectType = observer(function Select({
state,
items,
portal = false,
+ overflowPadding,
loading,
children,
title,
@@ -158,7 +160,9 @@ export const Select: ISelectType = observer(function Select({
return (
{renderIcon(item)}
- {valueSelector(item)}
+
+ {valueSelector(item)}
+
);
}
@@ -189,6 +193,7 @@ export const Select: ISelectType = observer(function Select({
-
-
+
+
{translate('ai_administration_settings')}
-
+
+
);
diff --git a/webapp/packages/plugin-ai-administration/src/AIProfiles/AIProfileForm/Options/AIProfileOptions.tsx b/webapp/packages/plugin-ai-administration/src/AIProfiles/AIProfileForm/Options/AIProfileOptions.tsx
index f33a2a7399..cb5bd10059 100644
--- a/webapp/packages/plugin-ai-administration/src/AIProfiles/AIProfileForm/Options/AIProfileOptions.tsx
+++ b/webapp/packages/plugin-ai-administration/src/AIProfiles/AIProfileForm/Options/AIProfileOptions.tsx
@@ -86,26 +86,28 @@ export const AIProfileOptions: TabContainerPanelComponent =
return (
-
-
- {translate('plugin_ai_administration_profile_form_field_name')}
-
-
+
+
+
+ {translate('plugin_ai_administration_profile_form_field_name')}
+
+
+
{!!part.state.engineId && (
-
+
{translate('ai_administration_language_model_settings')}
{hasModels && (
diff --git a/webapp/packages/plugin-ai-chat/src/AIChat/AIChatMessage/AIChatMessageActionsService.ts b/webapp/packages/plugin-ai-chat/src/AIChat/AIChatMessage/AIChatMessageActionsService.ts
index 1dcd5189a9..5cbfa20ccc 100644
--- a/webapp/packages/plugin-ai-chat/src/AIChat/AIChatMessage/AIChatMessageActionsService.ts
+++ b/webapp/packages/plugin-ai-chat/src/AIChat/AIChatMessage/AIChatMessageActionsService.ts
@@ -16,9 +16,9 @@ import {
ESqlDataSourceFeatures,
LocalStorageSqlDataSource,
SqlDataSourceService,
+ SqlEditorPermissionService,
SqlEditorService,
type ILocalStorageSqlDataSourceState,
- SqlEditorSettingsService,
type ISqlEditorTabState,
} from '@cloudbeaver/plugin-sql-editor';
import { NotificationService } from '@cloudbeaver/core-events';
@@ -41,12 +41,9 @@ const AI_CHAT_ID_METADATA_KEY = 'aiChatId';
SqlDataSourceService,
LocalizationService,
CommonDialogService,
- SqlEditorSettingsService,
+ SqlEditorPermissionService,
])
export class AIChatMessageActionsService {
- get isAllowed(): boolean {
- return this.sqlEditorSettingsService.scriptExecutionEnabled;
- }
get isDisabled(): boolean {
return !this.aiChatContextService.currentContext;
}
@@ -61,13 +58,21 @@ export class AIChatMessageActionsService {
private readonly sqlDataSourceService: SqlDataSourceService,
private readonly localizationService: LocalizationService,
private readonly commonDialogService: CommonDialogService,
- private readonly sqlEditorSettingsService: SqlEditorSettingsService,
+ private readonly sqlEditorPermissionService: SqlEditorPermissionService,
) {
makeObservable(this, {
isDisabled: computed,
});
}
+ isAllowed(connectionKey: IConnectionInfoParams | null): boolean {
+ if (!connectionKey) {
+ return false;
+ }
+
+ return this.sqlEditorPermissionService.isScriptExecutionEnabled(connectionKey);
+ }
+
async executeQuery(conversationId: string | undefined | null, connectionKey: IConnectionInfoParams | null, query: string): Promise {
const currentTab = this.navigationTabsService.currentTab;
diff --git a/webapp/packages/plugin-ai-chat/src/AIChat/AIChatMessage/CodeFormatter.tsx b/webapp/packages/plugin-ai-chat/src/AIChat/AIChatMessage/CodeFormatter.tsx
index 433325a875..46fc209fa1 100644
--- a/webapp/packages/plugin-ai-chat/src/AIChat/AIChatMessage/CodeFormatter.tsx
+++ b/webapp/packages/plugin-ai-chat/src/AIChat/AIChatMessage/CodeFormatter.tsx
@@ -62,7 +62,7 @@ export const CodeFormatter = observer(function CodeFormatter({ code, conv
(function MarkdownFormatter({ co
,
h1: ({ node, ...props }) => ,
h2: ({ node, ...props }) => ,
h3: ({ node, ...props }) => ,
diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/Info/UserFormInfo.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/Info/UserFormInfo.tsx
index e1e2a1ef8f..2f4d248f12 100644
--- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/Info/UserFormInfo.tsx
+++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/Info/UserFormInfo.tsx
@@ -37,9 +37,9 @@ export const UserFormInfo: TabContainerPanelComponent = observer(
const tabState = useTabState();
const userFormInfoPartService = useService(UserFormInfoPartService);
const administrationUsersManagementService = useService(AdministrationUsersManagementService);
- const userId = tabState.state.userId ?? formState.state.userId;
+ const userId = formState.state.userId;
const user = useResource(UserFormInfo, UsersResource, userId, {
- active: formState.mode === 'edit',
+ active: !!userId,
});
useAutoLoad(UserFormInfo, [tabState, ...administrationUsersManagementService.loaders], tab.selected);
diff --git a/webapp/packages/plugin-data-import/src/DataImportBootstrap.ts b/webapp/packages/plugin-data-import/src/DataImportBootstrap.ts
index 44e40e8e93..862e8b443e 100644
--- a/webapp/packages/plugin-data-import/src/DataImportBootstrap.ts
+++ b/webapp/packages/plugin-data-import/src/DataImportBootstrap.ts
@@ -5,10 +5,10 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
-import { createConnectionParam } from '@cloudbeaver/core-connections';
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { CommonDialogService, DialogueStateResult } from '@cloudbeaver/core-dialogs';
import { ACTION_IMPORT, ActionService, menuExtractItems, MenuService } from '@cloudbeaver/core-view';
+import { ConnectionInfoResource, createConnectionParam, EConnectionFeature } from '@cloudbeaver/core-connections';
import {
DATA_CONTEXT_DV_DDM,
DATA_CONTEXT_DV_DDM_RESULT_INDEX,
@@ -17,18 +17,21 @@ import {
DatabaseDataFeature,
DataViewerPresentationType,
isResultSetDataModel,
+ ResultSetDataSource,
+ type IDatabaseDataModel,
} from '@cloudbeaver/plugin-data-viewer';
import { DataImportDialogLazy } from './DataImportDialog/DataImportDialogLazy.js';
import { DataImportService } from './DataImportService.js';
-@injectable(() => [MenuService, ActionService, CommonDialogService, DataImportService])
+@injectable(() => [MenuService, ActionService, CommonDialogService, DataImportService, ConnectionInfoResource])
export class DataImportBootstrap extends Bootstrap {
constructor(
private readonly menuService: MenuService,
private readonly actionService: ActionService,
private readonly commonDialogService: CommonDialogService,
private readonly dataImportService: DataImportService,
+ private readonly connectionInfoResource: ConnectionInfoResource,
) {
super();
}
@@ -101,9 +104,21 @@ export class DataImportBootstrap extends Bootstrap {
menus: [DATA_VIEWER_DATA_MODEL_ACTIONS_MENU],
contexts: [DATA_CONTEXT_DV_DDM, DATA_CONTEXT_DV_DDM_RESULT_INDEX],
isApplicable: context => {
- const model = context.get(DATA_CONTEXT_DV_DDM)!;
+ const model = context.get(DATA_CONTEXT_DV_DDM)! as unknown as IDatabaseDataModel;
const presentation = context.get(DATA_CONTEXT_DV_PRESENTATION);
const resultIndex = context.get(DATA_CONTEXT_DV_DDM_RESULT_INDEX)!;
+
+ const executionContext = model.source.executionContext?.context;
+
+ if (executionContext) {
+ const connectionKey = createConnectionParam(executionContext.projectId, executionContext.connectionId);
+ const connection = this.connectionInfoResource.get(connectionKey);
+
+ if (connection?.features.includes(EConnectionFeature.restrictDataImport)) {
+ return false;
+ }
+ }
+
const allowedFeatures = [DatabaseDataFeature.DataEditor];
return (
diff --git a/webapp/packages/plugin-data-viewer-references/src/DataViewerReferencesPresentation.tsx b/webapp/packages/plugin-data-viewer-references/src/DataViewerReferencesPresentation.tsx
index 0ac7b34393..0cecba6bee 100644
--- a/webapp/packages/plugin-data-viewer-references/src/DataViewerReferencesPresentation.tsx
+++ b/webapp/packages/plugin-data-viewer-references/src/DataViewerReferencesPresentation.tsx
@@ -93,6 +93,7 @@ export const DataViewerReferencesPresentation: DataPresentationComponent = obser
keySelector={association => association.id}
titleSelector={getValueSelector}
valueSelector={getValueSelector}
+ overflowPadding={0}
iconSelector={association => (
{
const node = context.get(DATA_CONTEXT_NAV_NODE)!;
+ const connectionKey = context.get(DATA_CONTEXT_CONNECTION)!;
+
+ const connection = this.connectionInfoResource.get(connectionKey);
+
+ if (connection?.features.includes(EConnectionFeature.restrictMetadataEdit)) {
+ return false;
+ }
if (NodeManagerUtils.isDatabaseObject(node.uri) || isConnectionFolder(node)) {
if (action === ACTION_RENAME) {
diff --git a/webapp/packages/plugin-sql-editor-execution-plan/src/SqlExecutionPlanMenuBootstrap.ts b/webapp/packages/plugin-sql-editor-execution-plan/src/SqlExecutionPlanMenuBootstrap.ts
index 68b3e4c6ca..6e03f0f531 100644
--- a/webapp/packages/plugin-sql-editor-execution-plan/src/SqlExecutionPlanMenuBootstrap.ts
+++ b/webapp/packages/plugin-sql-editor-execution-plan/src/SqlExecutionPlanMenuBootstrap.ts
@@ -54,7 +54,7 @@ export class SqlExecutionPlanMenuBootstrap extends Bootstrap {
isActionApplicable: contexts => {
const data = contexts.get(DATA_CONTEXT_SQL_EDITOR_DATA);
- if (!data || !data.isExecutionAllowed) {
+ if (!data || !data.isExecutionAllowed()) {
return false;
}
@@ -86,7 +86,7 @@ export class SqlExecutionPlanMenuBootstrap extends Bootstrap {
const isQuery = data.model.dataSource?.hasFeature(ESqlDataSourceFeatures.query);
- if (!isQuery || !data.isExecutionAllowed || !data.dialect?.supportsExplainExecutionPlan) {
+ if (!isQuery || !data.isExecutionAllowed() || !data.dialect?.supportsExplainExecutionPlan) {
return;
}
diff --git a/webapp/packages/plugin-sql-editor/src/MenuBootstrap.ts b/webapp/packages/plugin-sql-editor/src/MenuBootstrap.ts
index d83db74f68..b3c324682b 100644
--- a/webapp/packages/plugin-sql-editor/src/MenuBootstrap.ts
+++ b/webapp/packages/plugin-sql-editor/src/MenuBootstrap.ts
@@ -244,7 +244,7 @@ export class MenuBootstrap extends Bootstrap {
return false;
}
- if (!sqlEditorData.isExecutionAllowed && EXECUTIONS_ACTIONS.includes(action)) {
+ if (!sqlEditorData.isExecutionAllowed() && EXECUTIONS_ACTIONS.includes(action)) {
return false;
}
@@ -314,7 +314,7 @@ export class MenuBootstrap extends Bootstrap {
contexts: [DATA_CONTEXT_SQL_EDITOR_DATA],
isBindingApplicable: (contexts, action) => {
const sqlEditorData = contexts.get(DATA_CONTEXT_SQL_EDITOR_DATA);
- return action === ACTION_SQL_EDITOR_EXECUTE_SCRIPT && sqlEditorData?.isExecutionAllowed === true;
+ return action === ACTION_SQL_EDITOR_EXECUTE_SCRIPT && sqlEditorData?.isExecutionAllowed() === true;
},
handler: this.sqlEditorActionHandler.bind(this),
});
diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/ISQLEditorData.ts b/webapp/packages/plugin-sql-editor/src/SqlEditor/ISQLEditorData.ts
index 122752e91d..1dbbc2b751 100644
--- a/webapp/packages/plugin-sql-editor/src/SqlEditor/ISQLEditorData.ts
+++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/ISQLEditorData.ts
@@ -30,13 +30,13 @@ export interface ISQLEditorData {
readonly isIncomingChanges: boolean;
readonly value: string;
readonly incomingValue?: string;
- readonly isExecutionAllowed: boolean;
readonly onExecute: ISyncExecutor;
readonly onSegmentExecute: ISyncExecutor;
readonly onFormat: ISyncExecutor<[ISQLScriptSegment, string]>;
/** displays if last getHintProposals call ended with limit */
readonly hintsLimitIsMet: boolean;
+ isExecutionAllowed(): boolean;
updateParserScriptsDebounced(): Promise;
setScript(query: string, source?: string, cursor?: ISqlEditorCursor): void;
setCursor(begin: number, end?: number): void;
diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts b/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts
index 6ca55db3df..43dfc50a8d 100644
--- a/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts
+++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts
@@ -33,6 +33,7 @@ import { SqlResultTabsService } from '../SqlResultTabs/SqlResultTabsService.js';
import type { ISQLEditorData } from './ISQLEditorData.js';
import { SqlEditorSettingsService } from '../SqlEditorSettingsService.js';
import { SqlEditorModelService } from '../SqlEditorModel/SqlEditorModelService.js';
+import { SqlEditorPermissionService } from '../SqlEditorPermissionService.js';
interface ISQLEditorDataPrivate extends ISQLEditorData {
readonly sqlDialectInfoService: SqlDialectInfoService;
@@ -44,6 +45,7 @@ interface ISQLEditorDataPrivate extends ISQLEditorData {
readonly commonDialogService: CommonDialogService;
readonly sqlResultTabsService: SqlResultTabsService;
readonly connectionInfoResource: ConnectionInfoResource;
+ readonly sqlEditorPermissionService: SqlEditorPermissionService;
readonly getLastAutocomplete: LastPromiseGetter;
readonly parseScript: LastPromiseGetter;
@@ -68,6 +70,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
const sqlEditorSettingsService = useService(SqlEditorSettingsService);
const sqlEditorModelService = useService(SqlEditorModelService);
const connectionInfoResource = useService(ConnectionInfoResource);
+ const sqlEditorPermissionService = useService(SqlEditorPermissionService);
const model = sqlEditorModelService.getOrCreate(state);
@@ -119,10 +122,6 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
return this.model.dataSource?.incomingScript;
},
- get isExecutionAllowed(): boolean {
- return !!this.model.dataSource?.hasFeature(ESqlDataSourceFeatures.executable) && this.sqlEditorSettingsService.scriptExecutionEnabled;
- },
-
onExecute: new SyncExecutor(),
onSegmentExecute: new SyncExecutor(),
@@ -131,6 +130,19 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
reactionDisposer: null,
hintsLimitIsMet: false,
+ isExecutionAllowed(): boolean {
+ const executionContext = this.model.dataSource?.executionContext;
+
+ if (!executionContext) {
+ return false;
+ }
+
+ const key = createConnectionParam(executionContext.projectId, executionContext.connectionId);
+ return (
+ !!this.model.dataSource?.hasFeature(ESqlDataSourceFeatures.executable) && this.sqlEditorPermissionService.isScriptExecutionEnabled(key)
+ );
+ },
+
setCursor(begin: number, end = begin): void {
this.model.dataSource?.setCursor(begin, end);
},
@@ -194,7 +206,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
async executeQuery(inNewTab = false): Promise {
const isQuery = this.model.dataSource?.hasFeature(ESqlDataSourceFeatures.query);
- if (!isQuery || !this.isExecutionAllowed) {
+ if (!isQuery || !this.isExecutionAllowed()) {
return;
}
@@ -211,7 +223,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
},
async executeScript(): Promise {
- if (!this.isExecutionAllowed || this.isDisabled || this.isScriptEmpty) {
+ if (!this.isExecutionAllowed() || this.isDisabled || this.isScriptEmpty) {
return;
}
@@ -358,6 +370,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
commonDialogService,
sqlEditorSettingsService,
connectionInfoResource,
+ sqlEditorPermissionService,
},
);
diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditorPermissionService.ts b/webapp/packages/plugin-sql-editor/src/SqlEditorPermissionService.ts
new file mode 100644
index 0000000000..5a01b90955
--- /dev/null
+++ b/webapp/packages/plugin-sql-editor/src/SqlEditorPermissionService.ts
@@ -0,0 +1,30 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2026 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+import { EConnectionFeature, ConnectionInfoResource, type IConnectionInfoParams } from '@cloudbeaver/core-connections';
+import { injectable } from '@cloudbeaver/core-di';
+
+import { SqlEditorSettingsService } from './SqlEditorSettingsService.js';
+
+@injectable(() => [ConnectionInfoResource, SqlEditorSettingsService])
+export class SqlEditorPermissionService {
+ constructor(
+ private readonly connectionInfoResource: ConnectionInfoResource,
+ private readonly sqlEditorSettingsService: SqlEditorSettingsService,
+ ) {}
+
+ isScriptExecutionEnabled(connectionKey: IConnectionInfoParams): boolean {
+ const connection = this.connectionInfoResource.get(connectionKey);
+
+ if (!connection) {
+ return false;
+ }
+
+ return !connection.features.includes(EConnectionFeature.restrictScriptExecute) && this.sqlEditorSettingsService.scriptExecutionEnabled;
+ }
+}
diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/OutputLogs/OutputMenuBootstrap.ts b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/OutputLogs/OutputMenuBootstrap.ts
index cc6c499106..5a9d9e1893 100644
--- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/OutputLogs/OutputMenuBootstrap.ts
+++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/OutputLogs/OutputMenuBootstrap.ts
@@ -127,7 +127,7 @@ export class OutputMenuBootstrap extends Bootstrap {
const sqlDataSource = this.sqlDataSourceService.get(state.editorId);
const isQuery = sqlDataSource?.hasFeature(ESqlDataSourceFeatures.query);
- const isExecutable = sqlEditorData?.isExecutionAllowed;
+ const isExecutable = sqlEditorData?.isExecutionAllowed();
return !!isQuery && !!isExecutable;
},
diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/SqlQueryService.ts b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/SqlQueryService.ts
index b341167dff..9045abb914 100644
--- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/SqlQueryService.ts
+++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/SqlQueryService.ts
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
- * Copyright (C) 2020-2025 DBeaver Corp and others
+ * Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -27,9 +27,9 @@ import type { IResultGroup, ISqlEditorTabState } from '../ISqlEditorTabState.js'
import { QueryDataSource } from '../QueryDataSource.js';
import { SqlDataSourceService } from '../SqlDataSource/SqlDataSourceService.js';
import { SqlQueryResultService } from './SqlQueryResultService.js';
-import { SqlEditorSettingsService } from '../SqlEditorSettingsService.js';
import { Executor, type IExecutor } from '@cloudbeaver/core-executor';
import { CommonDialogService } from '@cloudbeaver/core-dialogs';
+import { SqlEditorPermissionService } from '../SqlEditorPermissionService.js';
interface IQueryExecutionOptions {
onQueryExecutionStart?: (query: string, index: number) => void;
@@ -57,9 +57,9 @@ export interface IQueryExecutionStatistics {
DataViewerService,
SqlDataSourceService,
DataViewerSettingsService,
- SqlEditorSettingsService,
CommonDialogService,
AsyncTaskInfoEventHandler,
+ SqlEditorPermissionService,
])
export class SqlQueryService {
private readonly statisticsMap: Map;
@@ -78,9 +78,9 @@ export class SqlQueryService {
private readonly dataViewerService: DataViewerService,
private readonly sqlDataSourceService: SqlDataSourceService,
private readonly dataViewerSettingsService: DataViewerSettingsService,
- private readonly sqlEditorSettingsService: SqlEditorSettingsService,
private readonly commonDialogService: CommonDialogService,
private readonly asyncTaskInfoEventHandler: AsyncTaskInfoEventHandler,
+ private readonly sqlEditorPermissionService: SqlEditorPermissionService,
) {
this.statisticsMap = new Map();
this.onQueryExecution = new Executor();
@@ -143,10 +143,6 @@ export class SqlQueryService {
try {
const dataSource = this.sqlDataSourceService.get(editorState.editorId);
- if (!this.sqlEditorSettingsService.scriptExecutionEnabled) {
- throw new Error('Script execution is not allowed');
- }
-
const contextInfo = dataSource?.executionContext;
const executionContext = contextInfo && this.connectionExecutionContextService.get(contextInfo.id);
@@ -155,10 +151,14 @@ export class SqlQueryService {
return;
}
- let model: IDatabaseDataModel;
-
const connectionKey = createConnectionParam(contextInfo.projectId, contextInfo.connectionId);
+ if (!this.sqlEditorPermissionService.isScriptExecutionEnabled(connectionKey)) {
+ throw new Error('Script execution is not allowed');
+ }
+
+ let model: IDatabaseDataModel;
+
const connectionInfo = await this.connectionInfoResource.load(connectionKey);
tabGroup = this.sqlQueryResultService.getSelectedGroup(editorState);
@@ -224,10 +224,6 @@ export class SqlQueryService {
try {
const dataSource = this.sqlDataSourceService.get(editorState.editorId);
- if (!this.sqlEditorSettingsService.scriptExecutionEnabled) {
- throw new Error('Script execution is not allowed');
- }
-
const contextInfo = dataSource?.executionContext;
const executionContext = contextInfo && this.connectionExecutionContextService.get(contextInfo.id);
@@ -236,9 +232,14 @@ export class SqlQueryService {
return;
}
- const groupNameOrder = this.sqlQueryResultService.getGroupNameOrder(editorState);
const connectionKey = createConnectionParam(contextInfo.projectId, contextInfo.connectionId);
+ if (!this.sqlEditorPermissionService.isScriptExecutionEnabled(connectionKey)) {
+ throw new Error('Script execution is not allowed');
+ }
+
+ const groupNameOrder = this.sqlQueryResultService.getGroupNameOrder(editorState);
+
const connectionInfo = await this.connectionInfoResource.load(connectionKey);
const statisticsTab = this.sqlQueryResultService.createStatisticsTab(editorState);
diff --git a/webapp/packages/plugin-sql-editor/src/index.ts b/webapp/packages/plugin-sql-editor/src/index.ts
index 29927e0a7e..f822a97839 100644
--- a/webapp/packages/plugin-sql-editor/src/index.ts
+++ b/webapp/packages/plugin-sql-editor/src/index.ts
@@ -53,3 +53,4 @@ export * from './SqlEditorSettingsService.js';
export * from './SqlEditorView.js';
export * from './SqlResultTabs/SqlQueryService.js';
export * from './downloadSql.js';
+export * from './SqlEditorPermissionService.js';
diff --git a/webapp/packages/plugin-sql-editor/src/module.ts b/webapp/packages/plugin-sql-editor/src/module.ts
index d8ab3301e5..2269065c0b 100644
--- a/webapp/packages/plugin-sql-editor/src/module.ts
+++ b/webapp/packages/plugin-sql-editor/src/module.ts
@@ -26,6 +26,7 @@ import { MenuBootstrap } from './MenuBootstrap.js';
import { LocaleService } from './LocaleService.js';
import { SqlEditorModel } from './SqlEditorModel/SqlEditorModel.js';
import { SqlEditorModelService } from './SqlEditorModel/SqlEditorModelService.js';
+import { SqlEditorPermissionService } from './SqlEditorPermissionService.js';
export default ModuleRegistry.add({
name: '@cloudbeaver/plugin-sql-editor',
@@ -56,6 +57,7 @@ export default ModuleRegistry.add({
.addSingleton(SqlDataSourceService)
.addSingleton(LocalStorageSqlDataSourceBootstrap)
.addSingleton(SqlEditorModelService)
- .addSingleton(MenuBootstrap);
+ .addSingleton(MenuBootstrap)
+ .addSingleton(SqlEditorPermissionService);
},
});