diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebConnectionConfigInputHandler.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebConnectionConfigInputHandler.java new file mode 100644 index 0000000000..eba9e6e1d2 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebConnectionConfigInputHandler.java @@ -0,0 +1,109 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2024 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; + +import io.cloudbeaver.model.WebConnectionConfig; +import io.cloudbeaver.model.app.WebAppConfiguration; +import io.cloudbeaver.utils.ServletAppUtils; +import io.cloudbeaver.utils.WebDataSourceUtils; +import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.Log; +import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry; +import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration; +import org.jkiss.dbeaver.model.connection.DBPDriver; +import org.jkiss.dbeaver.registry.DataSourceDescriptor; +import org.jkiss.utils.CommonUtils; + +public class WebConnectionConfigInputHandler { + private static final Log log = Log.getLog(WebConnectionConfigInputHandler.class); + protected final T input; + protected final DBPDataSourceRegistry registry; + + public WebConnectionConfigInputHandler(@NotNull DBPDataSourceRegistry registry, T configInput) { + this.registry = registry; + this.input = configInput; + } + + public C createDataSourceContainer() throws DBWebException { + String driverId = input.getDriverId(); + if (CommonUtils.isEmpty(driverId)) { + throw new DBWebException("Driver must be specified"); + } + DBPDriver driver = WebDataSourceUtils.getDriverById(driverId); + + C newDataSource = createDataSourceContainerFromInput(driver); + + if (ServletAppUtils.getServletApplication().getAppConfiguration() instanceof WebAppConfiguration webAppConfiguration) { + newDataSource.setNavigatorSettings(webAppConfiguration.getDefaultNavigatorSettings()); + } + + WebDataSourceUtils.saveAuthProperties( + newDataSource, + newDataSource.getConnectionConfiguration(), + input.getCredentials(), + input.isSaveCredentials(), + input.isSharedCredentials() + ); + return newDataSource; + } + + public void updateDataSource(@NotNull C dataSource) throws DBWebException { + dataSource.setId(dataSource.getId()); + if (!CommonUtils.isEmpty(input.getName())) { + dataSource.setName(input.getName()); + } + + if (input.getDescription() != null) { + dataSource.setDescription(input.getDescription()); + } + + dataSource.setFolder(input.getFolder() != null ? registry.getFolder(input.getFolder()) : null); + if (input.isDefaultAutoCommit() != null) { + dataSource.setDefaultAutoCommit(input.isDefaultAutoCommit()); + } + WebDataSourceUtils.setConnectionConfiguration( + dataSource.getDriver(), + dataSource.getConnectionConfiguration(), + input + ); + WebDataSourceUtils.saveAuthProperties( + dataSource, + dataSource.getConnectionConfiguration(), + input.getCredentials(), + input.isSaveCredentials(), + input.isSharedCredentials() + ); + } + + @NotNull + protected C createDataSourceContainerFromInput(@NotNull DBPDriver driver) { + DBPConnectionConfiguration dsConfig = new DBPConnectionConfiguration(); + WebDataSourceUtils.setConnectionConfiguration(driver, dsConfig, input); + C newDataSource = registry.createDataSource(driver, dsConfig); + + newDataSource.setSavePassword(true); + newDataSource.setName(CommonUtils.notNull(input.getName(), "NewConnection")); + newDataSource.setDescription(input.getDescription()); + newDataSource.setConnectionReadOnly(input.isReadOnly()); + if (input.getFolder() != null) { + newDataSource.setFolder(registry.getFolder(input.getFolder())); + } + return newDataSource; + } + + +} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebSessionProjectImpl.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebSessionProjectImpl.java index 7e5ec7a754..0b84c19b1e 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebSessionProjectImpl.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebSessionProjectImpl.java @@ -16,11 +16,13 @@ */ package io.cloudbeaver; +import io.cloudbeaver.model.WebConnectionConfig; import io.cloudbeaver.model.WebConnectionInfo; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.utils.WebDataSourceUtils; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; +import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBPDataSourceContainer; import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry; @@ -33,6 +35,7 @@ import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty; import org.jkiss.dbeaver.registry.DataSourceDescriptor; import org.jkiss.dbeaver.registry.DataSourceRegistry; import org.jkiss.dbeaver.runtime.jobs.DisconnectJob; +import org.jkiss.utils.CommonUtils; import java.nio.file.Path; import java.util.ArrayList; @@ -159,7 +162,7 @@ public class WebSessionProjectImpl extends WebProjectImpl { */ @NotNull public synchronized WebConnectionInfo addConnection(@NotNull DBPDataSourceContainer dataSourceContainer) { - WebConnectionInfo connection = new WebConnectionInfo(webSession, dataSourceContainer); + WebConnectionInfo connection = createConnectionInfo(dataSourceContainer); synchronized (connections) { connections.put(dataSourceContainer.getId(), connection); } @@ -237,4 +240,110 @@ public class WebSessionProjectImpl extends WebProjectImpl { } return sendDataSourceUpdatedEvent; } + + @NotNull + public WebConnectionInfo createConnectionInfo(@NotNull DBPDataSourceContainer dataSourceDescriptor) { + return new WebConnectionInfo(webSession, dataSourceDescriptor); + } + + @NotNull + public WebConnectionInfo createConnection(@NotNull Map configMap) throws DBWebException { + if (CommonUtils.isEmpty(configMap)) { + throw new DBWebException("Connection configuration parameters are missing"); + } + DBPDataSourceContainer newDataSource = getDataSourceContainerFromInput(configMap); + return addDataSourceToProject(newDataSource); + } + + @NotNull + public WebConnectionInfo addDataSourceToProject(@NotNull DBPDataSourceContainer newDataSource) throws DBWebException { + DBPDataSourceRegistry registry = getDataSourceRegistry(); + try { + registry.addDataSource(newDataSource); + registry.checkForErrors(); + } catch (DBException e) { + registry.removeDataSource(newDataSource); + throw new DBWebException("Failed to create connection", e); + } + + WebConnectionInfo connectionInfo = addConnection(newDataSource); + webSession.addInfoMessage("New connection was created - " + WebDataSourceUtils.getConnectionContainerInfo( + newDataSource)); + log.info(String.format( + "New connection was created: [info=%s, user=%s]", + WebDataSourceUtils.getConnectionContainerInfo(newDataSource), + webSession.getUserId() + )); + return connectionInfo; + } + + @NotNull + public WebConnectionInfo updateConnection(@Nullable Map configMap) throws DBWebException { + WebConnectionConfig config = getConnectionConfigInput(configMap); + WebConnectionInfo connectionInfo = getWebConnectionInfo(config.getConnectionId()); + DataSourceDescriptor dataSource = (DataSourceDescriptor) connectionInfo.getDataSourceContainer(); + webSession.addInfoMessage("Update connection - " + WebDataSourceUtils.getConnectionContainerInfo(dataSource)); + + DBPDataSourceRegistry registry = getDataSourceRegistry(); + getInputConfigHandler(configMap).updateDataSource(dataSource); + connectionInfo.setCredentialsSavedInSession(null); + try { + registry.updateDataSource(dataSource); + registry.checkForErrors(); + } catch (DBException e) { + throw new DBWebException("Failed to update connection", e); + } + return connectionInfo; + } + + public boolean deleteConnection(@NotNull String connectionId) throws DBWebException { + WebConnectionInfo connectionInfo = getWebConnectionInfo(connectionId); + webSession.addInfoMessage("Delete connection - " + + WebDataSourceUtils.getConnectionContainerInfo(connectionInfo.getDataSourceContainer())); + closeAndDeleteConnection(connectionInfo); + + log.info(String.format( + "Connection deleted: [info=%s, userId=%s]", + WebDataSourceUtils.getConnectionContainerInfo(connectionInfo.getDataSourceContainer()), + webSession.getUserId() + )); + return true; + } + + @NotNull + private WebConnectionInfo closeAndDeleteConnection(@NotNull WebConnectionInfo connectionInfo) throws DBWebException { + DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer(); + boolean disconnected = WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer); + DBPDataSourceRegistry registry = getDataSourceRegistry(); + registry.removeDataSource(dataSourceContainer); + try { + registry.checkForErrors(); + } catch (DBException e) { + try { + registry.addDataSource(dataSourceContainer); + } catch (DBException ex) { + log.error("Error re-adding after delete attempt", e); + } + throw new DBWebException("Failed to delete connection", e); + } + removeConnection(dataSourceContainer); + return connectionInfo; + } + + @NotNull + public DataSourceDescriptor getDataSourceContainerFromInput(@NotNull Map configMap) throws DBWebException { + return getInputConfigHandler(configMap).createDataSourceContainer(); + } + + @NotNull + public WebConnectionConfig getConnectionConfigInput(@Nullable Map configMap) { + return new WebConnectionConfig(configMap == null ? Map.of() : configMap); + } + + @NotNull + protected WebConnectionConfigInputHandler getInputConfigHandler(@NotNull Map configMap) { + return new WebConnectionConfigInputHandler<>(getDataSourceRegistry(), getConnectionConfigInput(configMap)); + } + + } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionConfig.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionConfig.java index d2787d958d..78ba286606 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionConfig.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionConfig.java @@ -16,11 +16,11 @@ */ package io.cloudbeaver.model; +import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.model.connection.DBPDriverConfigurationType; import org.jkiss.dbeaver.model.data.json.JSONUtils; import org.jkiss.dbeaver.model.meta.Property; -import org.jkiss.utils.CommonUtils; import java.util.ArrayList; import java.util.List; @@ -58,7 +58,6 @@ public class WebConnectionConfig { private Map mainPropertyValues; private Map expertSettingsValues; private Map providerProperties; - private Map externalParameters; private List networkHandlersConfig; private DBPDriverConfigurationType configurationType; private String selectedSecretId; @@ -69,54 +68,52 @@ public class WebConnectionConfig { public WebConnectionConfig() { } - public WebConnectionConfig(Map params) { - if (!CommonUtils.isEmpty(params)) { - connectionId = JSONUtils.getString(params, "connectionId"); - driverId = JSONUtils.getString(params, "driverId"); + public WebConnectionConfig(@NotNull Map params) { + connectionId = JSONUtils.getString(params, "connectionId"); + driverId = JSONUtils.getString(params, "driverId"); - host = JSONUtils.getString(params, "host"); - port = JSONUtils.getString(params, "port"); - serverName = JSONUtils.getString(params, "serverName"); - databaseName = JSONUtils.getString(params, "databaseName"); - url = JSONUtils.getString(params, "url"); + host = JSONUtils.getString(params, "host"); + port = JSONUtils.getString(params, "port"); + serverName = JSONUtils.getString(params, "serverName"); + databaseName = JSONUtils.getString(params, "databaseName"); + url = JSONUtils.getString(params, "url"); - name = JSONUtils.getString(params, "name"); - description = JSONUtils.getString(params, "description"); - folder = JSONUtils.getString(params, "folder"); + name = JSONUtils.getString(params, "name"); + description = JSONUtils.getString(params, "description"); + folder = JSONUtils.getString(params, "folder"); - properties = JSONUtils.getObjectOrNull(params, "properties"); - userName = JSONUtils.getString(params, "userName"); - userPassword = JSONUtils.getString(params, "userPassword"); - selectedSecretId = JSONUtils.getString(params, "selectedSecretId"); + properties = JSONUtils.getObjectOrNull(params, "properties"); + userName = JSONUtils.getString(params, "userName"); + userPassword = JSONUtils.getString(params, "userPassword"); + selectedSecretId = JSONUtils.getString(params, "selectedSecretId"); - authModelId = JSONUtils.getString(params, "authModelId"); - credentials = JSONUtils.getObjectOrNull(params, "credentials"); - saveCredentials = JSONUtils.getBoolean(params, "saveCredentials"); - sharedCredentials = JSONUtils.getBoolean(params, "sharedCredentials"); + authModelId = JSONUtils.getString(params, "authModelId"); + credentials = JSONUtils.getObjectOrNull(params, "credentials"); + saveCredentials = JSONUtils.getBoolean(params, "saveCredentials"); + sharedCredentials = JSONUtils.getBoolean(params, "sharedCredentials"); - mainPropertyValues = JSONUtils.getObjectOrNull(params, "mainPropertyValues"); - providerProperties = JSONUtils.getObjectOrNull(params, "providerProperties"); + mainPropertyValues = JSONUtils.getObjectOrNull(params, "mainPropertyValues"); + providerProperties = JSONUtils.getObjectOrNull(params, "providerProperties"); + expertSettingsValues = JSONUtils.getObjectOrNull(params, "expertSettingsValues"); + keepAliveInterval = JSONUtils.getInteger( + expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_KEEP_ALIVE_INTERVAL, -1); + readOnly = JSONUtils.getBoolean( + expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_READ_ONLY); + defaultAutoCommit = JSONUtils.getBoolean( + expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_AUTO_COMMIT, true); + defaultCatalogName = JSONUtils.getString( + expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_DEFAULT_CATALOG); + defaultSchemaName = JSONUtils.getString( + expertSettingsValues != null ? expertSettingsValues : params, + WebExpertSettingsProperties.PROP_DEFAULT_SCHEMA + ); - expertSettingsValues = JSONUtils.getObjectOrNull(params, "expertSettingsValues"); - keepAliveInterval = JSONUtils.getInteger( - expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_KEEP_ALIVE_INTERVAL, -1); - readOnly = JSONUtils.getBoolean( - expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_READ_ONLY); - defaultAutoCommit = JSONUtils.getBoolean( - expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_AUTO_COMMIT, true); - defaultCatalogName = JSONUtils.getString( - expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_DEFAULT_CATALOG); - defaultSchemaName = JSONUtils.getString( - expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_DEFAULT_SCHEMA); + String configType = JSONUtils.getString(params, "configurationType"); + configurationType = configType == null ? null : DBPDriverConfigurationType.valueOf(configType); - String configType = JSONUtils.getString(params, "configurationType"); - configurationType = configType == null ? null : DBPDriverConfigurationType.valueOf(configType); - externalParameters = JSONUtils.getObjectOrNull(params, "externalParameters"); - - networkHandlersConfig = new ArrayList<>(); - for (Map nhc : JSONUtils.getObjectList(params, "networkHandlersConfig")) { - networkHandlersConfig.add(new WebNetworkHandlerConfigInput(nhc)); - } + networkHandlersConfig = new ArrayList<>(); + for (Map nhc : JSONUtils.getObjectList(params, "networkHandlersConfig")) { + networkHandlersConfig.add(new WebNetworkHandlerConfigInput(nhc)); } } @@ -262,8 +259,4 @@ public class WebConnectionConfig { public String getDefaultSchemaName() { return defaultSchemaName; } - - public Map getExternalParameters() { - return externalParameters; - } } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java index a49b1f16b5..8c3591c363 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java @@ -16,6 +16,7 @@ */ package io.cloudbeaver.model; +import io.cloudbeaver.DBWebException; import io.cloudbeaver.WebProjectImpl; import io.cloudbeaver.model.app.BaseWebAppConfiguration; import io.cloudbeaver.model.session.WebSession; @@ -319,6 +320,10 @@ public class WebConnectionInfo { !dataSourceContainer.getDriver().isAnonymousAccess(); } + public void validateConnection() throws DBWebException { + + } + // we don't show non-secured properties in FE when connecting to DB without saved credentials private boolean isAuthPropertiesEmpty() { return Arrays.stream(getAuthProperties()).allMatch(f -> f.hasFeature(DBConstants.PROP_FEATURE_NON_SECURED)); 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 23717d53a1..a796ba8542 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 @@ -23,6 +23,7 @@ import com.google.gson.Strictness; import io.cloudbeaver.DBWConstants; import io.cloudbeaver.DBWebException; import io.cloudbeaver.WebSessionProjectImpl; +import io.cloudbeaver.model.WebConnectionConfig; import io.cloudbeaver.model.WebConnectionInfo; import io.cloudbeaver.model.WebNetworkHandlerConfigInput; import io.cloudbeaver.model.session.WebSession; @@ -30,21 +31,25 @@ import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; 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.DBAAuthCredentialsWithComplexProperties; import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry; import org.jkiss.dbeaver.model.app.DBPProject; import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration; +import org.jkiss.dbeaver.model.connection.DBPDataSourceProviderDescriptor; +import org.jkiss.dbeaver.model.connection.DBPDriver; +import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNativeCredentials; import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration; import org.jkiss.dbeaver.model.net.ssh.SSHConstants; import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceDisconnectEvent; +import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor; +import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry; import org.jkiss.dbeaver.runtime.DBWorkbench; import org.jkiss.utils.CommonUtils; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; public class WebDataSourceUtils { @@ -219,4 +224,181 @@ public class WebDataSourceUtils { } credGson.fromJson(credGson.toJsonTree(properties), credentials.getClass()); } + + public static void saveAuthProperties( + @NotNull DBPDataSourceContainer dataSourceContainer, + @NotNull DBPConnectionConfiguration configuration, + @Nullable Map authProperties, + boolean saveCredentials, + boolean sharedCredentials + ) { + saveAuthProperties(dataSourceContainer, configuration, authProperties, saveCredentials, sharedCredentials, false); + } + + public static void saveAuthProperties( + @NotNull DBPDataSourceContainer dataSourceContainer, + @NotNull DBPConnectionConfiguration configuration, + @Nullable Map authProperties, + boolean saveCredentials, + boolean sharedCredentials, + boolean isTest + ) { + dataSourceContainer.setSavePassword(saveCredentials); + dataSourceContainer.setSharedCredentials(sharedCredentials); + if (!saveCredentials) { + // Reset credentials + if (authProperties == null) { + authProperties = new LinkedHashMap<>(); + } + authProperties.replace(AuthModelDatabaseNativeCredentials.PROP_USER_PASSWORD, null); + dataSourceContainer.resetPassword(); + } else { + if (authProperties == null) { + // No changes + return; + } + } + { + // Read save credentials + DBAAuthCredentials credentials = configuration.getAuthModel().loadCredentials(dataSourceContainer, configuration); + + if (isTest) { + var currentAuthProps = new HashMap(); + for (Map.Entry stringObjectEntry : authProperties.entrySet()) { + var value = stringObjectEntry.getValue() == null ? null : stringObjectEntry.getValue().toString(); + currentAuthProps.put(stringObjectEntry.getKey(), value); + } + configuration.setAuthProperties(currentAuthProps); + } + if (!authProperties.isEmpty()) { + updateCredentialsFromProperties(credentials, authProperties); + } + + configuration.getAuthModel().saveCredentials(dataSourceContainer, configuration, credentials); + } + } + + public static void setConnectionConfiguration( + @NotNull DBPDriver driver, + @NotNull DBPConnectionConfiguration dsConfig, + @NotNull WebConnectionConfig config + ) { + setMainProperties(dsConfig, config); + if (config.getProperties() != null) { + Map newProps = new LinkedHashMap<>(); + for (Map.Entry pe : config.getProperties().entrySet()) { + newProps.put(pe.getKey(), CommonUtils.toString(pe.getValue())); + } + dsConfig.setProperties(newProps); + } + if (config.getUserName() != null) { + dsConfig.setUserName(config.getUserName()); + } + if (config.getUserPassword() != null) { + dsConfig.setUserPassword(config.getUserPassword()); + } + if (config.getAuthModelId() != null) { + dsConfig.setAuthModelId(config.getAuthModelId()); + } + if (config.getKeepAliveInterval() >= 0) { + dsConfig.setKeepAliveInterval(config.getKeepAliveInterval()); + } + if (config.isDefaultAutoCommit() != null) { + dsConfig.getBootstrap().setDefaultAutoCommit(config.isDefaultAutoCommit()); + } + dsConfig.getBootstrap().setDefaultCatalogName(config.getDefaultCatalogName()); + dsConfig.getBootstrap().setDefaultSchemaName(config.getDefaultSchemaName()); + // Save provider props + if (config.getProviderProperties() != null) { + dsConfig.setProviderProperties(new LinkedHashMap<>()); + for (Map.Entry e : config.getProviderProperties().entrySet()) { + dsConfig.setProviderProperty(e.getKey(), CommonUtils.toString(e.getValue())); + } + } + if (config.getConfigurationType() != null) { + dsConfig.setConfigurationType(config.getConfigurationType()); + } + if (CommonUtils.isEmpty(config.getUrl())) { + dsConfig.setUrl(driver.getConnectionURL(dsConfig)); + } + // Save network handlers + if (config.getNetworkHandlersConfig() != null) { + for (WebNetworkHandlerConfigInput nhc : config.getNetworkHandlersConfig()) { + DBWHandlerConfiguration handlerConfig = dsConfig.getHandler(nhc.getId()); + if (handlerConfig == null) { + NetworkHandlerDescriptor handlerDescriptor = NetworkHandlerRegistry.getInstance().getDescriptor(nhc.getId()); + if (handlerDescriptor == null) { + log.warn("Can't find network handler '" + nhc.getId() + "'"); + continue; + } else { + handlerConfig = new DBWHandlerConfiguration(handlerDescriptor, null); + WebDataSourceUtils.updateHandlerConfig(handlerConfig, nhc); + } + } else { + WebDataSourceUtils.updateHandlerConfig(handlerConfig, nhc); + } + dsConfig.updateHandler(handlerConfig); + } + } + } + + public static void setMainProperties(@NotNull DBPConnectionConfiguration dsConfig, @NotNull WebConnectionConfig config) { + if (CommonUtils.isNotEmpty(config.getUrl())) { + dsConfig.setUrl(config.getUrl()); + return; + } + if (config.getMainPropertyValues() != null) { + for (Map.Entry e : config.getMainPropertyValues().entrySet()) { + if (e.getValue() == null) { + continue; + } + switch (e.getKey()) { + case DBConstants.PROP_HOST -> dsConfig.setHostName(CommonUtils.toString(e.getValue())); + case DBConstants.PROP_PORT -> dsConfig.setHostPort(CommonUtils.toString(e.getValue())); + case DBConstants.PROP_DATABASE -> dsConfig.setDatabaseName(CommonUtils.toString(e.getValue())); + case DBConstants.PROP_SERVER -> dsConfig.setServerName(CommonUtils.toString(e.getValue())); + default -> throw new IllegalStateException("Unexpected value: " + e.getKey()); + } + } + return; + } + if (config.getHost() != null) { + dsConfig.setHostName(config.getHost()); + } + if (config.getPort() != null) { + dsConfig.setHostPort(config.getPort()); + } + if (config.getDatabaseName() != null) { + dsConfig.setDatabaseName(config.getDatabaseName()); + } + if (config.getServerName() != null) { + dsConfig.setServerName(config.getServerName()); + } + } + + @NotNull + public static DBPDriver getDriverById(String id) throws DBWebException { + int divPos = id.indexOf(':'); + if (divPos < 0) { + throw new DBWebException("Bad driver id [" + id + "]"); + } + String dsId = id.substring(0, divPos); + String driverId = id.substring(divPos + 1); + DBPDataSourceProviderDescriptor dsProvider = DBWorkbench.getPlatform().getDataSourceProviderRegistry().getDataSourceProvider(dsId); + if (dsProvider == null) { + throw new DBWebException("Data source provider '" + dsId + "' not found"); + } + DBPDriver driver = dsProvider.getDriver(driverId); + if (driver == null) { + throw new DBWebException("Driver '" + driverId + "' not found in provider '" + dsId + "'"); + } + return driver; + } + + public static String getConnectionContainerInfo(@Nullable DBPDataSourceContainer container) { + if (container == null) { + return null; + } + return container.getName() + " [" + container.getId() + "]"; + } } diff --git a/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/CBApplication.java b/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/CBApplication.java index f60cfa5109..a085b03cad 100644 --- a/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/CBApplication.java +++ b/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/CBApplication.java @@ -30,8 +30,6 @@ import io.cloudbeaver.registry.WebDriverRegistry; import io.cloudbeaver.registry.WebFeatureRegistry; import io.cloudbeaver.registry.WebServiceRegistry; import io.cloudbeaver.server.jetty.CBJettyServer; -import io.cloudbeaver.service.ConnectionController; -import io.cloudbeaver.service.ConnectionControllerCE; import io.cloudbeaver.service.DBWServiceInitializer; import io.cloudbeaver.service.DBWServiceServerConfigurator; import io.cloudbeaver.service.session.CBSessionManager; @@ -785,11 +783,6 @@ public abstract class CBApplication return new CBWebServerConfig(this); } - @Override - public ConnectionController getConnectionController() { - return new ConnectionControllerCE(); - } - @NotNull @Override public ServletSystemInformationCollector getSystemInformationCollector() { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebServiceUtils.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebServiceUtils.java index f27af0efc9..ecce9f4749 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebServiceUtils.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebServiceUtils.java @@ -18,10 +18,7 @@ package io.cloudbeaver; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import io.cloudbeaver.model.WebConnectionConfig; -import io.cloudbeaver.model.WebNetworkHandlerConfigInput; import io.cloudbeaver.model.WebPropertyInfo; -import io.cloudbeaver.model.app.ServletApplication; import io.cloudbeaver.model.app.WebAppConfiguration; import io.cloudbeaver.model.rm.DBNResourceManagerResource; import io.cloudbeaver.model.session.WebActionParameters; @@ -30,7 +27,6 @@ import io.cloudbeaver.model.utils.ConfigurationUtils; import io.cloudbeaver.registry.WebAuthProviderDescriptor; import io.cloudbeaver.registry.WebAuthProviderRegistry; import io.cloudbeaver.server.WebAppUtils; -import io.cloudbeaver.server.WebApplication; import io.cloudbeaver.service.navigator.WebPropertyFilter; import io.cloudbeaver.utils.ServletAppUtils; import io.cloudbeaver.utils.WebCommonUtils; @@ -39,25 +35,14 @@ import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; 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.app.DBPDataSourceRegistry; import org.jkiss.dbeaver.model.app.DBPProject; -import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration; import org.jkiss.dbeaver.model.connection.DBPDriver; -import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNativeCredentials; import org.jkiss.dbeaver.model.navigator.*; -import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration; import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor; import org.jkiss.dbeaver.model.rm.RMProjectType; -import org.jkiss.dbeaver.registry.DataSourceDescriptor; import org.jkiss.dbeaver.registry.DataSourceNavigatorSettings; -import org.jkiss.dbeaver.registry.DataSourceProviderDescriptor; -import org.jkiss.dbeaver.registry.DataSourceProviderRegistry; -import org.jkiss.dbeaver.registry.driver.DriverDescriptor; -import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor; -import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry; import org.jkiss.dbeaver.runtime.properties.PropertyCollector; import org.jkiss.utils.CommonUtils; @@ -74,25 +59,6 @@ public class WebServiceUtils extends WebCommonUtils { private static final Gson gson = new GsonBuilder().create(); - @NotNull - public static DBPDriver getDriverById(String id) throws DBWebException { - int divPos = id.indexOf(':'); - if (divPos < 0) { - throw new DBWebException("Bad driver id [" + id + "]"); - } - String dsId = id.substring(0, divPos); - String driverId = id.substring(divPos + 1); - DataSourceProviderDescriptor dsProvider = DataSourceProviderRegistry.getInstance().getDataSourceProvider(dsId); - if (dsProvider == null) { - throw new DBWebException("Data source provider '" + dsId + "' not found"); - } - DriverDescriptor driver = dsProvider.getDriver(driverId); - if (driver == null) { - throw new DBWebException("Driver '" + driverId + "' not found in provider '" + dsId + "'"); - } - return driver; - } - @NotNull public static DBPDataSourceRegistry getGlobalDataSourceRegistry() throws DBWebException { return WebDataSourceUtils.getGlobalDataSourceRegistry(); @@ -102,198 +68,6 @@ public class WebServiceUtils extends WebCommonUtils { return WebServiceUtils.class.getClassLoader().getResourceAsStream(path); } - @NotNull - public static DBPDataSourceContainer createConnectionFromConfig(WebConnectionConfig config, DBPDataSourceRegistry registry) throws DBWebException { - DBPDataSourceContainer newDataSource; - if (!CommonUtils.isEmpty(config.getDriverId())) { - String driverId = config.getDriverId(); - if (CommonUtils.isEmpty(driverId)) { - throw new DBWebException("Driver not specified"); - } - DBPDriver driver = getDriverById(driverId); - - DBPConnectionConfiguration dsConfig = new DBPConnectionConfiguration(); - - setConnectionConfiguration(driver, dsConfig, config); - - newDataSource = registry.createDataSource(driver, dsConfig); - } else { - throw new DBWebException("Template connection or driver must be specified"); - } - - newDataSource.setSavePassword(true); - newDataSource.setName(config.getName()); - newDataSource.setDescription(config.getDescription()); - newDataSource.setConnectionReadOnly(config.isReadOnly()); - if (config.getFolder() != null) { - newDataSource.setFolder(registry.getFolder(config.getFolder())); - } - - ServletApplication app = ServletAppUtils.getServletApplication(); - if (app instanceof WebApplication webApplication) { - ((DataSourceDescriptor) newDataSource).setNavigatorSettings( - webApplication.getAppConfiguration().getDefaultNavigatorSettings()); - } - - saveAuthProperties( - newDataSource, - newDataSource.getConnectionConfiguration(), - config.getCredentials(), - config.isSaveCredentials(), - config.isSharedCredentials() - ); - - - return newDataSource; - } - - public static void setConnectionConfiguration(DBPDriver driver, DBPConnectionConfiguration dsConfig, WebConnectionConfig config) { - setMainProperties(dsConfig, config); - if (config.getProperties() != null) { - Map newProps = new LinkedHashMap<>(); - for (Map.Entry pe : config.getProperties().entrySet()) { - newProps.put(pe.getKey(), CommonUtils.toString(pe.getValue())); - } - dsConfig.setProperties(newProps); - } - if (config.getUserName() != null) { - dsConfig.setUserName(config.getUserName()); - } - if (config.getUserPassword() != null) { - dsConfig.setUserPassword(config.getUserPassword()); - } - if (config.getAuthModelId() != null) { - dsConfig.setAuthModelId(config.getAuthModelId()); - } - if (config.getKeepAliveInterval() >= 0) { - dsConfig.setKeepAliveInterval(config.getKeepAliveInterval()); - } - if (config.isDefaultAutoCommit() != null) { - dsConfig.getBootstrap().setDefaultAutoCommit(config.isDefaultAutoCommit()); - } - dsConfig.getBootstrap().setDefaultCatalogName(config.getDefaultCatalogName()); - dsConfig.getBootstrap().setDefaultSchemaName(config.getDefaultSchemaName()); - // Save provider props - if (config.getProviderProperties() != null) { - dsConfig.setProviderProperties(new LinkedHashMap<>()); - for (Map.Entry e : config.getProviderProperties().entrySet()) { - dsConfig.setProviderProperty(e.getKey(), CommonUtils.toString(e.getValue())); - } - } - if (config.getConfigurationType() != null) { - dsConfig.setConfigurationType(config.getConfigurationType()); - } - if (CommonUtils.isEmpty(config.getUrl())) { - dsConfig.setUrl(driver.getConnectionURL(dsConfig)); - } - // Save network handlers - if (config.getNetworkHandlersConfig() != null) { - for (WebNetworkHandlerConfigInput nhc : config.getNetworkHandlersConfig()) { - DBWHandlerConfiguration handlerConfig = dsConfig.getHandler(nhc.getId()); - if (handlerConfig == null) { - NetworkHandlerDescriptor handlerDescriptor = NetworkHandlerRegistry.getInstance().getDescriptor(nhc.getId()); - if (handlerDescriptor == null) { - log.warn("Can't find network handler '" + nhc.getId() + "'"); - continue; - } else { - handlerConfig = new DBWHandlerConfiguration(handlerDescriptor, null); - WebDataSourceUtils.updateHandlerConfig(handlerConfig, nhc); - } - } else { - WebDataSourceUtils.updateHandlerConfig(handlerConfig, nhc); - } - dsConfig.updateHandler(handlerConfig); - } - } - } - - private static void setMainProperties(DBPConnectionConfiguration dsConfig, WebConnectionConfig config) { - if (CommonUtils.isNotEmpty(config.getUrl())) { - dsConfig.setUrl(config.getUrl()); - return; - } - if (config.getMainPropertyValues() != null) { - for (Map.Entry e : config.getMainPropertyValues().entrySet()) { - if (e.getValue() == null) { - continue; - } - switch (e.getKey()) { - case DBConstants.PROP_HOST -> dsConfig.setHostName(CommonUtils.toString(e.getValue())); - case DBConstants.PROP_PORT -> dsConfig.setHostPort(CommonUtils.toString(e.getValue())); - case DBConstants.PROP_DATABASE -> dsConfig.setDatabaseName(CommonUtils.toString(e.getValue())); - case DBConstants.PROP_SERVER -> dsConfig.setServerName(CommonUtils.toString(e.getValue())); - default -> throw new IllegalStateException("Unexpected value: " + e.getKey()); - } - } - return; - } - if (config.getHost() != null) { - dsConfig.setHostName(config.getHost()); - } - if (config.getPort() != null) { - dsConfig.setHostPort(config.getPort()); - } - if (config.getDatabaseName() != null) { - dsConfig.setDatabaseName(config.getDatabaseName()); - } - if (config.getServerName() != null) { - dsConfig.setServerName(config.getServerName()); - } - } - - public static void saveAuthProperties( - @NotNull DBPDataSourceContainer dataSourceContainer, - @NotNull DBPConnectionConfiguration configuration, - @Nullable Map authProperties, - boolean saveCredentials, - boolean sharedCredentials - ) { - saveAuthProperties(dataSourceContainer, configuration, authProperties, saveCredentials, sharedCredentials, false); - } - - public static void saveAuthProperties( - @NotNull DBPDataSourceContainer dataSourceContainer, - @NotNull DBPConnectionConfiguration configuration, - @Nullable Map authProperties, - boolean saveCredentials, - boolean sharedCredentials, - boolean isTest - ) { - dataSourceContainer.setSavePassword(saveCredentials); - dataSourceContainer.setSharedCredentials(sharedCredentials); - if (!saveCredentials) { - // Reset credentials - if (authProperties == null) { - authProperties = new LinkedHashMap<>(); - } - authProperties.replace(AuthModelDatabaseNativeCredentials.PROP_USER_PASSWORD, null); - dataSourceContainer.resetPassword(); - } else { - if (authProperties == null) { - // No changes - return; - } - } - { - // Read save credentials - DBAAuthCredentials credentials = configuration.getAuthModel().loadCredentials(dataSourceContainer, configuration); - - if (isTest) { - var currentAuthProps = new HashMap(); - for (Map.Entry stringObjectEntry : authProperties.entrySet()) { - var value = stringObjectEntry.getValue() == null ? null : stringObjectEntry.getValue().toString(); - currentAuthProps.put(stringObjectEntry.getKey(), value); - } - configuration.setAuthProperties(currentAuthProps); - } - if (!authProperties.isEmpty()) { - WebDataSourceUtils.updateCredentialsFromProperties(credentials, authProperties); - } - - configuration.getAuthModel().saveCredentials(dataSourceContainer, configuration, credentials); - } - } - public static DBNBrowseSettings parseNavigatorSettings(Map settingsMap) { return gson.fromJson( gson.toJsonTree(settingsMap), DataSourceNavigatorSettings.class); @@ -310,13 +84,6 @@ public class WebServiceUtils extends WebCommonUtils { WebActionParameters.saveToSession(webSession, actionParameters); } - public static String getConnectionContainerInfo(DBPDataSourceContainer container) { - if (container == null) { - return null; - } - return container.getName() + " [" + container.getId() + "]"; - } - public static void refreshDatabases(WebSession session, String projectId) throws DBWebException { DBNProject projectNode = session.getNavigatorModelOrThrow().getRoot().getProjectNode(session.getProjectById(projectId)); if (projectNode != null) { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/WebApplication.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/WebApplication.java index f6d973c029..25de26e9d0 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/WebApplication.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/WebApplication.java @@ -21,7 +21,6 @@ import io.cloudbeaver.model.app.ServletApplication; import io.cloudbeaver.model.app.WebAppConfiguration; import io.cloudbeaver.model.app.WebServerConfiguration; import io.cloudbeaver.registry.WebDriverRegistry; -import io.cloudbeaver.service.ConnectionController; import org.jkiss.code.NotNull; import java.net.InetAddress; @@ -53,6 +52,4 @@ public interface WebApplication extends ServletApplication { WebServerConfig getWebServerConfig(); - ConnectionController getConnectionController(); - } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/ConnectionController.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/ConnectionController.java deleted file mode 100644 index 5ba9d33d82..0000000000 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/ConnectionController.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2025 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.service; - -import io.cloudbeaver.DBWebException; -import io.cloudbeaver.WebObjectId; -import io.cloudbeaver.model.WebConnectionConfig; -import io.cloudbeaver.model.WebConnectionInfo; -import io.cloudbeaver.model.WebNetworkHandlerConfigInput; -import io.cloudbeaver.model.session.WebSession; -import org.jkiss.code.NotNull; -import org.jkiss.code.Nullable; -import org.jkiss.dbeaver.model.DBPDataSourceContainer; -import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry; -import org.jkiss.dbeaver.registry.DataSourceDescriptor; - -import java.util.List; -import java.util.Map; - - -public interface ConnectionController { - - DBPDataSourceContainer createDataSourceContainer( - @NotNull WebSession webSession, - @Nullable @WebObjectId String projectId, - @NotNull WebConnectionConfig connectionConfig - ) throws DBWebException; - - WebConnectionInfo createConnection( - @NotNull WebSession webSession, - @Nullable String projectId, - DBPDataSourceRegistry sessionRegistry, - DBPDataSourceContainer newDataSource - ) throws DBWebException; - - DBPDataSourceContainer getDatasourceConnection( - @NotNull WebSession webSession, - @Nullable @WebObjectId String projectId, - @NotNull WebConnectionConfig connectionConfig) throws DBWebException; - - WebConnectionInfo updateConnection( - @NotNull WebSession webSession, - @Nullable String projectId, - @NotNull WebConnectionConfig config, - DBPDataSourceContainer dataSource, - DBPDataSourceRegistry sessionRegistry - ) throws DBWebException; - - boolean deleteConnection( - @NotNull WebSession webSession, - @Nullable @WebObjectId String projectId, - @NotNull String connectionId) throws DBWebException; - - DataSourceDescriptor prepareTestConnection( - @NotNull WebSession webSession, - @Nullable String projectId, - @NotNull WebConnectionConfig connectionConfig) throws DBWebException; - - WebConnectionInfo testConnection( - @NotNull WebSession webSession, - @Nullable String projectId, - @NotNull WebConnectionConfig connectionConfig, - DataSourceDescriptor dataSource - ) throws DBWebException; - - WebConnectionInfo getConnectionState( - @NotNull WebSession webSession, - @Nullable String projectId, - @NotNull String connectionId - ) throws DBWebException; - - WebConnectionInfo initConnection( - @NotNull WebSession webSession, - @Nullable String projectId, - @NotNull String connectionId, - @NotNull Map authProperties, - @Nullable List networkCredentials, - boolean saveCredentials, - boolean sharedCredentials, - @Nullable String selectedSecretId - ) throws DBWebException; - - void validateConnection(DBPDataSourceContainer dataSourceContainer) throws DBWebException; - -} diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/ConnectionControllerCE.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/ConnectionControllerCE.java deleted file mode 100644 index f99e311c1e..0000000000 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/ConnectionControllerCE.java +++ /dev/null @@ -1,496 +0,0 @@ -/* - * DBeaver - Universal Database Manager - * Copyright (C) 2010-2025 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.service; - -import io.cloudbeaver.*; -import io.cloudbeaver.model.WebConnectionConfig; -import io.cloudbeaver.model.WebConnectionInfo; -import io.cloudbeaver.model.WebNetworkHandlerConfigInput; -import io.cloudbeaver.model.session.WebSession; -import io.cloudbeaver.utils.ServletAppUtils; -import io.cloudbeaver.utils.WebDataSourceUtils; -import org.jkiss.code.NotNull; -import org.jkiss.code.Nullable; -import org.jkiss.dbeaver.DBException; -import org.jkiss.dbeaver.Log; -import org.jkiss.dbeaver.model.DBPDataSourceContainer; -import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry; -import org.jkiss.dbeaver.model.exec.DBCConnectException; -import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration; -import org.jkiss.dbeaver.model.net.DBWHandlerType; -import org.jkiss.dbeaver.model.rm.RMProjectType; -import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; -import org.jkiss.dbeaver.model.secret.DBSSecretController; -import org.jkiss.dbeaver.model.secret.DBSSecretValue; -import org.jkiss.dbeaver.registry.DataSourceDescriptor; -import org.jkiss.dbeaver.runtime.DBWorkbench; -import org.jkiss.dbeaver.runtime.jobs.ConnectionTestJob; -import org.jkiss.dbeaver.utils.RuntimeUtils; -import org.jkiss.utils.CommonUtils; - -import java.util.List; -import java.util.Map; - -public class ConnectionControllerCE implements ConnectionController { - - private static final Log log = Log.getLog(ConnectionController.class); - - - @Override - public DBPDataSourceContainer createDataSourceContainer( - @NotNull WebSession webSession, - @Nullable String projectId, - @NotNull WebConnectionConfig connectionConfig - ) throws DBWebException { - WebSessionProjectImpl project = getProjectById(webSession, projectId); - var rmProject = project.getRMProject(); - if (rmProject.getType() == RMProjectType.USER - && !webSession.hasPermission(DBWConstants.PERMISSION_ADMIN) - && !ServletAppUtils.getServletApplication().getAppConfiguration().isSupportsCustomConnections() - ) { - throw new DBWebException("New connection create is restricted by server configuration"); - } - webSession.addInfoMessage("Create new connection"); - DBPDataSourceRegistry sessionRegistry = project.getDataSourceRegistry(); - - DBPDataSourceContainer newDataSource = WebServiceUtils.createConnectionFromConfig(connectionConfig, - sessionRegistry); - if (CommonUtils.isEmpty(newDataSource.getName())) { - newDataSource.setName(CommonUtils.notNull(connectionConfig.getName(), "NewConnection")); - } - return newDataSource; - } - - @Override - @NotNull - public WebConnectionInfo createConnection( - @NotNull WebSession webSession, - @Nullable String projectId, - DBPDataSourceRegistry sessionRegistry, - DBPDataSourceContainer newDataSource - ) throws DBWebException { - WebSessionProjectImpl project = getProjectById(webSession, projectId); - try { - sessionRegistry.addDataSource(newDataSource); - - sessionRegistry.checkForErrors(); - } catch (DBException e) { - sessionRegistry.removeDataSource(newDataSource); - throw new DBWebException("Failed to create connection", e); - } - - WebConnectionInfo connectionInfo = project.addConnection(newDataSource); - webSession.addInfoMessage("New connection was created - " + WebServiceUtils.getConnectionContainerInfo( - newDataSource)); - log.info(String.format( - "New connection was created: [info=%s, user=%s]", - WebServiceUtils.getConnectionContainerInfo(newDataSource), - webSession.getUserId() - )); - return connectionInfo; - } - - @Override - public DBPDataSourceContainer getDatasourceConnection( - @NotNull WebSession webSession, - @Nullable String projectId, - @NotNull WebConnectionConfig config - ) throws DBWebException { - // Do not check for custom connection option. Already created connections can be edited. - // Also template connections can be edited -// if (!CBApplication.getInstance().getAppConfiguration().isSupportsCustomConnections()) { -// throw new DBWebException("Connection edit is restricted by server configuration"); -// } - - WebConnectionInfo connectionInfo = WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, config.getConnectionId()); - DBPDataSourceContainer dataSource = connectionInfo.getDataSourceContainer(); - webSession.addInfoMessage("Update connection - " + WebServiceUtils.getConnectionContainerInfo(dataSource)); - getOldDataSource(dataSource); - if (!CommonUtils.isEmpty(config.getName())) { - dataSource.setName(config.getName()); - } - - if (config.getDescription() != null) { - dataSource.setDescription(config.getDescription()); - } - - WebSessionProjectImpl project = getProjectById(webSession, projectId); - DBPDataSourceRegistry sessionRegistry = project.getDataSourceRegistry(); - dataSource.setFolder(config.getFolder() != null ? sessionRegistry.getFolder(config.getFolder()) : null); - if (config.isDefaultAutoCommit() != null) { - dataSource.setDefaultAutoCommit(config.isDefaultAutoCommit()); - } - dataSource.setConnectionReadOnly(config.isReadOnly()); - WebServiceUtils.setConnectionConfiguration(dataSource.getDriver(), - dataSource.getConnectionConfiguration(), - config); - - // we should check that the config has changed but not check for password changes - dataSource.setSharedCredentials(config.isSharedCredentials()); - dataSource.setSavePassword(config.isSaveCredentials()); - boolean sharedCredentials = isSharedCredentials(dataSource); - if (sharedCredentials) { - //we must notify about the shared password change - WebServiceUtils.saveAuthProperties( - dataSource, - dataSource.getConnectionConfiguration(), - config.getCredentials(), - config.isSaveCredentials(), - config.isSharedCredentials() - ); - } - connectionInfo.setCredentialsSavedInSession(null); - // same here - return dataSource; - } - - private static boolean isSharedCredentials(DBPDataSourceContainer dataSource) { - return dataSource.isSharedCredentials() || !dataSource.getProject() - .isUseSecretStorage() && dataSource.isSavePassword(); - } - - private static DataSourceDescriptor getOldDataSource(DBPDataSourceContainer dataSource) { - DataSourceDescriptor oldDataSource; - oldDataSource = dataSource.getRegistry().createDataSource(dataSource); - oldDataSource.setId(dataSource.getId()); - return oldDataSource; - } - - @Override - public WebConnectionInfo updateConnection( - @NotNull WebSession webSession, - @Nullable String projectId, - @NotNull WebConnectionConfig config, - DBPDataSourceContainer dataSource, - DBPDataSourceRegistry sessionRegistry - ) throws DBWebException { - WebConnectionInfo connectionInfo = WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, config.getConnectionId()); - if (!isSharedCredentials(dataSource)) { - // secret controller is responsible for notification, password changes applied after checks - WebServiceUtils.saveAuthProperties( - dataSource, - dataSource.getConnectionConfiguration(), - config.getCredentials(), - config.isSaveCredentials(), - config.isSharedCredentials() - ); - } - try { - sessionRegistry.updateDataSource(dataSource); - sessionRegistry.checkForErrors(); - } catch (DBException e) { - throw new DBWebException("Failed to update connection", e); - } - log.info(String.format( - "Connection updated: [info=%s, userId=%s]", - WebServiceUtils.getConnectionContainerInfo(dataSource), - webSession.getUser() - )); - return connectionInfo; - } - - @Override - public boolean deleteConnection( - @NotNull WebSession webSession, @Nullable String projectId, @NotNull String connectionId - ) throws DBWebException { - WebConnectionInfo connectionInfo = WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, connectionId); - if (connectionInfo.getDataSourceContainer().getProject() != getProjectById(webSession, projectId)) { - throw new DBWebException("Global connection '" + connectionInfo.getName() + "' configuration cannot be deleted"); - } - webSession.addInfoMessage("Delete connection - " + - WebServiceUtils.getConnectionContainerInfo(connectionInfo.getDataSourceContainer())); - closeAndDeleteConnection(webSession, projectId, connectionId, true); - - log.info(String.format( - "Connection deleted: [info=%s, userId=%s]", - WebServiceUtils.getConnectionContainerInfo(connectionInfo.getDataSourceContainer()), - webSession.getUserId() - )); - return true; - } - - @Override - public DataSourceDescriptor prepareTestConnection( - @NotNull WebSession webSession, - @Nullable String projectId, - @NotNull WebConnectionConfig connectionConfig - ) throws DBWebException { - String connectionId = connectionConfig.getConnectionId(); - - connectionConfig.setSaveCredentials(true); // It is used in createConnectionFromConfig - - DataSourceDescriptor dataSource = (DataSourceDescriptor) WebDataSourceUtils.getLocalOrGlobalDataSource( - webSession, projectId, connectionId); - - validateConnection(dataSource); - - WebProjectImpl project = getProjectById(webSession, projectId); - DBPDataSourceRegistry sessionRegistry = project.getDataSourceRegistry(); - DataSourceDescriptor testDataSource; - if (dataSource != null) { - try { - // Check that creds are saved to trigger secrets resolve - dataSource.isCredentialsSaved(); - } catch (DBException e) { - throw new DBWebException("Can't determine whether datasource credentials are saved", e); - } - - testDataSource = (DataSourceDescriptor) dataSource.createCopy(dataSource.getRegistry()); - WebServiceUtils.setConnectionConfiguration( - testDataSource.getDriver(), - testDataSource.getConnectionConfiguration(), - connectionConfig - ); - if (connectionConfig.getSelectedSecretId() != null) { - try { - dataSource.listSharedCredentials() - .stream() - .filter(secret -> connectionConfig.getSelectedSecretId().equals(secret.getSubjectId())) - .findFirst() - .ifPresent(testDataSource::setSelectedSharedCredentials); - - } catch (DBException e) { - throw new DBWebException("Failed to load secret value: " + connectionConfig.getSelectedSecretId()); - } - } - WebServiceUtils.saveAuthProperties( - testDataSource, - testDataSource.getConnectionConfiguration(), - connectionConfig.getCredentials(), - true, - false, - true - ); - } else { - testDataSource = (DataSourceDescriptor) WebServiceUtils.createConnectionFromConfig(connectionConfig, - sessionRegistry); - } - return testDataSource; - } - - @Override - @NotNull - public WebConnectionInfo testConnection( - @NotNull WebSession webSession, - @Nullable String projectId, - @NotNull WebConnectionConfig connectionConfig, - DataSourceDescriptor testDataSource - ) throws DBWebException { - validateDriverLibrariesPresence(testDataSource); - webSession.provideAuthParameters(webSession.getProgressMonitor(), - testDataSource, - testDataSource.getConnectionConfiguration()); - testDataSource.setSavePassword(true); // We need for test to avoid password callback - if (DataSourceDescriptor.class.isAssignableFrom(testDataSource.getClass())) { - testDataSource.setAccessCheckRequired(!webSession.hasPermission(DBWConstants.PERMISSION_ADMIN)); - } - try { - ConnectionTestJob ct = new ConnectionTestJob(testDataSource, param -> { - }); - ct.run(webSession.getProgressMonitor()); - if (ct.getConnectError() != null) { - if (ct.getConnectError() instanceof DBCConnectException error) { - Throwable rootCause = CommonUtils.getRootCause(error); - if (rootCause instanceof ClassNotFoundException) { - throwDriverNotFoundException(testDataSource); - } - } - throw new DBWebException("Connection failed", ct.getConnectError()); - } - WebConnectionInfo connectionInfo = new WebConnectionInfo(webSession, testDataSource); - connectionInfo.setConnectError(ct.getConnectError()); - connectionInfo.setServerVersion(ct.getServerVersion()); - connectionInfo.setClientVersion(ct.getClientVersion()); - connectionInfo.setConnectTime(RuntimeUtils.formatExecutionTime(ct.getConnectTime())); - return connectionInfo; - } catch (DBException e) { - throw new DBWebException("Error connecting to database", e); - } - } - - private WebSessionProjectImpl getProjectById(WebSession webSession, String projectId) throws DBWebException { - WebSessionProjectImpl project = webSession.getProjectById(projectId); - if (project == null) { - throw new DBWebException("Project '" + projectId + "' not found"); - } - return project; - } - - @NotNull - private WebConnectionInfo closeAndDeleteConnection( - @NotNull WebSession webSession, - @NotNull String projectId, - @NotNull String connectionId, - boolean forceDelete - ) throws DBWebException { - WebSessionProjectImpl project = getProjectById(webSession, projectId); - WebConnectionInfo connectionInfo = project.getWebConnectionInfo(connectionId); - - DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer(); - boolean disconnected = WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer); - if (forceDelete) { - DBPDataSourceRegistry registry = project.getDataSourceRegistry(); - registry.removeDataSource(dataSourceContainer); - try { - registry.checkForErrors(); - } catch (DBException e) { - try { - registry.addDataSource(dataSourceContainer); - } catch (DBException ex) { - log.error("Error re-adding after delete attempt", e); - } - throw new DBWebException("Failed to delete connection", e); - } - project.removeConnection(dataSourceContainer); - } else { - // Just reset saved credentials - connectionInfo.clearCache(); - } - - return connectionInfo; - } - - @Override - public WebConnectionInfo getConnectionState( - @NotNull WebSession webSession, - @Nullable String projectId, - @NotNull String connectionId - ) throws DBWebException { - return WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, connectionId); - } - - @Override - public WebConnectionInfo initConnection(@NotNull WebSession webSession, @Nullable String projectId, @NotNull String connectionId, @NotNull Map authProperties, @Nullable List networkCredentials, boolean saveCredentials, boolean sharedCredentials, @Nullable String selectedSecretId) throws DBWebException { - WebConnectionInfo connectionInfo = WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, connectionId); - connectionInfo.setSavedCredentials(authProperties, networkCredentials); - - var dataSourceContainer = connectionInfo.getDataSourceContainer();; - validateConnection(dataSourceContainer); - if (dataSourceContainer.isConnected()) { - throw new DBWebException("Datasource '" + dataSourceContainer.getName() + "' is already connected"); - } - if (dataSourceContainer.isSharedCredentials() && selectedSecretId != null) { - List allSecrets; - try { - allSecrets = dataSourceContainer.listSharedCredentials(); - } catch (DBException e) { - throw new DBWebException("Error loading connection secret", e); - } - DBSSecretValue selectedSecret = - allSecrets.stream() - .filter(secret -> selectedSecretId.equals(secret.getUniqueId())) - .findFirst().orElse(null); - if (selectedSecret == null) { - throw new DBWebException("Secret not found:" + selectedSecretId); - } - dataSourceContainer.setSelectedSharedCredentials(selectedSecret); - } - - boolean oldSavePassword = dataSourceContainer.isSavePassword(); - DBRProgressMonitor monitor = webSession.getProgressMonitor(); - validateDriverLibrariesPresence(dataSourceContainer); - try { - boolean connect = dataSourceContainer.connect(monitor, true, false); - } catch (Exception e) { - if (e instanceof DBCConnectException) { - Throwable rootCause = CommonUtils.getRootCause(e); - if (rootCause instanceof ClassNotFoundException) { - throwDriverNotFoundException(dataSourceContainer); - } - } - throw new DBWebException("Error connecting to database", e); - } finally { - dataSourceContainer.setSavePassword(oldSavePassword); - connectionInfo.clearCache(); - } - // Mark all specified network configs as saved - boolean[] saveConfig = new boolean[1]; - - if (networkCredentials != null) { - networkCredentials.forEach(c -> { - if (CommonUtils.toBoolean(c.isSavePassword())) { - DBWHandlerConfiguration handlerCfg = dataSourceContainer.getConnectionConfiguration() - .getHandler(c.getId()); - if (handlerCfg != null && - // check username param only for ssh config - !(CommonUtils.isEmpty(c.getUserName()) && CommonUtils.equalObjects(handlerCfg.getType(), - DBWHandlerType.TUNNEL)) - ) { - WebDataSourceUtils.updateHandlerCredentials(handlerCfg, c); - handlerCfg.setSavePassword(true); - saveConfig[0] = true; - } - } - }); - } - if (saveCredentials) { - // Save all passed credentials in the datasource container - WebServiceUtils.saveAuthProperties( - dataSourceContainer, - dataSourceContainer.getConnectionConfiguration(), - authProperties, - true, - sharedCredentials - ); - - var project = dataSourceContainer.getProject(); - if (project.isUseSecretStorage()) { - try { - dataSourceContainer.persistSecrets( - DBSSecretController.getProjectSecretController(dataSourceContainer.getProject()) - ); - } catch (DBException e) { - throw new DBWebException("Failed to save credentials", e); - } - } - - WebDataSourceUtils.saveCredentialsInDataSource(connectionInfo, - dataSourceContainer, - dataSourceContainer.getConnectionConfiguration()); - saveConfig[0] = true; - } - if (WebServiceUtils.isGlobalProject(dataSourceContainer.getProject())) { - // Do not flush config for global project (only admin can do it - CB-2415) - if (saveCredentials) { - connectionInfo.setCredentialsSavedInSession(true); - } - saveConfig[0] = false; - } - if (saveConfig[0]) { - dataSourceContainer.persistConfiguration(); - } - - return connectionInfo; - } - - @Override - public void validateConnection(DBPDataSourceContainer dataSourceContainer) throws DBWebException { - } - - - private void validateDriverLibrariesPresence(@NotNull DBPDataSourceContainer container) throws DBWebException { - if (!DBWorkbench.isDistributed() && container.getDriver().getDriverLoader(container).needsExternalDependencies()) { - throwDriverNotFoundException(container); - } - } - - @NotNull - private static String throwDriverNotFoundException(@NotNull DBPDataSourceContainer container) throws DBWebException { - throw new DBWebException("Driver files for %s are not found. Please ask the administrator to download it." - .formatted(container.getDriver().getName())); - } -} diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java index 1c8a48a4e5..2b0b3eb649 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java @@ -130,31 +130,37 @@ public interface DBWServiceCore extends DBWService { WebConnectionInfo createConnection( @NotNull WebSession webSession, @Nullable @WebObjectId String projectId, - @NotNull WebConnectionConfig connectionConfig + @NotNull Map connectionConfig ) throws DBWebException; @WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATASOURCES_EDIT}) WebConnectionInfo updateConnection( @NotNull WebSession webSession, @Nullable @WebObjectId String projectId, - @NotNull WebConnectionConfig connectionConfig) throws DBWebException; + @NotNull Map connectionConfig + ) throws DBWebException; @WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATASOURCES_EDIT}) boolean deleteConnection( @NotNull WebSession webSession, @Nullable @WebObjectId String projectId, - @NotNull String connectionId) throws DBWebException; + @NotNull String connectionId + ) throws DBWebException; @WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATASOURCES_EDIT}) WebConnectionInfo copyConnectionFromNode( @NotNull WebSession webSession, @Nullable @WebObjectId String projectId, @NotNull String nodePath, - @NotNull WebConnectionConfig config) throws DBWebException; + @NotNull Map connectionConfig + ) throws DBWebException; @WebAction WebConnectionInfo testConnection( - @NotNull WebSession webSession, @Nullable String projectId, @NotNull WebConnectionConfig connectionConfig) throws DBWebException; + @NotNull WebSession webSession, + @Nullable String projectId, + @NotNull Map connectionConfig + ) throws DBWebException; @WebAction WebNetworkEndpointInfo testNetworkHandler(@NotNull WebSession webSession, @NotNull WebNetworkHandlerConfigInput nhConfig) throws DBWebException; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java index 35891089f0..16f57b936a 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java @@ -17,11 +17,9 @@ package io.cloudbeaver.service.core; import graphql.TypeResolutionEnvironment; -import graphql.schema.DataFetchingEnvironment; import graphql.schema.idl.TypeRuntimeWiring; import io.cloudbeaver.DBWebException; import io.cloudbeaver.WebServiceUtils; -import io.cloudbeaver.model.WebConnectionConfig; import io.cloudbeaver.model.WebNetworkHandlerConfigInput; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.server.WebAppSessionManager; @@ -105,16 +103,20 @@ public class WebServiceBindingCore extends WebServiceBindingBase .dataFetcher("changeSessionLanguage", env -> getService(env).changeSessionLanguage(getWebSession(env), env.getArgument("locale"))) .dataFetcher("createConnection", env -> getService(env).createConnection( - getWebSession(env), getProjectReference(env), getConnectionConfig(env))) + getWebSession(env), getProjectReference(env), env.getArgument("config")) + ) .dataFetcher("updateConnection", env -> getService(env).updateConnection( - getWebSession(env), getProjectReference(env), getConnectionConfig(env))) + getWebSession(env), getProjectReference(env), env.getArgument("config")) + ) .dataFetcher("deleteConnection", env -> getService(env).deleteConnection( getWebSession(env), getProjectReference(env), env.getArgument("id"))) .dataFetcher("copyConnectionFromNode", env -> getService(env).copyConnectionFromNode( getWebSession(env), getProjectReference(env), env.getArgument("nodePath"), - new WebConnectionConfig(env.getArgument("config")))) + env.getArgument("config") + ) + ) .dataFetcher("initConnection", env -> { List> networkCredentials = env.getArgument("networkCredentials"); List nhc = null; @@ -134,7 +136,7 @@ public class WebServiceBindingCore extends WebServiceBindingBase } ) .dataFetcher("testConnection", env -> getService(env).testConnection( - getWebSession(env), getProjectReference(env), getConnectionConfig(env) + getWebSession(env), getProjectReference(env), env.getArgument("config") )) .dataFetcher("testNetworkHandler", env -> getService(env).testNetworkHandler( getWebSession(env), new WebNetworkHandlerConfigInput(env.getArgument("config")) @@ -177,8 +179,4 @@ public class WebServiceBindingCore extends WebServiceBindingBase ); } - - private WebConnectionConfig getConnectionConfig(DataFetchingEnvironment env) { - return new WebConnectionConfig(env.getArgument("config")); - } } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java index e094affe4e..0e73b0feb1 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java @@ -44,23 +44,29 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry; import org.jkiss.dbeaver.model.app.DBPProject; import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration; import org.jkiss.dbeaver.model.connection.DBPDriver; +import org.jkiss.dbeaver.model.exec.DBCConnectException; import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings; import org.jkiss.dbeaver.model.navigator.DBNDataSource; import org.jkiss.dbeaver.model.navigator.DBNModel; import org.jkiss.dbeaver.model.navigator.DBNNode; import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration; +import org.jkiss.dbeaver.model.net.DBWHandlerType; import org.jkiss.dbeaver.model.net.DBWNetworkHandler; import org.jkiss.dbeaver.model.net.DBWTunnel; import org.jkiss.dbeaver.model.net.ssh.SSHSession; import org.jkiss.dbeaver.model.rm.RMProjectType; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; -import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty; +import org.jkiss.dbeaver.model.secret.DBSSecretController; +import org.jkiss.dbeaver.model.secret.DBSSecretValue; import org.jkiss.dbeaver.registry.DataSourceDescriptor; import org.jkiss.dbeaver.registry.DataSourceProviderRegistry; import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor; import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry; import org.jkiss.dbeaver.registry.settings.ProductSettingDescriptor; import org.jkiss.dbeaver.registry.settings.ProductSettingsRegistry; +import org.jkiss.dbeaver.runtime.DBWorkbench; +import org.jkiss.dbeaver.runtime.jobs.ConnectionTestJob; +import org.jkiss.dbeaver.utils.RuntimeUtils; import org.jkiss.utils.CommonUtils; import java.util.*; @@ -280,7 +286,7 @@ public class WebServiceCore implements DBWServiceCore { @Nullable String projectId, @NotNull String connectionId ) throws DBWebException { - return WebAppUtils.getWebApplication().getConnectionController().getConnectionState(webSession, projectId, connectionId); + return WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, connectionId); } @@ -295,65 +301,137 @@ public class WebServiceCore implements DBWServiceCore { boolean sharedCredentials, @Nullable String selectedSecretId ) throws DBWebException { - return WebAppUtils.getWebApplication().getConnectionController().initConnection(webSession, projectId, - connectionId, authProperties, networkCredentials, saveCredentials, sharedCredentials, selectedSecretId); + WebConnectionInfo connectionInfo = WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, connectionId); + connectionInfo.validateConnection(); + connectionInfo.setSavedCredentials(authProperties, networkCredentials); + + var dataSourceContainer = connectionInfo.getDataSourceContainer(); + if (dataSourceContainer.isConnected()) { + throw new DBWebException("Datasource '" + dataSourceContainer.getName() + "' is already connected"); + } + if (dataSourceContainer.isSharedCredentials() && selectedSecretId != null) { + List allSecrets; + try { + allSecrets = dataSourceContainer.listSharedCredentials(); + } catch (DBException e) { + throw new DBWebException("Error loading connection secret", e); + } + DBSSecretValue selectedSecret = + allSecrets.stream() + .filter(secret -> selectedSecretId.equals(secret.getUniqueId())) + .findFirst().orElse(null); + if (selectedSecret == null) { + throw new DBWebException("Secret not found:" + selectedSecretId); + } + dataSourceContainer.setSelectedSharedCredentials(selectedSecret); + } + + boolean oldSavePassword = dataSourceContainer.isSavePassword(); + DBRProgressMonitor monitor = webSession.getProgressMonitor(); + validateDriverLibrariesPresence(dataSourceContainer); + try { + boolean connect = dataSourceContainer.connect(monitor, true, false); + } catch (Exception e) { + if (e instanceof DBCConnectException) { + Throwable rootCause = CommonUtils.getRootCause(e); + if (rootCause instanceof ClassNotFoundException) { + throwDriverNotFoundException(dataSourceContainer); + } + } + throw new DBWebException("Error connecting to database", e); + } finally { + dataSourceContainer.setSavePassword(oldSavePassword); + connectionInfo.clearCache(); + } + // Mark all specified network configs as saved + boolean[] saveConfig = new boolean[1]; + + if (networkCredentials != null) { + networkCredentials.forEach(c -> { + if (CommonUtils.toBoolean(c.isSavePassword())) { + DBWHandlerConfiguration handlerCfg = dataSourceContainer.getConnectionConfiguration() + .getHandler(c.getId()); + if (handlerCfg != null && + // check username param only for ssh config + !(CommonUtils.isEmpty(c.getUserName()) && CommonUtils.equalObjects( + handlerCfg.getType(), + DBWHandlerType.TUNNEL + )) + ) { + WebDataSourceUtils.updateHandlerCredentials(handlerCfg, c); + handlerCfg.setSavePassword(true); + saveConfig[0] = true; + } + } + }); + } + if (saveCredentials) { + // Save all passed credentials in the datasource container + WebDataSourceUtils.saveAuthProperties( + dataSourceContainer, + dataSourceContainer.getConnectionConfiguration(), + authProperties, + true, + sharedCredentials + ); + + var project = dataSourceContainer.getProject(); + if (project.isUseSecretStorage()) { + try { + dataSourceContainer.persistSecrets( + DBSSecretController.getProjectSecretController(dataSourceContainer.getProject()) + ); + } catch (DBException e) { + throw new DBWebException("Failed to save credentials", e); + } + } + + WebDataSourceUtils.saveCredentialsInDataSource( + connectionInfo, + dataSourceContainer, + dataSourceContainer.getConnectionConfiguration() + ); + saveConfig[0] = true; + } + if (WebServiceUtils.isGlobalProject(dataSourceContainer.getProject())) { + // Do not flush config for global project (only admin can do it - CB-2415) + if (saveCredentials) { + connectionInfo.setCredentialsSavedInSession(true); + } + saveConfig[0] = false; + } + if (saveConfig[0]) { + dataSourceContainer.persistConfiguration(); + } + + return connectionInfo; } @Override public WebConnectionInfo createConnection( @NotNull WebSession webSession, @Nullable String projectId, - @NotNull WebConnectionConfig connectionConfig + @NotNull Map connectionConfig ) throws DBWebException { - DBPDataSourceContainer dataSourceContainer = - WebAppUtils.getWebApplication().getConnectionController().createDataSourceContainer(webSession, projectId, connectionConfig); - return WebAppUtils.getWebApplication().getConnectionController().createConnection( - webSession, - projectId, - dataSourceContainer.getRegistry(), - dataSourceContainer - ); + return getProjectById(webSession, projectId).createConnection(connectionConfig); } @Override public WebConnectionInfo updateConnection( @NotNull WebSession webSession, @Nullable String projectId, - @NotNull WebConnectionConfig config + @NotNull Map connectionConfig ) throws DBWebException { - DBPDataSourceContainer dataSourceContainer = - WebAppUtils.getWebApplication().getConnectionController().getDatasourceConnection(webSession, projectId, config); - return WebAppUtils.getWebApplication().getConnectionController().updateConnection( - webSession, - projectId, - config, - dataSourceContainer, - dataSourceContainer.getRegistry() - ); - } - - private WSDataSourceProperty getDatasourceEventProperty( - DataSourceDescriptor oldDataSource, - DBPDataSourceContainer dataSource - ) { - if (!oldDataSource.equalConfiguration((DataSourceDescriptor) dataSource)) { - return WSDataSourceProperty.CONFIGURATION; - } - - var nameChanged = !CommonUtils.equalObjects(oldDataSource.getName(), dataSource.getName()); - var descriptionChanged = !CommonUtils.equalObjects(oldDataSource.getDescription(), dataSource.getDescription()); - if (nameChanged && descriptionChanged) { - return WSDataSourceProperty.CONFIGURATION; - } - - return nameChanged ? WSDataSourceProperty.NAME : WSDataSourceProperty.CONFIGURATION; + return getProjectById(webSession, projectId).updateConnection(connectionConfig); } @Override public boolean deleteConnection( - @NotNull WebSession webSession, @Nullable String projectId, @NotNull String connectionId + @NotNull WebSession webSession, + @Nullable String projectId, + @NotNull String connectionId ) throws DBWebException { - return WebAppUtils.getWebApplication().getConnectionController().deleteConnection(webSession, projectId, connectionId); + return getProjectById(webSession, projectId).deleteConnection(connectionId); } @@ -362,7 +440,7 @@ public class WebServiceCore implements DBWServiceCore { @NotNull WebSession webSession, @Nullable String projectId, @NotNull String nodePath, - @NotNull WebConnectionConfig config + @NotNull Map connectionConfig ) throws DBWebException { try { DBNModel navigatorModel = webSession.getNavigatorModelOrThrow(); @@ -373,19 +451,20 @@ public class WebServiceCore implements DBWServiceCore { if (srcNode == null) { throw new DBException("Node '" + nodePath + "' not found"); } - if (!(srcNode instanceof DBNDataSource)) { + if (!(srcNode instanceof DBNDataSource dbnDataSource)) { throw new DBException("Node '" + nodePath + "' is not a datasource node"); } - DBPDataSourceContainer dataSourceTemplate = ((DBNDataSource) srcNode).getDataSourceContainer(); + DBPDataSourceContainer dataSourceTemplate = dbnDataSource.getDataSourceContainer(); - DBPDataSourceContainer newDataSource = dataSourceRegistry.createDataSource(dataSourceTemplate); + DataSourceDescriptor newDataSource = dataSourceRegistry.createDataSource(dataSourceTemplate); ServletApplication app = ServletAppUtils.getServletApplication(); if (app instanceof WebApplication webApplication) { - ((DataSourceDescriptor) newDataSource).setNavigatorSettings( - webApplication.getAppConfiguration().getDefaultNavigatorSettings()); + newDataSource.setNavigatorSettings(webApplication.getAppConfiguration().getDefaultNavigatorSettings()); } + WebConnectionConfig config = project.getConnectionConfigInput(connectionConfig); + // Copy props from config if (!CommonUtils.isEmpty(config.getName())) { newDataSource.setName(config.getName()); @@ -407,12 +486,84 @@ public class WebServiceCore implements DBWServiceCore { public WebConnectionInfo testConnection( @NotNull WebSession webSession, @Nullable String projectId, - @NotNull WebConnectionConfig connectionConfig + @NotNull Map connectionConfig ) throws DBWebException { - DataSourceDescriptor dataSourceDescriptor = WebAppUtils.getWebApplication().getConnectionController() - .prepareTestConnection(webSession, projectId, connectionConfig); - return WebAppUtils.getWebApplication().getConnectionController() - .testConnection(webSession, projectId, connectionConfig, dataSourceDescriptor); + WebSessionProjectImpl project = getProjectById(webSession, projectId); + WebConnectionConfig configInput = project.getConnectionConfigInput(connectionConfig); + + DataSourceDescriptor dataSource = (DataSourceDescriptor) WebDataSourceUtils.getLocalOrGlobalDataSource( + webSession, projectId, configInput.getConnectionId()); + + DataSourceDescriptor testDataSource; + if (dataSource != null) { + try { + // Check that creds are saved to trigger secrets resolve + dataSource.isCredentialsSaved(); + } catch (DBException e) { + throw new DBWebException("Can't determine whether datasource credentials are saved", e); + } + + testDataSource = (DataSourceDescriptor) dataSource.createCopy(dataSource.getRegistry()); + WebDataSourceUtils.setConnectionConfiguration( + testDataSource.getDriver(), + testDataSource.getConnectionConfiguration(), + configInput + ); + if (configInput.getSelectedSecretId() != null) { + try { + dataSource.listSharedCredentials() + .stream() + .filter(secret -> configInput.getSelectedSecretId().equals(secret.getSubjectId())) + .findFirst() + .ifPresent(testDataSource::setSelectedSharedCredentials); + + } catch (DBException e) { + throw new DBWebException("Failed to load secret value: " + configInput.getSelectedSecretId()); + } + } + WebDataSourceUtils.saveAuthProperties( + testDataSource, + testDataSource.getConnectionConfiguration(), + configInput.getCredentials(), + true, + false, + true + ); + } else { + testDataSource = project.getDataSourceContainerFromInput(connectionConfig); + } + validateDriverLibrariesPresence(testDataSource); + webSession.provideAuthParameters( + webSession.getProgressMonitor(), + testDataSource, + testDataSource.getConnectionConfiguration() + ); + testDataSource.setSavePassword(true); // We need for test to avoid password callback + testDataSource.setAccessCheckRequired(!webSession.hasPermission(DBWConstants.PERMISSION_ADMIN)); + try { + ConnectionTestJob ct = new ConnectionTestJob( + testDataSource, param -> { + } + ); + ct.run(webSession.getProgressMonitor()); + if (ct.getConnectError() != null) { + if (ct.getConnectError() instanceof DBCConnectException error) { + Throwable rootCause = CommonUtils.getRootCause(error); + if (rootCause instanceof ClassNotFoundException) { + throwDriverNotFoundException(testDataSource); + } + } + throw new DBWebException("Connection failed", ct.getConnectError()); + } + WebConnectionInfo connectionInfo = project.createConnectionInfo(testDataSource); + connectionInfo.setConnectError(ct.getConnectError()); + connectionInfo.setServerVersion(ct.getServerVersion()); + connectionInfo.setClientVersion(ct.getClientVersion()); + connectionInfo.setConnectTime(RuntimeUtils.formatExecutionTime(ct.getConnectTime())); + return connectionInfo; + } catch (DBException e) { + throw new DBWebException("Error connecting to database", e); + } } @Override @@ -471,42 +622,15 @@ public class WebServiceCore implements DBWServiceCore { @Override public WebConnectionInfo closeConnection( - @NotNull WebSession webSession, @Nullable String projectId, @NotNull String connectionId - ) throws DBWebException { - return closeAndDeleteConnection(webSession, projectId, connectionId, false); - } - - @NotNull - private WebConnectionInfo closeAndDeleteConnection( @NotNull WebSession webSession, - @NotNull String projectId, - @NotNull String connectionId, - boolean forceDelete + @Nullable String projectId, + @NotNull String connectionId ) throws DBWebException { WebSessionProjectImpl project = getProjectById(webSession, projectId); WebConnectionInfo connectionInfo = project.getWebConnectionInfo(connectionId); DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer(); boolean disconnected = WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer); - if (forceDelete) { - DBPDataSourceRegistry registry = project.getDataSourceRegistry(); - registry.removeDataSource(dataSourceContainer); - try { - registry.checkForErrors(); - } catch (DBException e) { - try { - registry.addDataSource(dataSourceContainer); - } catch (DBException ex) { - log.error("Error re-adding after delete attempt", e); - } - throw new DBWebException("Failed to delete connection", e); - } - project.removeConnection(dataSourceContainer); - } else { - // Just reset saved credentials - connectionInfo.clearCache(); - } - return connectionInfo; } @@ -611,11 +735,24 @@ public class WebServiceCore implements DBWServiceCore { return new WebGroupPropertiesInfo<>(webSession, ProductSettingsRegistry.getInstance().getSettings()); } - private WebSessionProjectImpl getProjectById(WebSession webSession, String projectId) throws DBWebException { + @NotNull + private WebSessionProjectImpl getProjectById(@NotNull WebSession webSession, @Nullable String projectId) throws DBWebException { WebSessionProjectImpl project = webSession.getProjectById(projectId); if (project == null) { throw new DBWebException("Project '" + projectId + "' not found"); } return project; } + + private void validateDriverLibrariesPresence(@NotNull DBPDataSourceContainer container) throws DBWebException { + if (!DBWorkbench.isDistributed() && container.getDriver().getDriverLoader(container).needsExternalDependencies()) { + throwDriverNotFoundException(container); + } + } + + @NotNull + private static String throwDriverNotFoundException(@NotNull DBPDataSourceContainer container) throws DBWebException { + throw new DBWebException("Driver files for %s are not found. Please ask the administrator to download it." + .formatted(container.getDriver().getName())); + } } diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java index 26830b6d2a..33d311e7fb 100644 --- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java +++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java @@ -35,6 +35,7 @@ import io.cloudbeaver.service.DBWServiceServerConfigurator; import io.cloudbeaver.service.admin.*; import io.cloudbeaver.service.security.SMUtils; import io.cloudbeaver.utils.ServletAppUtils; +import io.cloudbeaver.utils.WebDataSourceUtils; import jakarta.servlet.http.HttpServletRequest; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; @@ -701,7 +702,7 @@ public class WebServiceAdmin implements DBWServiceAdmin { // driver is removed from disabled list // we need to enable if it is embedded try { - DBPDriver driver = WebServiceUtils.getDriverById(driverId); + DBPDriver driver = WebDataSourceUtils.getDriverById(driverId); if (driver.isEmbedded()) { enabledIds.add(driverId); }