From e135ac680cf4946d0240608ff235bc604f7e9e86 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Wed, 15 Apr 2026 19:15:52 +0200 Subject: [PATCH 1/8] dbeaver/pro#8234 only admin can overwrite existing udbt handler --- .../bundles/io.cloudbeaver.model/plugin.xml | 2 + ...eaver.rmDataSourceConfigUpdateHandler.exsd | 11 ++++ .../rm/local/BaseLocalResourceController.java | 51 +++++++++------ .../rm/local/LocalResourceController.java | 30 +++++++++ .../RMDataSourceConfigUpdateHandler.java | 32 ++++++++++ ...taSourceConfigUpdateHandlerDescriptor.java | 37 +++++++++++ ...ataSourceConfigUpdateHandlersRegistry.java | 62 +++++++++++++++++++ 7 files changed, 207 insertions(+), 18 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd create mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java create mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java create mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java diff --git a/server/bundles/io.cloudbeaver.model/plugin.xml b/server/bundles/io.cloudbeaver.model/plugin.xml index de60a463385..01d919d5489 100644 --- a/server/bundles/io.cloudbeaver.model/plugin.xml +++ b/server/bundles/io.cloudbeaver.model/plugin.xml @@ -10,6 +10,8 @@ + diff --git a/server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd b/server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd new file mode 100644 index 00000000000..cfa43cbee72 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd @@ -0,0 +1,11 @@ + + + + + + + + RM data source config update handler + + + diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java index 3ec793dceba..c1b720568cb 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java @@ -116,22 +116,7 @@ public String getProjectsDataSources(@NotNull String projectId, @Nullable String return doFileReadOperation( projectId, projectMetadata.getMetadataFolder(false), - () -> { - DBPDataSourceRegistry registry = projectMetadata.getDataSourceRegistry(); - registry.refreshConfig(); - registry.checkForErrors(); - DataSourceConfigurationManagerBuffer buffer = new DataSourceConfigurationManagerBuffer(); - Predicate filter = null; - if (!ArrayUtils.isEmpty(dataSourceIds)) { - filter = ds -> ArrayUtils.contains(dataSourceIds, ds.getId()); - } - ((DataSourcePersistentRegistry) registry).saveConfigurationToManager(new VoidProgressMonitor(), - buffer, - filter); - registry.checkForErrors(); - - return new String(buffer.getData(), StandardCharsets.UTF_8); - } + () -> serializeProjectDataSourcesConfiguration(projectMetadata, dataSourceIds) ); } @@ -160,12 +145,13 @@ protected DataSourceParseResults updateProjectDataSourcesConfig( @Nullable List dataSourceIds ) throws DBException { try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("updateProjectDataSources"))) { - DBPProject project = getWebProject(projectId, false); + RMLocalProject project = getWebProject(projectId, false); + String processedConfiguration = preprocessDataSourceConfigurationUpdate(project, projectId, configuration, dataSourceIds); return doFileWriteOperation( projectId, project.getMetadataFolder(false), () -> { DBPDataSourceRegistry registry = project.getDataSourceRegistry(); - DBPDataSourceConfigurationStorage storage = new DataSourceMemoryStorage(configuration.getBytes( + DBPDataSourceConfigurationStorage storage = new DataSourceMemoryStorage(processedConfiguration.getBytes( StandardCharsets.UTF_8)); DataSourceConfigurationManager manager = new DataSourceConfigurationManagerBuffer(); final DataSourceParseResults parseResults = ((DataSourcePersistentRegistry) registry).loadDataSources( @@ -185,6 +171,35 @@ protected DataSourceParseResults updateProjectDataSourcesConfig( } } + @NotNull + protected String preprocessDataSourceConfigurationUpdate( + @NotNull RMLocalProject project, + @NotNull String projectId, + @NotNull String configuration, + @Nullable List dataSourceIds + ) throws DBException { + return configuration; + } + + @NotNull + protected String serializeProjectDataSourcesConfiguration( + @NotNull DBPProject project, + @Nullable String[] dataSourceIds + ) throws DBException { + DBPDataSourceRegistry registry = project.getDataSourceRegistry(); + registry.refreshConfig(); + registry.checkForErrors(); + DataSourceConfigurationManagerBuffer buffer = new DataSourceConfigurationManagerBuffer(); + Predicate filter = null; + if (!ArrayUtils.isEmpty(dataSourceIds)) { + filter = ds -> ArrayUtils.contains(dataSourceIds, ds.getId()); + } + ((DataSourcePersistentRegistry) registry).saveConfigurationToManager(new VoidProgressMonitor(), buffer, filter); + registry.checkForErrors(); + + return new String(buffer.getData(), StandardCharsets.UTF_8); + } + @Override public void deleteProjectDataSources( @NotNull String projectId, diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java index 5a0033c300d..345ed6a5f1d 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java @@ -82,6 +82,7 @@ public class LocalResourceController extends BaseLocalResourceController { private final String globalProjectName; private Supplier smControllerSupplier; protected final List fileHandlers; + protected final List dataSourceConfigUpdateHandlers; private final Map projectRegistries = new LinkedHashMap<>(); private final ProjectsMetadataInfo sharedProjectsMetadataInfo; @@ -104,6 +105,7 @@ public LocalResourceController( this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName(); this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers(); + this.dataSourceConfigUpdateHandlers = RMDataSourceConfigUpdateHandlersRegistry.getInstance().getHandlers(); this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath, lockController); } @@ -411,6 +413,34 @@ public boolean updateProjectDataSources( return parseResults != null; } + @Override + @NotNull + protected String preprocessDataSourceConfigurationUpdate( + @NotNull RMLocalProject project, + @NotNull String projectId, + @NotNull String configuration, + @Nullable List dataSourceIds + ) throws DBException { + if (dataSourceConfigUpdateHandlers.isEmpty()) { + return configuration; + } + + Set projectPermissions = getProjectPermissions(projectId, project.getProjectType()); + String currentConfiguration = serializeProjectDataSourcesConfiguration( + project, + dataSourceIds == null ? null : dataSourceIds.toArray(String[]::new) + ); + String processedConfiguration = configuration; + for (RMDataSourceConfigUpdateHandler handler : dataSourceConfigUpdateHandlers) { + processedConfiguration = handler.processDataSourceConfigurationUpdate( + currentConfiguration, + processedConfiguration, + projectPermissions + ); + } + return processedConfiguration; + } + @Override public void deleteProjectDataSources(@NotNull String projectId, @NotNull String[] dataSourceIds) throws DBException { super.deleteProjectDataSources(projectId, dataSourceIds); diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java new file mode 100644 index 00000000000..03b07d54b25 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java @@ -0,0 +1,32 @@ + /* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver.model.rm.local; + +import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.model.rm.RMProjectPermission; + +import java.util.Set; + +public interface RMDataSourceConfigUpdateHandler { + @NotNull + String processDataSourceConfigurationUpdate( + @NotNull String currentConfiguration, + @NotNull String updatedConfiguration, + @NotNull Set projectPermissions + ) throws DBException; +} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java new file mode 100644 index 00000000000..1da255f1b05 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java @@ -0,0 +1,37 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver.model.rm.local; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.model.impl.AbstractDescriptor; + +public class RMDataSourceConfigUpdateHandlerDescriptor extends AbstractDescriptor { + public static final String EXTENSION_ID = "io.cloudbeaver.rm.datasource.update.handler"; //$NON-NLS-1$ + + private final RMDataSourceConfigUpdateHandler instance; + + public RMDataSourceConfigUpdateHandlerDescriptor(IConfigurationElement config) throws DBException { + super(config); + ObjectType implClass = new ObjectType(config.getAttribute("class")); + this.instance = implClass.createInstance(RMDataSourceConfigUpdateHandler.class); + } + + public RMDataSourceConfigUpdateHandler getInstance() { + return instance; + } +} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java new file mode 100644 index 00000000000..c662a2cdc93 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java @@ -0,0 +1,62 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver.model.rm.local; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.Log; + +import java.util.ArrayList; +import java.util.List; + +public class RMDataSourceConfigUpdateHandlersRegistry { + private static final Log log = Log.getLog(RMDataSourceConfigUpdateHandlersRegistry.class); + + private static final String TAG_HANDLER = "rmDataSourceConfigUpdateHandler"; //$NON-NLS-1$ + + private static RMDataSourceConfigUpdateHandlersRegistry instance; + + private final List handlers = new ArrayList<>(); + + public static synchronized RMDataSourceConfigUpdateHandlersRegistry getInstance() { + if (instance == null) { + instance = new RMDataSourceConfigUpdateHandlersRegistry(); + instance.loadExtensions(Platform.getExtensionRegistry()); + } + return instance; + } + + private synchronized void loadExtensions(IExtensionRegistry registry) { + IConfigurationElement[] extConfigs = registry.getConfigurationElementsFor(RMDataSourceConfigUpdateHandlerDescriptor.EXTENSION_ID); + for (IConfigurationElement ext : extConfigs) { + if (!TAG_HANDLER.equals(ext.getName())) { + continue; + } + try { + handlers.add(new RMDataSourceConfigUpdateHandlerDescriptor(ext).getInstance()); + } catch (DBException e) { + log.error("Error loading data source config update handler", e); + } + } + } + + public List getHandlers() { + return handlers; + } +} From bebc4f173f97ec8d30d2578a218485e57d831ec5 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Wed, 15 Apr 2026 19:15:52 +0200 Subject: [PATCH 2/8] dbeaver/pro#8234 only admin can overwrite existing udbt handler --- .../bundles/io.cloudbeaver.model/plugin.xml | 2 + ...eaver.rmDataSourceConfigUpdateHandler.exsd | 11 ++++ .../rm/local/BaseLocalResourceController.java | 51 +++++++++------ .../rm/local/LocalResourceController.java | 30 +++++++++ .../RMDataSourceConfigUpdateHandler.java | 32 ++++++++++ ...taSourceConfigUpdateHandlerDescriptor.java | 37 +++++++++++ ...ataSourceConfigUpdateHandlersRegistry.java | 62 +++++++++++++++++++ 7 files changed, 207 insertions(+), 18 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd create mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java create mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java create mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java diff --git a/server/bundles/io.cloudbeaver.model/plugin.xml b/server/bundles/io.cloudbeaver.model/plugin.xml index de60a463385..01d919d5489 100644 --- a/server/bundles/io.cloudbeaver.model/plugin.xml +++ b/server/bundles/io.cloudbeaver.model/plugin.xml @@ -10,6 +10,8 @@ + diff --git a/server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd b/server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd new file mode 100644 index 00000000000..cfa43cbee72 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd @@ -0,0 +1,11 @@ + + + + + + + + RM data source config update handler + + + diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java index 3ec793dceba..c1b720568cb 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java @@ -116,22 +116,7 @@ public String getProjectsDataSources(@NotNull String projectId, @Nullable String return doFileReadOperation( projectId, projectMetadata.getMetadataFolder(false), - () -> { - DBPDataSourceRegistry registry = projectMetadata.getDataSourceRegistry(); - registry.refreshConfig(); - registry.checkForErrors(); - DataSourceConfigurationManagerBuffer buffer = new DataSourceConfigurationManagerBuffer(); - Predicate filter = null; - if (!ArrayUtils.isEmpty(dataSourceIds)) { - filter = ds -> ArrayUtils.contains(dataSourceIds, ds.getId()); - } - ((DataSourcePersistentRegistry) registry).saveConfigurationToManager(new VoidProgressMonitor(), - buffer, - filter); - registry.checkForErrors(); - - return new String(buffer.getData(), StandardCharsets.UTF_8); - } + () -> serializeProjectDataSourcesConfiguration(projectMetadata, dataSourceIds) ); } @@ -160,12 +145,13 @@ protected DataSourceParseResults updateProjectDataSourcesConfig( @Nullable List dataSourceIds ) throws DBException { try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("updateProjectDataSources"))) { - DBPProject project = getWebProject(projectId, false); + RMLocalProject project = getWebProject(projectId, false); + String processedConfiguration = preprocessDataSourceConfigurationUpdate(project, projectId, configuration, dataSourceIds); return doFileWriteOperation( projectId, project.getMetadataFolder(false), () -> { DBPDataSourceRegistry registry = project.getDataSourceRegistry(); - DBPDataSourceConfigurationStorage storage = new DataSourceMemoryStorage(configuration.getBytes( + DBPDataSourceConfigurationStorage storage = new DataSourceMemoryStorage(processedConfiguration.getBytes( StandardCharsets.UTF_8)); DataSourceConfigurationManager manager = new DataSourceConfigurationManagerBuffer(); final DataSourceParseResults parseResults = ((DataSourcePersistentRegistry) registry).loadDataSources( @@ -185,6 +171,35 @@ protected DataSourceParseResults updateProjectDataSourcesConfig( } } + @NotNull + protected String preprocessDataSourceConfigurationUpdate( + @NotNull RMLocalProject project, + @NotNull String projectId, + @NotNull String configuration, + @Nullable List dataSourceIds + ) throws DBException { + return configuration; + } + + @NotNull + protected String serializeProjectDataSourcesConfiguration( + @NotNull DBPProject project, + @Nullable String[] dataSourceIds + ) throws DBException { + DBPDataSourceRegistry registry = project.getDataSourceRegistry(); + registry.refreshConfig(); + registry.checkForErrors(); + DataSourceConfigurationManagerBuffer buffer = new DataSourceConfigurationManagerBuffer(); + Predicate filter = null; + if (!ArrayUtils.isEmpty(dataSourceIds)) { + filter = ds -> ArrayUtils.contains(dataSourceIds, ds.getId()); + } + ((DataSourcePersistentRegistry) registry).saveConfigurationToManager(new VoidProgressMonitor(), buffer, filter); + registry.checkForErrors(); + + return new String(buffer.getData(), StandardCharsets.UTF_8); + } + @Override public void deleteProjectDataSources( @NotNull String projectId, diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java index 5a0033c300d..345ed6a5f1d 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java @@ -82,6 +82,7 @@ public class LocalResourceController extends BaseLocalResourceController { private final String globalProjectName; private Supplier smControllerSupplier; protected final List fileHandlers; + protected final List dataSourceConfigUpdateHandlers; private final Map projectRegistries = new LinkedHashMap<>(); private final ProjectsMetadataInfo sharedProjectsMetadataInfo; @@ -104,6 +105,7 @@ public LocalResourceController( this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName(); this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers(); + this.dataSourceConfigUpdateHandlers = RMDataSourceConfigUpdateHandlersRegistry.getInstance().getHandlers(); this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath, lockController); } @@ -411,6 +413,34 @@ public boolean updateProjectDataSources( return parseResults != null; } + @Override + @NotNull + protected String preprocessDataSourceConfigurationUpdate( + @NotNull RMLocalProject project, + @NotNull String projectId, + @NotNull String configuration, + @Nullable List dataSourceIds + ) throws DBException { + if (dataSourceConfigUpdateHandlers.isEmpty()) { + return configuration; + } + + Set projectPermissions = getProjectPermissions(projectId, project.getProjectType()); + String currentConfiguration = serializeProjectDataSourcesConfiguration( + project, + dataSourceIds == null ? null : dataSourceIds.toArray(String[]::new) + ); + String processedConfiguration = configuration; + for (RMDataSourceConfigUpdateHandler handler : dataSourceConfigUpdateHandlers) { + processedConfiguration = handler.processDataSourceConfigurationUpdate( + currentConfiguration, + processedConfiguration, + projectPermissions + ); + } + return processedConfiguration; + } + @Override public void deleteProjectDataSources(@NotNull String projectId, @NotNull String[] dataSourceIds) throws DBException { super.deleteProjectDataSources(projectId, dataSourceIds); diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java new file mode 100644 index 00000000000..03b07d54b25 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java @@ -0,0 +1,32 @@ + /* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver.model.rm.local; + +import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.model.rm.RMProjectPermission; + +import java.util.Set; + +public interface RMDataSourceConfigUpdateHandler { + @NotNull + String processDataSourceConfigurationUpdate( + @NotNull String currentConfiguration, + @NotNull String updatedConfiguration, + @NotNull Set projectPermissions + ) throws DBException; +} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java new file mode 100644 index 00000000000..1da255f1b05 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java @@ -0,0 +1,37 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver.model.rm.local; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.model.impl.AbstractDescriptor; + +public class RMDataSourceConfigUpdateHandlerDescriptor extends AbstractDescriptor { + public static final String EXTENSION_ID = "io.cloudbeaver.rm.datasource.update.handler"; //$NON-NLS-1$ + + private final RMDataSourceConfigUpdateHandler instance; + + public RMDataSourceConfigUpdateHandlerDescriptor(IConfigurationElement config) throws DBException { + super(config); + ObjectType implClass = new ObjectType(config.getAttribute("class")); + this.instance = implClass.createInstance(RMDataSourceConfigUpdateHandler.class); + } + + public RMDataSourceConfigUpdateHandler getInstance() { + return instance; + } +} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java new file mode 100644 index 00000000000..c662a2cdc93 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java @@ -0,0 +1,62 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver.model.rm.local; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.Log; + +import java.util.ArrayList; +import java.util.List; + +public class RMDataSourceConfigUpdateHandlersRegistry { + private static final Log log = Log.getLog(RMDataSourceConfigUpdateHandlersRegistry.class); + + private static final String TAG_HANDLER = "rmDataSourceConfigUpdateHandler"; //$NON-NLS-1$ + + private static RMDataSourceConfigUpdateHandlersRegistry instance; + + private final List handlers = new ArrayList<>(); + + public static synchronized RMDataSourceConfigUpdateHandlersRegistry getInstance() { + if (instance == null) { + instance = new RMDataSourceConfigUpdateHandlersRegistry(); + instance.loadExtensions(Platform.getExtensionRegistry()); + } + return instance; + } + + private synchronized void loadExtensions(IExtensionRegistry registry) { + IConfigurationElement[] extConfigs = registry.getConfigurationElementsFor(RMDataSourceConfigUpdateHandlerDescriptor.EXTENSION_ID); + for (IConfigurationElement ext : extConfigs) { + if (!TAG_HANDLER.equals(ext.getName())) { + continue; + } + try { + handlers.add(new RMDataSourceConfigUpdateHandlerDescriptor(ext).getInstance()); + } catch (DBException e) { + log.error("Error loading data source config update handler", e); + } + } + } + + public List getHandlers() { + return handlers; + } +} From 3e81e5ad564203de67de848ea6e70f4b60c1640e Mon Sep 17 00:00:00 2001 From: Ruslan Date: Mon, 20 Apr 2026 17:09:45 +0200 Subject: [PATCH 3/8] dbeaver/pro#8234 Refactor data source configuration handling and update permission checks --- .../bundles/io.cloudbeaver.model/plugin.xml | 2 - ...eaver.rmDataSourceConfigUpdateHandler.exsd | 11 -- .../rm/local/BaseLocalResourceController.java | 34 +++-- .../rm/local/LocalResourceController.java | 116 ++++++++++++++---- .../RMDataSourceConfigUpdateHandler.java | 32 ----- ...taSourceConfigUpdateHandlerDescriptor.java | 37 ------ ...ataSourceConfigUpdateHandlersRegistry.java | 62 ---------- 7 files changed, 122 insertions(+), 172 deletions(-) delete mode 100644 server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd delete mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java delete mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java delete mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java diff --git a/server/bundles/io.cloudbeaver.model/plugin.xml b/server/bundles/io.cloudbeaver.model/plugin.xml index 01d919d5489..de60a463385 100644 --- a/server/bundles/io.cloudbeaver.model/plugin.xml +++ b/server/bundles/io.cloudbeaver.model/plugin.xml @@ -10,8 +10,6 @@ - diff --git a/server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd b/server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd deleted file mode 100644 index cfa43cbee72..00000000000 --- a/server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - RM data source config update handler - - - diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java index c1b720568cb..9fe1d41d075 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2025 DBeaver Corp and others + * Copyright (C) 2010-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry; import org.jkiss.dbeaver.model.app.DBPProject; import org.jkiss.dbeaver.model.app.DBPWorkspace; +import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration; import org.jkiss.dbeaver.model.fs.lock.LockManager; import org.jkiss.dbeaver.model.fs.lock.LockOptions; import org.jkiss.dbeaver.model.fs.lock.LockTarget; @@ -45,6 +46,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import java.util.Map; import java.util.function.Predicate; public abstract class BaseLocalResourceController implements RMController { @@ -146,12 +148,13 @@ protected DataSourceParseResults updateProjectDataSourcesConfig( ) throws DBException { try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("updateProjectDataSources"))) { RMLocalProject project = getWebProject(projectId, false); - String processedConfiguration = preprocessDataSourceConfigurationUpdate(project, projectId, configuration, dataSourceIds); + Map storedDataSourceConfigurations = + captureCurrentDataSourceConfigurations(project, dataSourceIds); return doFileWriteOperation( projectId, project.getMetadataFolder(false), () -> { DBPDataSourceRegistry registry = project.getDataSourceRegistry(); - DBPDataSourceConfigurationStorage storage = new DataSourceMemoryStorage(processedConfiguration.getBytes( + DBPDataSourceConfigurationStorage storage = new DataSourceMemoryStorage(configuration.getBytes( StandardCharsets.UTF_8)); DataSourceConfigurationManager manager = new DataSourceConfigurationManagerBuffer(); final DataSourceParseResults parseResults = ((DataSourcePersistentRegistry) registry).loadDataSources( @@ -162,6 +165,17 @@ protected DataSourceParseResults updateProjectDataSourcesConfig( dataSourceIds == null ); registry.checkForErrors(); + try { + processLoadedDataSourceConfigurationUpdate( + project, + projectId, + dataSourceIds, + storedDataSourceConfigurations + ); + } catch (DBException e) { + registry.refreshConfig(); + registry.checkForErrors(); + } log.debug("Save data sources configuration in project '" + projectId + "'"); ((DataSourcePersistentRegistry) registry).saveDataSources(); registry.checkForErrors(); @@ -172,13 +186,19 @@ protected DataSourceParseResults updateProjectDataSourcesConfig( } @NotNull - protected String preprocessDataSourceConfigurationUpdate( + protected Map captureCurrentDataSourceConfigurations( @NotNull RMLocalProject project, - @NotNull String projectId, - @NotNull String configuration, @Nullable List dataSourceIds + ) { + return Map.of(); + } + + protected void processLoadedDataSourceConfigurationUpdate( + @NotNull RMLocalProject project, + @NotNull String projectId, + @Nullable List dataSourceIds, + @NotNull Map storedDataSourceConfigurations ) throws DBException { - return configuration; } @NotNull diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java index 345ed6a5f1d..e581f7a6115 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java @@ -33,12 +33,14 @@ import org.jkiss.dbeaver.model.app.DBPWorkspace; import org.jkiss.dbeaver.model.auth.SMCredentials; import org.jkiss.dbeaver.model.auth.SMCredentialsProvider; +import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration; import org.jkiss.dbeaver.model.fs.lock.LockManager; import org.jkiss.dbeaver.model.fs.lock.LockOptions; import org.jkiss.dbeaver.model.fs.lock.LockTarget; import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl; import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl; import org.jkiss.dbeaver.model.navigator.DBNLocalFolder; +import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration; import org.jkiss.dbeaver.model.rm.*; import org.jkiss.dbeaver.model.security.SMAdminController; import org.jkiss.dbeaver.model.security.SMObjectType; @@ -52,6 +54,7 @@ import org.jkiss.dbeaver.registry.DataSourceParseResults; import org.jkiss.dbeaver.registry.ResourceTypeDescriptor; import org.jkiss.dbeaver.registry.ResourceTypeRegistry; +import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor; import org.jkiss.dbeaver.runtime.DBWorkbench; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.IOUtils; @@ -82,7 +85,6 @@ public class LocalResourceController extends BaseLocalResourceController { private final String globalProjectName; private Supplier smControllerSupplier; protected final List fileHandlers; - protected final List dataSourceConfigUpdateHandlers; private final Map projectRegistries = new LinkedHashMap<>(); private final ProjectsMetadataInfo sharedProjectsMetadataInfo; @@ -105,7 +107,6 @@ public LocalResourceController( this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName(); this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers(); - this.dataSourceConfigUpdateHandlers = RMDataSourceConfigUpdateHandlersRegistry.getInstance().getHandlers(); this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath, lockController); } @@ -415,30 +416,103 @@ public boolean updateProjectDataSources( @Override @NotNull - protected String preprocessDataSourceConfigurationUpdate( + protected Map captureCurrentDataSourceConfigurations( @NotNull RMLocalProject project, - @NotNull String projectId, - @NotNull String configuration, @Nullable List dataSourceIds + ) { + return project.getDataSourceRegistry().getDataSources().stream() + .filter(ds -> dataSourceIds == null || dataSourceIds.contains(ds.getId())) + .collect(Collectors.toMap( + DBPDataSourceContainer::getId, + ds -> new DBPConnectionConfiguration(ds.getConnectionConfiguration()) + )); + } + + @Override + protected void processLoadedDataSourceConfigurationUpdate( + @NotNull RMLocalProject project, + @NotNull String projectId, + @Nullable List dataSourceIds, + @NotNull Map storedDataSourceConfigurations ) throws DBException { - if (dataSourceConfigUpdateHandlers.isEmpty()) { - return configuration; - } - - Set projectPermissions = getProjectPermissions(projectId, project.getProjectType()); - String currentConfiguration = serializeProjectDataSourcesConfiguration( - project, - dataSourceIds == null ? null : dataSourceIds.toArray(String[]::new) - ); - String processedConfiguration = configuration; - for (RMDataSourceConfigUpdateHandler handler : dataSourceConfigUpdateHandlers) { - processedConfiguration = handler.processDataSourceConfigurationUpdate( - currentConfiguration, - processedConfiguration, - projectPermissions + Set userProjectPermissions = getProjectPermissions(projectId, project.getProjectType()); + Set grantedPermissions = userProjectPermissions.stream() + .flatMap(permission -> permission.getAllPermissions().stream()) + .collect(Collectors.toSet()); + + for (DBPDataSourceContainer dataSource : project.getDataSourceRegistry().getDataSources()) { + if (dataSourceIds != null && !dataSourceIds.contains(dataSource.getId())) { + continue; + } + + DBPConnectionConfiguration storedConfiguration = storedDataSourceConfigurations.get(dataSource.getId()); + reconcileNetworkHandlers( + storedConfiguration, + dataSource.getConnectionConfiguration(), + grantedPermissions ); } - return processedConfiguration; + } + + private void reconcileNetworkHandlers( + @Nullable DBPConnectionConfiguration storedConfiguration, + @NotNull DBPConnectionConfiguration updatedConfiguration, + @NotNull Set grantedPermissions + ) throws DBException { + + // Getting all handlers ids from both stored and updated configurations to check permissions for all of them + // and to add missing handlers if user has no permissions to add new ones + Set handlerIds = new LinkedHashSet<>(); + if (storedConfiguration != null) { + storedConfiguration.getHandlers().stream() + .map(DBWHandlerConfiguration::getId) + .forEach(handlerIds::add); + } + updatedConfiguration.getHandlers().stream() + .map(DBWHandlerConfiguration::getId) + .forEach(handlerIds::add); + + for (String handlerId : handlerIds) { + DBWHandlerConfiguration storedHandler = storedConfiguration == null ? null : storedConfiguration.getHandler(handlerId); + DBWHandlerConfiguration updatedHandler = updatedConfiguration.getHandler(handlerId); + DBWHandlerConfiguration descriptorSource = updatedHandler != null ? updatedHandler : storedHandler; + if (descriptorSource == null + || !(descriptorSource.getHandlerDescriptor() instanceof NetworkHandlerDescriptor descriptor)) { + continue; + } + + if (descriptor.getRequiredPermissions().isEmpty() + || grantedPermissions.containsAll(descriptor.getRequiredPermissions())) { + continue; + } + + if (storedHandler == null) { + throw new DBException("No permissions to configure network handler '" + handlerId + "'"); + } + + // if user doesn't have permissions to add new handler, we should keep old one + if (updatedHandler == null) { + updatedConfiguration.updateHandler(new DBWHandlerConfiguration(storedHandler)); + continue; + } + + if (!areSameHandlerConfiguration(storedHandler, updatedHandler)) { + throw new DBException("No permissions to modify network handler '" + handlerId + "'"); + } + } + } + + private boolean areSameHandlerConfiguration( + @NotNull DBWHandlerConfiguration storedHandler, + @NotNull DBWHandlerConfiguration updatedHandler + ) { + DBWHandlerConfiguration storedHandlerCopy = new DBWHandlerConfiguration(storedHandler); + storedHandlerCopy.setDataSource(null); + + DBWHandlerConfiguration updatedHandlerCopy = new DBWHandlerConfiguration(updatedHandler); + updatedHandlerCopy.setDataSource(null); + + return storedHandlerCopy.equals(updatedHandlerCopy); } @Override diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java deleted file mode 100644 index 03b07d54b25..00000000000 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java +++ /dev/null @@ -1,32 +0,0 @@ - /* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2026 DBeaver Corp and others - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.cloudbeaver.model.rm.local; - -import org.jkiss.code.NotNull; -import org.jkiss.dbeaver.DBException; -import org.jkiss.dbeaver.model.rm.RMProjectPermission; - -import java.util.Set; - -public interface RMDataSourceConfigUpdateHandler { - @NotNull - String processDataSourceConfigurationUpdate( - @NotNull String currentConfiguration, - @NotNull String updatedConfiguration, - @NotNull Set projectPermissions - ) throws DBException; -} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java deleted file mode 100644 index 1da255f1b05..00000000000 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2026 DBeaver Corp and others - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.cloudbeaver.model.rm.local; - -import org.eclipse.core.runtime.IConfigurationElement; -import org.jkiss.dbeaver.DBException; -import org.jkiss.dbeaver.model.impl.AbstractDescriptor; - -public class RMDataSourceConfigUpdateHandlerDescriptor extends AbstractDescriptor { - public static final String EXTENSION_ID = "io.cloudbeaver.rm.datasource.update.handler"; //$NON-NLS-1$ - - private final RMDataSourceConfigUpdateHandler instance; - - public RMDataSourceConfigUpdateHandlerDescriptor(IConfigurationElement config) throws DBException { - super(config); - ObjectType implClass = new ObjectType(config.getAttribute("class")); - this.instance = implClass.createInstance(RMDataSourceConfigUpdateHandler.class); - } - - public RMDataSourceConfigUpdateHandler getInstance() { - return instance; - } -} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java deleted file mode 100644 index c662a2cdc93..00000000000 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2026 DBeaver Corp and others - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.cloudbeaver.model.rm.local; - -import org.eclipse.core.runtime.IConfigurationElement; -import org.eclipse.core.runtime.IExtensionRegistry; -import org.eclipse.core.runtime.Platform; -import org.jkiss.dbeaver.DBException; -import org.jkiss.dbeaver.Log; - -import java.util.ArrayList; -import java.util.List; - -public class RMDataSourceConfigUpdateHandlersRegistry { - private static final Log log = Log.getLog(RMDataSourceConfigUpdateHandlersRegistry.class); - - private static final String TAG_HANDLER = "rmDataSourceConfigUpdateHandler"; //$NON-NLS-1$ - - private static RMDataSourceConfigUpdateHandlersRegistry instance; - - private final List handlers = new ArrayList<>(); - - public static synchronized RMDataSourceConfigUpdateHandlersRegistry getInstance() { - if (instance == null) { - instance = new RMDataSourceConfigUpdateHandlersRegistry(); - instance.loadExtensions(Platform.getExtensionRegistry()); - } - return instance; - } - - private synchronized void loadExtensions(IExtensionRegistry registry) { - IConfigurationElement[] extConfigs = registry.getConfigurationElementsFor(RMDataSourceConfigUpdateHandlerDescriptor.EXTENSION_ID); - for (IConfigurationElement ext : extConfigs) { - if (!TAG_HANDLER.equals(ext.getName())) { - continue; - } - try { - handlers.add(new RMDataSourceConfigUpdateHandlerDescriptor(ext).getInstance()); - } catch (DBException e) { - log.error("Error loading data source config update handler", e); - } - } - } - - public List getHandlers() { - return handlers; - } -} From d1500b1dd6273a3e28d9dbaed00e436ea397dee6 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Mon, 20 Apr 2026 17:33:14 +0200 Subject: [PATCH 4/8] dbeaver/pro#8234 fixes --- .../bundles/io.cloudbeaver.model/plugin.xml | 2 - ...eaver.rmDataSourceConfigUpdateHandler.exsd | 11 ---- .../rm/local/BaseLocalResourceController.java | 37 +++++------ .../RMDataSourceConfigUpdateHandler.java | 32 ---------- ...taSourceConfigUpdateHandlerDescriptor.java | 37 ----------- ...ataSourceConfigUpdateHandlersRegistry.java | 62 ------------------- 6 files changed, 16 insertions(+), 165 deletions(-) delete mode 100644 server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd delete mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java delete mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java delete mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java diff --git a/server/bundles/io.cloudbeaver.model/plugin.xml b/server/bundles/io.cloudbeaver.model/plugin.xml index 01d919d5489..de60a463385 100644 --- a/server/bundles/io.cloudbeaver.model/plugin.xml +++ b/server/bundles/io.cloudbeaver.model/plugin.xml @@ -10,8 +10,6 @@ - diff --git a/server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd b/server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd deleted file mode 100644 index cfa43cbee72..00000000000 --- a/server/bundles/io.cloudbeaver.model/schema/io.cloudbeaver.rmDataSourceConfigUpdateHandler.exsd +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - RM data source config update handler - - - diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java index 9fe1d41d075..3a02af3beec 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java @@ -118,7 +118,22 @@ public String getProjectsDataSources(@NotNull String projectId, @Nullable String return doFileReadOperation( projectId, projectMetadata.getMetadataFolder(false), - () -> serializeProjectDataSourcesConfiguration(projectMetadata, dataSourceIds) + () -> { + DBPDataSourceRegistry registry = projectMetadata.getDataSourceRegistry(); + registry.refreshConfig(); + registry.checkForErrors(); + DataSourceConfigurationManagerBuffer buffer = new DataSourceConfigurationManagerBuffer(); + Predicate filter = null; + if (!ArrayUtils.isEmpty(dataSourceIds)) { + filter = ds -> ArrayUtils.contains(dataSourceIds, ds.getId()); + } + ((DataSourcePersistentRegistry) registry).saveConfigurationToManager(new VoidProgressMonitor(), + buffer, + filter); + registry.checkForErrors(); + + return new String(buffer.getData(), StandardCharsets.UTF_8); + } ); } @@ -173,7 +188,6 @@ protected DataSourceParseResults updateProjectDataSourcesConfig( storedDataSourceConfigurations ); } catch (DBException e) { - registry.refreshConfig(); registry.checkForErrors(); } log.debug("Save data sources configuration in project '" + projectId + "'"); @@ -201,25 +215,6 @@ protected void processLoadedDataSourceConfigurationUpdate( ) throws DBException { } - @NotNull - protected String serializeProjectDataSourcesConfiguration( - @NotNull DBPProject project, - @Nullable String[] dataSourceIds - ) throws DBException { - DBPDataSourceRegistry registry = project.getDataSourceRegistry(); - registry.refreshConfig(); - registry.checkForErrors(); - DataSourceConfigurationManagerBuffer buffer = new DataSourceConfigurationManagerBuffer(); - Predicate filter = null; - if (!ArrayUtils.isEmpty(dataSourceIds)) { - filter = ds -> ArrayUtils.contains(dataSourceIds, ds.getId()); - } - ((DataSourcePersistentRegistry) registry).saveConfigurationToManager(new VoidProgressMonitor(), buffer, filter); - registry.checkForErrors(); - - return new String(buffer.getData(), StandardCharsets.UTF_8); - } - @Override public void deleteProjectDataSources( @NotNull String projectId, diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java deleted file mode 100644 index 03b07d54b25..00000000000 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandler.java +++ /dev/null @@ -1,32 +0,0 @@ - /* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2026 DBeaver Corp and others - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.cloudbeaver.model.rm.local; - -import org.jkiss.code.NotNull; -import org.jkiss.dbeaver.DBException; -import org.jkiss.dbeaver.model.rm.RMProjectPermission; - -import java.util.Set; - -public interface RMDataSourceConfigUpdateHandler { - @NotNull - String processDataSourceConfigurationUpdate( - @NotNull String currentConfiguration, - @NotNull String updatedConfiguration, - @NotNull Set projectPermissions - ) throws DBException; -} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java deleted file mode 100644 index 1da255f1b05..00000000000 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlerDescriptor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2026 DBeaver Corp and others - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.cloudbeaver.model.rm.local; - -import org.eclipse.core.runtime.IConfigurationElement; -import org.jkiss.dbeaver.DBException; -import org.jkiss.dbeaver.model.impl.AbstractDescriptor; - -public class RMDataSourceConfigUpdateHandlerDescriptor extends AbstractDescriptor { - public static final String EXTENSION_ID = "io.cloudbeaver.rm.datasource.update.handler"; //$NON-NLS-1$ - - private final RMDataSourceConfigUpdateHandler instance; - - public RMDataSourceConfigUpdateHandlerDescriptor(IConfigurationElement config) throws DBException { - super(config); - ObjectType implClass = new ObjectType(config.getAttribute("class")); - this.instance = implClass.createInstance(RMDataSourceConfigUpdateHandler.class); - } - - public RMDataSourceConfigUpdateHandler getInstance() { - return instance; - } -} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java deleted file mode 100644 index c662a2cdc93..00000000000 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/RMDataSourceConfigUpdateHandlersRegistry.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2026 DBeaver Corp and others - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.cloudbeaver.model.rm.local; - -import org.eclipse.core.runtime.IConfigurationElement; -import org.eclipse.core.runtime.IExtensionRegistry; -import org.eclipse.core.runtime.Platform; -import org.jkiss.dbeaver.DBException; -import org.jkiss.dbeaver.Log; - -import java.util.ArrayList; -import java.util.List; - -public class RMDataSourceConfigUpdateHandlersRegistry { - private static final Log log = Log.getLog(RMDataSourceConfigUpdateHandlersRegistry.class); - - private static final String TAG_HANDLER = "rmDataSourceConfigUpdateHandler"; //$NON-NLS-1$ - - private static RMDataSourceConfigUpdateHandlersRegistry instance; - - private final List handlers = new ArrayList<>(); - - public static synchronized RMDataSourceConfigUpdateHandlersRegistry getInstance() { - if (instance == null) { - instance = new RMDataSourceConfigUpdateHandlersRegistry(); - instance.loadExtensions(Platform.getExtensionRegistry()); - } - return instance; - } - - private synchronized void loadExtensions(IExtensionRegistry registry) { - IConfigurationElement[] extConfigs = registry.getConfigurationElementsFor(RMDataSourceConfigUpdateHandlerDescriptor.EXTENSION_ID); - for (IConfigurationElement ext : extConfigs) { - if (!TAG_HANDLER.equals(ext.getName())) { - continue; - } - try { - handlers.add(new RMDataSourceConfigUpdateHandlerDescriptor(ext).getInstance()); - } catch (DBException e) { - log.error("Error loading data source config update handler", e); - } - } - } - - public List getHandlers() { - return handlers; - } -} From f9e369d626b06158679f2a85e1db40a4b6becee6 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Thu, 27 Aug 2026 12:03:16 +0200 Subject: [PATCH 5/8] dbeaver/pro#8234 don't save broken config --- .../model/rm/local/BaseLocalResourceController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java index c279f328638..114f1b5141a 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java @@ -186,7 +186,8 @@ protected DataSourceParseResults updateProjectDataSourcesConfig( storedDataSourceConfigurations ); } catch (DBException e) { - registry.checkForErrors(); + registry.refreshConfig(); + throw e; } log.debug("Save data sources configuration in project '" + projectId + "'"); ((DataSourcePersistentRegistry) registry).saveDataSources(); From 74f3135be0dcd55b1c02328839d62f16dfd86d1c Mon Sep 17 00:00:00 2001 From: Ruslan Date: Fri, 28 Aug 2026 00:13:05 +0200 Subject: [PATCH 6/8] dbeaver/pro#8234 fix parser --- .../rm/local/BaseLocalResourceController.java | 13 ++- .../rm/local/LocalResourceController.java | 104 +++++++++++++++++- 2 files changed, 113 insertions(+), 4 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java index 114f1b5141a..4eea99718aa 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java @@ -30,6 +30,7 @@ import org.jkiss.dbeaver.model.fs.lock.LockManager; import org.jkiss.dbeaver.model.fs.lock.LockOptions; import org.jkiss.dbeaver.model.fs.lock.LockTarget; +import org.jkiss.dbeaver.model.net.DBWNetworkProfile; import org.jkiss.dbeaver.model.rm.RMController; import org.jkiss.dbeaver.model.rm.RMEvent; import org.jkiss.dbeaver.model.rm.RMEventManager; @@ -163,6 +164,7 @@ protected DataSourceParseResults updateProjectDataSourcesConfig( RMLocalProject project = getWebProject(projectId, false); Map storedDataSourceConfigurations = captureCurrentDataSourceConfigurations(project, dataSourceIds); + Map storedNetworkProfiles = captureCurrentNetworkProfiles(project); return doFileWriteOperation( projectId, project.getMetadataFolder(false), () -> { @@ -183,7 +185,8 @@ protected DataSourceParseResults updateProjectDataSourcesConfig( project, projectId, dataSourceIds, - storedDataSourceConfigurations + storedDataSourceConfigurations, + storedNetworkProfiles ); } catch (DBException e) { registry.refreshConfig(); @@ -206,11 +209,17 @@ protected Map captureCurrentDataSourceConfig return Map.of(); } + @NotNull + protected Map captureCurrentNetworkProfiles(@NotNull RMLocalProject project) { + return Map.of(); + } + protected void processLoadedDataSourceConfigurationUpdate( @NotNull RMLocalProject project, @NotNull String projectId, @Nullable List dataSourceIds, - @NotNull Map storedDataSourceConfigurations + @NotNull Map storedDataSourceConfigurations, + @NotNull Map storedNetworkProfiles ) throws DBException { } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java index 07af6628b36..226a891003a 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java @@ -33,8 +33,8 @@ import org.jkiss.dbeaver.model.app.DBPWorkspace; import org.jkiss.dbeaver.model.auth.SMCredentials; import org.jkiss.dbeaver.model.auth.SMCredentialsProvider; -import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration; import org.jkiss.dbeaver.model.auth.SMObjectType; +import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration; import org.jkiss.dbeaver.model.fs.lock.LockManager; import org.jkiss.dbeaver.model.fs.lock.LockOptions; import org.jkiss.dbeaver.model.fs.lock.LockTarget; @@ -436,12 +436,23 @@ protected Map captureCurrentDataSourceConfig )); } + @Override + @NotNull + protected Map captureCurrentNetworkProfiles(@NotNull RMLocalProject project) { + return project.getDataSourceRegistry().getNetworkProfiles().getProfiles().stream() + .collect(Collectors.toMap( + DBWNetworkProfile::getProfileId, + profile -> copyNetworkProfile(project, profile) + )); + } + @Override protected void processLoadedDataSourceConfigurationUpdate( @NotNull RMLocalProject project, @NotNull String projectId, @Nullable List dataSourceIds, - @NotNull Map storedDataSourceConfigurations + @NotNull Map storedDataSourceConfigurations, + @NotNull Map storedNetworkProfiles ) throws DBException { Set userProjectPermissions = getProjectPermissions(projectId, project.getProjectType()); Set grantedPermissions = userProjectPermissions.stream() @@ -460,6 +471,95 @@ protected void processLoadedDataSourceConfigurationUpdate( grantedPermissions ); } + + Map updatedNetworkProfiles = project.getDataSourceRegistry() + .getNetworkProfiles() + .getProfiles() + .stream() + .collect(Collectors.toMap(DBWNetworkProfile::getProfileId, profile -> profile)); + reconcileNetworkProfiles(storedNetworkProfiles, updatedNetworkProfiles, grantedPermissions); + } + + @NotNull + private DBWNetworkProfile copyNetworkProfile( + @NotNull RMLocalProject project, + @NotNull DBWNetworkProfile source + ) { + DBWNetworkProfile copy = new DBWNetworkProfile(project); + copy.setProfileId(source.getProfileId()); + copy.setProfileName(source.getProfileName()); + copy.setProfileDescription(source.getProfileDescription()); + copy.setProperties(new LinkedHashMap<>(source.getProperties())); + source.getConfigurations().stream() + .map(DBWHandlerConfiguration::new) + .forEach(copy::updateConfiguration); + return copy; + } + + private void reconcileNetworkProfiles( + @NotNull Map storedProfiles, + @NotNull Map updatedProfiles, + @NotNull Set grantedPermissions + ) throws DBException { + Set profileIds = new LinkedHashSet<>(storedProfiles.keySet()); + profileIds.addAll(updatedProfiles.keySet()); + + for (String profileId : profileIds) { + DBWNetworkProfile storedProfile = storedProfiles.get(profileId); + DBWNetworkProfile updatedProfile = updatedProfiles.get(profileId); + if (!hasRestrictedNetworkHandler(storedProfile, grantedPermissions) + && !hasRestrictedNetworkHandler(updatedProfile, grantedPermissions)) { + continue; + } + + if (storedProfile == null) { + throw new DBException("No permissions to configure network profile '" + profileId + "'"); + } + if (updatedProfile == null) { + throw new DBException("No permissions to delete network profile '" + profileId + "'"); + } + if (!areSameNetworkProfile(storedProfile, updatedProfile)) { + throw new DBException("No permissions to modify network profile '" + profileId + "'"); + } + } + } + + private boolean hasRestrictedNetworkHandler( + @Nullable DBWNetworkProfile profile, + @NotNull Set grantedPermissions + ) { + if (profile == null) { + return false; + } + for (DBWHandlerConfiguration configuration : profile.getConfigurations()) { + if (configuration.getHandlerDescriptor() instanceof NetworkHandlerDescriptor descriptor + && !descriptor.getRequiredPermissions().isEmpty() + && !grantedPermissions.containsAll(descriptor.getRequiredPermissions())) { + return true; + } + } + return false; + } + + private boolean areSameNetworkProfile( + @NotNull DBWNetworkProfile storedProfile, + @NotNull DBWNetworkProfile updatedProfile + ) { + if (!Objects.equals(storedProfile.getProfileId(), updatedProfile.getProfileId()) + || !Objects.equals(storedProfile.getProfileName(), updatedProfile.getProfileName()) + || !Objects.equals(storedProfile.getProfileDescription(), updatedProfile.getProfileDescription()) + || !Objects.equals(storedProfile.getProperties(), updatedProfile.getProperties()) + || storedProfile.getConfigurations().size() != updatedProfile.getConfigurations().size()) { + return false; + } + for (DBWHandlerConfiguration storedConfiguration : storedProfile.getConfigurations()) { + DBWHandlerConfiguration updatedConfiguration = updatedProfile.getConfiguration(storedConfiguration.getId()); + if (updatedConfiguration == null + || !areSameHandlerConfiguration(storedConfiguration, updatedConfiguration)) { + return false; + } + } + return true; } private void reconcileNetworkHandlers( From c1869c3b9f33e4777c1a3652259b92bdcdbf7866 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Fri, 28 Aug 2026 13:46:35 +0200 Subject: [PATCH 7/8] dbeaver/pro#8234 udbt for admins --- .../io/cloudbeaver/BaseWebProjectImpl.java | 17 +- .../WebConnectionConfigInputHandler.java | 37 ++++- .../rm/local/BaseLocalResourceController.java | 26 +-- .../rm/local/LocalResourceController.java | 151 +++--------------- .../cloudbeaver/utils/WebDataSourceUtils.java | 30 +++- 5 files changed, 110 insertions(+), 151 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/BaseWebProjectImpl.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/BaseWebProjectImpl.java index 9a2fe14182b..ad50a40115f 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/BaseWebProjectImpl.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/BaseWebProjectImpl.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2024 DBeaver Corp and others + * Copyright (C) 2010-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import org.jkiss.dbeaver.model.auth.SMSessionContext; import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl; import org.jkiss.dbeaver.model.rm.*; +import org.jkiss.utils.ArrayUtils; import java.nio.file.Path; @@ -109,6 +110,20 @@ public boolean isPrivateProject() { return RMProjectType.USER.equals(getRMProject().getType()); } + @Override + public boolean hasRealmPermission(@NotNull String permission) { + if (getWorkspace().hasRealmPermission(permission)) { + return true; + } + for (String projectPermissionId : ArrayUtils.safeArray(getRMProject().getProjectPermissions())) { + RMProjectPermission projectPermission = RMProjectPermission.fromPermission(projectPermissionId); + if (projectPermission != null && projectPermission.getAllPermissions().contains(permission)) { + return true; + } + } + return false; + } + @Override public void updateProject(@Nullable String newName, @Nullable String description) throws DBException { RMProject rmProject = getResourceController().updateProject(this.getId(), new RMProjectInfo(newName, description)); diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebConnectionConfigInputHandler.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebConnectionConfigInputHandler.java index ed159db4f3c..d68db8770e8 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebConnectionConfigInputHandler.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebConnectionConfigInputHandler.java @@ -28,6 +28,8 @@ import org.jkiss.dbeaver.model.connection.DBPDriver; import org.jkiss.dbeaver.registry.DataSourceDescriptor; import org.jkiss.dbeaver.registry.DataSourcePreferenceStore; +import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry; +import org.jkiss.dbeaver.runtime.DBSecurityException; import org.jkiss.utils.CommonUtils; public class WebConnectionConfigInputHandler { @@ -67,6 +69,18 @@ public C createDataSourceContainer() throws DBWebException { } public void updateDataSource(@NotNull C dataSource) throws DBWebException { + DBPConnectionConfiguration storedConfiguration = new DBPConnectionConfiguration( + dataSource.getConnectionConfiguration() + ); + DBPConnectionConfiguration updatedConfiguration = new DBPConnectionConfiguration(storedConfiguration); + WebDataSourceUtils.setConnectionConfiguration( + dataSource.getDriver(), + updatedConfiguration, + input, + registry.getProject() + ); + validateHandlerConfigurationUpdate(storedConfiguration, updatedConfiguration); + dataSource.setId(dataSource.getId()); if (!CommonUtils.isEmpty(input.getName())) { dataSource.setName(input.getName()); @@ -81,8 +95,10 @@ public void updateDataSource(@NotNull C dataSource) throws DBWebException { WebDataSourceUtils.setConnectionConfiguration( dataSource.getDriver(), dataSource.getConnectionConfiguration(), - input + input, + registry.getProject() ); + validateHandlerConfigurationUpdate(storedConfiguration, dataSource.getConnectionConfiguration()); WebDataSourceUtils.saveAuthProperties( webSession.getProgressMonitor(), dataSource, @@ -97,10 +113,25 @@ public void updateDataSource(@NotNull C dataSource) throws DBWebException { preferenceStore.setProperties(input.getDefaultUserPreferences()); } + private void validateHandlerConfigurationUpdate( + @NotNull DBPConnectionConfiguration storedConfiguration, + @NotNull DBPConnectionConfiguration updatedConfiguration + ) throws DBWebException { + try { + NetworkHandlerRegistry.getInstance().validateHandlerConfigurationUpdate( + storedConfiguration, + updatedConfiguration, + registry.getProject()::hasRealmPermission + ); + } catch (DBSecurityException e) { + throw new DBWebException(e.getMessage(), e); + } + } + @NotNull - protected C createDataSourceContainerFromInput(@NotNull DBPDriver driver) { + protected C createDataSourceContainerFromInput(@NotNull DBPDriver driver) throws DBWebException { DBPConnectionConfiguration dsConfig = new DBPConnectionConfiguration(); - WebDataSourceUtils.setConnectionConfiguration(driver, dsConfig, input); + WebDataSourceUtils.setConnectionConfiguration(driver, dsConfig, input, registry.getProject()); C newDataSource = registry.createDataSource(driver, dsConfig); newDataSource.setSavePassword(true); diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java index 4eea99718aa..41e089124b2 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java @@ -172,15 +172,15 @@ protected DataSourceParseResults updateProjectDataSourcesConfig( DBPDataSourceConfigurationStorage storage = new DataSourceMemoryStorage(configuration.getBytes( StandardCharsets.UTF_8)); DataSourceConfigurationManager manager = new DataSourceConfigurationManagerBuffer(); - final DataSourceParseResults parseResults = ((DataSourcePersistentRegistry) registry).loadDataSources( - List.of(storage), - manager, - dataSourceIds, - true, - dataSourceIds == null - ); - registry.checkForErrors(); try { + final DataSourceParseResults parseResults = ((DataSourcePersistentRegistry) registry).loadDataSources( + List.of(storage), + manager, + dataSourceIds, + true, + dataSourceIds == null + ); + registry.checkForErrors(); processLoadedDataSourceConfigurationUpdate( project, projectId, @@ -188,14 +188,14 @@ protected DataSourceParseResults updateProjectDataSourcesConfig( storedDataSourceConfigurations, storedNetworkProfiles ); - } catch (DBException e) { + log.debug("Save data sources configuration in project '" + projectId + "'"); + ((DataSourcePersistentRegistry) registry).saveDataSources(); + registry.checkForErrors(); + return parseResults; + } catch (DBException | RuntimeException e) { registry.refreshConfig(); throw e; } - log.debug("Save data sources configuration in project '" + projectId + "'"); - ((DataSourcePersistentRegistry) registry).saveDataSources(); - registry.checkForErrors(); - return parseResults; } ); } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java index 226a891003a..22951da216a 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java @@ -56,7 +56,7 @@ import org.jkiss.dbeaver.registry.DataSourceParseResults; import org.jkiss.dbeaver.registry.ResourceTypeDescriptor; import org.jkiss.dbeaver.registry.ResourceTypeRegistry; -import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor; +import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry; import org.jkiss.dbeaver.runtime.DBWorkbench; import org.jkiss.dbeaver.utils.GeneralUtils; import org.jkiss.utils.CommonUtils; @@ -69,6 +69,7 @@ import java.time.OffsetDateTime; import java.time.ZoneId; import java.util.*; +import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -456,8 +457,13 @@ protected void processLoadedDataSourceConfigurationUpdate( ) throws DBException { Set userProjectPermissions = getProjectPermissions(projectId, project.getProjectType()); Set grantedPermissions = userProjectPermissions.stream() + .filter(Objects::nonNull) .flatMap(permission -> permission.getAllPermissions().stream()) .collect(Collectors.toSet()); + Predicate hasPermission = permission -> + grantedPermissions.contains(permission) || + credentialsProvider.hasPermission(DBWConstants.PERMISSION_ADMIN) || + credentialsProvider.hasPermission(permission); for (DBPDataSourceContainer dataSource : project.getDataSourceRegistry().getDataSources()) { if (dataSourceIds != null && !dataSourceIds.contains(dataSource.getId())) { @@ -465,10 +471,10 @@ protected void processLoadedDataSourceConfigurationUpdate( } DBPConnectionConfiguration storedConfiguration = storedDataSourceConfigurations.get(dataSource.getId()); - reconcileNetworkHandlers( + NetworkHandlerRegistry.getInstance().validateHandlerConfigurationUpdate( storedConfiguration, dataSource.getConnectionConfiguration(), - grantedPermissions + hasPermission ); } @@ -477,7 +483,17 @@ protected void processLoadedDataSourceConfigurationUpdate( .getProfiles() .stream() .collect(Collectors.toMap(DBWNetworkProfile::getProfileId, profile -> profile)); - reconcileNetworkProfiles(storedNetworkProfiles, updatedNetworkProfiles, grantedPermissions); + Set profileIds = new LinkedHashSet<>(storedNetworkProfiles.keySet()); + profileIds.addAll(updatedNetworkProfiles.keySet()); + for (String profileId : profileIds) { + DBWNetworkProfile storedProfile = storedNetworkProfiles.get(profileId); + DBWNetworkProfile updatedProfile = updatedNetworkProfiles.get(profileId); + NetworkHandlerRegistry.getInstance().validateHandlerConfigurationUpdate( + storedProfile == null ? List.of() : storedProfile.getConfigurations(), + updatedProfile == null ? List.of() : updatedProfile.getConfigurations(), + hasPermission + ); + } } @NotNull @@ -496,133 +512,6 @@ private DBWNetworkProfile copyNetworkProfile( return copy; } - private void reconcileNetworkProfiles( - @NotNull Map storedProfiles, - @NotNull Map updatedProfiles, - @NotNull Set grantedPermissions - ) throws DBException { - Set profileIds = new LinkedHashSet<>(storedProfiles.keySet()); - profileIds.addAll(updatedProfiles.keySet()); - - for (String profileId : profileIds) { - DBWNetworkProfile storedProfile = storedProfiles.get(profileId); - DBWNetworkProfile updatedProfile = updatedProfiles.get(profileId); - if (!hasRestrictedNetworkHandler(storedProfile, grantedPermissions) - && !hasRestrictedNetworkHandler(updatedProfile, grantedPermissions)) { - continue; - } - - if (storedProfile == null) { - throw new DBException("No permissions to configure network profile '" + profileId + "'"); - } - if (updatedProfile == null) { - throw new DBException("No permissions to delete network profile '" + profileId + "'"); - } - if (!areSameNetworkProfile(storedProfile, updatedProfile)) { - throw new DBException("No permissions to modify network profile '" + profileId + "'"); - } - } - } - - private boolean hasRestrictedNetworkHandler( - @Nullable DBWNetworkProfile profile, - @NotNull Set grantedPermissions - ) { - if (profile == null) { - return false; - } - for (DBWHandlerConfiguration configuration : profile.getConfigurations()) { - if (configuration.getHandlerDescriptor() instanceof NetworkHandlerDescriptor descriptor - && !descriptor.getRequiredPermissions().isEmpty() - && !grantedPermissions.containsAll(descriptor.getRequiredPermissions())) { - return true; - } - } - return false; - } - - private boolean areSameNetworkProfile( - @NotNull DBWNetworkProfile storedProfile, - @NotNull DBWNetworkProfile updatedProfile - ) { - if (!Objects.equals(storedProfile.getProfileId(), updatedProfile.getProfileId()) - || !Objects.equals(storedProfile.getProfileName(), updatedProfile.getProfileName()) - || !Objects.equals(storedProfile.getProfileDescription(), updatedProfile.getProfileDescription()) - || !Objects.equals(storedProfile.getProperties(), updatedProfile.getProperties()) - || storedProfile.getConfigurations().size() != updatedProfile.getConfigurations().size()) { - return false; - } - for (DBWHandlerConfiguration storedConfiguration : storedProfile.getConfigurations()) { - DBWHandlerConfiguration updatedConfiguration = updatedProfile.getConfiguration(storedConfiguration.getId()); - if (updatedConfiguration == null - || !areSameHandlerConfiguration(storedConfiguration, updatedConfiguration)) { - return false; - } - } - return true; - } - - private void reconcileNetworkHandlers( - @Nullable DBPConnectionConfiguration storedConfiguration, - @NotNull DBPConnectionConfiguration updatedConfiguration, - @NotNull Set grantedPermissions - ) throws DBException { - - // Getting all handlers ids from both stored and updated configurations to check permissions for all of them - // and to add missing handlers if user has no permissions to add new ones - Set handlerIds = new LinkedHashSet<>(); - if (storedConfiguration != null) { - storedConfiguration.getHandlers().stream() - .map(DBWHandlerConfiguration::getId) - .forEach(handlerIds::add); - } - updatedConfiguration.getHandlers().stream() - .map(DBWHandlerConfiguration::getId) - .forEach(handlerIds::add); - - for (String handlerId : handlerIds) { - DBWHandlerConfiguration storedHandler = storedConfiguration == null ? null : storedConfiguration.getHandler(handlerId); - DBWHandlerConfiguration updatedHandler = updatedConfiguration.getHandler(handlerId); - DBWHandlerConfiguration descriptorSource = updatedHandler != null ? updatedHandler : storedHandler; - if (descriptorSource == null - || !(descriptorSource.getHandlerDescriptor() instanceof NetworkHandlerDescriptor descriptor)) { - continue; - } - - if (descriptor.getRequiredPermissions().isEmpty() - || grantedPermissions.containsAll(descriptor.getRequiredPermissions())) { - continue; - } - - if (storedHandler == null) { - throw new DBException("No permissions to configure network handler '" + handlerId + "'"); - } - - // if user doesn't have permissions to add new handler, we should keep old one - if (updatedHandler == null) { - updatedConfiguration.updateHandler(new DBWHandlerConfiguration(storedHandler)); - continue; - } - - if (!areSameHandlerConfiguration(storedHandler, updatedHandler)) { - throw new DBException("No permissions to modify network handler '" + handlerId + "'"); - } - } - } - - private boolean areSameHandlerConfiguration( - @NotNull DBWHandlerConfiguration storedHandler, - @NotNull DBWHandlerConfiguration updatedHandler - ) { - DBWHandlerConfiguration storedHandlerCopy = new DBWHandlerConfiguration(storedHandler); - storedHandlerCopy.setDataSource(null); - - DBWHandlerConfiguration updatedHandlerCopy = new DBWHandlerConfiguration(updatedHandler); - updatedHandlerCopy.setDataSource(null); - - return storedHandlerCopy.equals(updatedHandlerCopy); - } - @Override public void deleteProjectDataSources(@NotNull String projectId, @NotNull String[] dataSourceIds) throws DBException { super.deleteProjectDataSources(projectId, dataSourceIds); diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebDataSourceUtils.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebDataSourceUtils.java index bcf1f339586..a232b8d490b 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebDataSourceUtils.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebDataSourceUtils.java @@ -26,10 +26,12 @@ import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.DBRuntimeException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBConstants; import org.jkiss.dbeaver.model.DBPDataSourceContainer; import org.jkiss.dbeaver.model.access.DBAAuthCredentials; +import org.jkiss.dbeaver.model.access.DBAPermissionRealm; import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry; import org.jkiss.dbeaver.model.app.DBPProject; import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration; @@ -290,6 +292,24 @@ public static void setConnectionConfiguration( @NotNull DBPConnectionConfiguration dsConfig, @NotNull WebConnectionConfig config ) { + try { + setConnectionConfiguration( + driver, + dsConfig, + config, + DBWorkbench.getPlatform().getWorkspace() + ); + } catch (DBWebException e) { + throw new DBRuntimeException(e); + } + } + + public static void setConnectionConfiguration( + @NotNull DBPDriver driver, + @NotNull DBPConnectionConfiguration dsConfig, + @NotNull WebConnectionConfig config, + @NotNull DBAPermissionRealm permissionRealm + ) throws DBWebException { setMainProperties(dsConfig, config); if (config.getProperties() != null) { Map newProps = new LinkedHashMap<>(); @@ -342,10 +362,14 @@ public static void setConnectionConfiguration( for (WebNetworkHandlerConfigInput nhc : config.getNetworkHandlersConfig()) { DBWHandlerConfiguration handlerConfig = dsConfig.getHandler(nhc.getId()); if (handlerConfig == null) { - NetworkHandlerDescriptor handlerDescriptor = NetworkHandlerRegistry.getInstance().getDescriptor(nhc.getId()); + NetworkHandlerDescriptor handlerDescriptor = NetworkHandlerRegistry.getInstance() + .getDescriptor(nhc.getId(), permissionRealm); if (handlerDescriptor == null) { - log.warn("Can't find network handler '" + nhc.getId() + "'"); - continue; + if (NetworkHandlerRegistry.getInstance().getRawDescriptor(nhc.getId()) == null) { + log.warn("Can't find network handler '" + nhc.getId() + "'"); + continue; + } + throw new DBWebException("No permissions to configure network handler '" + nhc.getId() + "'"); } else { handlerConfig = new DBWHandlerConfiguration(handlerDescriptor, null); WebDataSourceUtils.updateHandlerConfig(handlerConfig, nhc); From 27022774514509a9c8d0c5bce1800aa9d82df7de Mon Sep 17 00:00:00 2001 From: Ruslan Date: Fri, 28 Aug 2026 16:30:35 +0200 Subject: [PATCH 8/8] dbeaver/pro#8234 fix --- .../rm/local/LocalResourceController.java | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java index 22951da216a..911919c5b50 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java @@ -41,7 +41,6 @@ import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl; import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl; import org.jkiss.dbeaver.model.navigator.DBNLocalFolder; -import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration; import org.jkiss.dbeaver.model.net.DBWNetworkProfile; import org.jkiss.dbeaver.model.rm.*; import org.jkiss.dbeaver.model.security.SMAdminController; @@ -443,7 +442,7 @@ protected Map captureCurrentNetworkProfiles(@NotNull return project.getDataSourceRegistry().getNetworkProfiles().getProfiles().stream() .collect(Collectors.toMap( DBWNetworkProfile::getProfileId, - profile -> copyNetworkProfile(project, profile) + DBWNetworkProfile::new )); } @@ -496,22 +495,6 @@ protected void processLoadedDataSourceConfigurationUpdate( } } - @NotNull - private DBWNetworkProfile copyNetworkProfile( - @NotNull RMLocalProject project, - @NotNull DBWNetworkProfile source - ) { - DBWNetworkProfile copy = new DBWNetworkProfile(project); - copy.setProfileId(source.getProfileId()); - copy.setProfileName(source.getProfileName()); - copy.setProfileDescription(source.getProfileDescription()); - copy.setProperties(new LinkedHashMap<>(source.getProperties())); - source.getConfigurations().stream() - .map(DBWHandlerConfiguration::new) - .forEach(copy::updateConfiguration); - return copy; - } - @Override public void deleteProjectDataSources(@NotNull String projectId, @NotNull String[] dataSourceIds) throws DBException { super.deleteProjectDataSources(projectId, dataSourceIds);