diff --git a/README.md b/README.md index a656ab0327..be604f1cdb 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,10 @@ You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io ## Changelog +### 24.2.5. 2024-11-18 +- Updated user storage mechanism: New user logins are now stored in lowercase to prevent duplicate entries (e.g., "ADMIN" and "admin"). Note: This update does not affect previously created user logins; +- A new setting in Global Preferences was added to restrict data import for non-admin users. + ### 24.2.4. 2024-11-04 - General: - Data export: Added the ability to export JSON values as embedded JSON; diff --git a/config/core/cloudbeaver.conf b/config/core/cloudbeaver.conf index 8f6ec86e77..9ebad0078f 100644 --- a/config/core/cloudbeaver.conf +++ b/config/core/cloudbeaver.conf @@ -1,6 +1,6 @@ { server: { - serverPort: "${CLOUDBEAVER_SERVICE_PORT:8978}", + serverPort: "${CLOUDBEAVER_WEB_SERVER_PORT:8978}", workspaceLocation: "${CLOUDBEAVER_WORKSPACE_LOCATION:workspace}", contentRoot: "web", diff --git a/osgi-app.properties b/osgi-app.properties index 423b98ad5f..1681d2bbb2 100644 --- a/osgi-app.properties +++ b/osgi-app.properties @@ -30,4 +30,6 @@ testBundlePaths=\ additionalModuleRoots=\ opt; optionalFeatureRepositories=\ - dbeaver/product/repositories \ No newline at end of file + dbeaver/product/repositories +excludeOutputs=\ + cloudbeaver/webapp/node_modules \ No newline at end of file diff --git a/server/bundles/io.cloudbeaver.model/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.model/META-INF/MANIFEST.MF index 66edf9bbb2..5f998bc37e 100644 --- a/server/bundles/io.cloudbeaver.model/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.model/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: DBeaver Corp Bundle-Name: Cloudbeaver Web Model Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true -Bundle-Version: 1.0.65.qualifier -Bundle-Release-Date: 20241118 +Bundle-Version: 1.0.66.qualifier +Bundle-Release-Date: 20241202 Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.model/pom.xml b/server/bundles/io.cloudbeaver.model/pom.xml index 0832142631..cd0f7854f8 100644 --- a/server/bundles/io.cloudbeaver.model/pom.xml +++ b/server/bundles/io.cloudbeaver.model/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.model - 1.0.65-SNAPSHOT + 1.0.66-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/DBWebException.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/DBWebException.java index f4cc8e50ea..a06626ed51 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/DBWebException.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/DBWebException.java @@ -22,7 +22,6 @@ import graphql.language.SourceLocation; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.model.sql.SQLState; -import org.jkiss.dbeaver.utils.GeneralUtils; import org.jkiss.utils.CommonUtils; import java.io.PrintWriter; @@ -100,7 +99,7 @@ public ErrorClassification getErrorType() { @Override public Map getExtensions() { StringWriter buf = new StringWriter(); - GeneralUtils.getRootCause(this).printStackTrace(new PrintWriter(buf, true)); + CommonUtils.getRootCause(this).printStackTrace(new PrintWriter(buf, true)); Map extensions = new LinkedHashMap<>(); String stString = buf.toString(); 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 85f9d249d3..c41c22e7fb 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 @@ -42,6 +42,8 @@ import org.jkiss.dbeaver.model.rm.RMConstants; import org.jkiss.dbeaver.model.rm.RMProjectPermission; import org.jkiss.dbeaver.model.runtime.DBRRunnableParametrized; +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; @@ -356,8 +358,16 @@ public WebPropertyInfo[] getAuthProperties() { @Property public List getNetworkHandlersConfig() { - return dataSourceContainer.getConnectionConfiguration().getHandlers().stream() - .map(WebNetworkHandlerConfig::new).collect(Collectors.toList()); + var registry = NetworkHandlerRegistry.getInstance(); + return dataSourceContainer.getConnectionConfiguration() + .getHandlers() + .stream() + .filter(handlerConf -> { + NetworkHandlerDescriptor descriptor = registry.getDescriptor(handlerConf.getId()); + return descriptor != null && !descriptor.isDesktopHandler(); + }) + .map(WebNetworkHandlerConfig::new) + .collect(Collectors.toList()); } @Property diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java index 16822ec600..78a87c8fcb 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java @@ -55,6 +55,7 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor { private final boolean trusted; private final boolean isPrivate; private final boolean isAuthHidden; + private final boolean isCaseInsensitive; private final String[] requiredFeatures; private final boolean isRequired; private final String[] types; @@ -69,6 +70,7 @@ public WebAuthProviderDescriptor(IConfigurationElement cfg) { this.isPrivate = CommonUtils.toBoolean(cfg.getAttribute("private")); this.isRequired = CommonUtils.toBoolean(cfg.getAttribute("required")); this.isAuthHidden = CommonUtils.toBoolean(cfg.getAttribute("authHidden")); + this.isCaseInsensitive = CommonUtils.toBoolean(cfg.getAttribute("caseInsensitive")); for (IConfigurationElement cfgElement : cfg.getChildren("configuration")) { List properties = WebAuthProviderRegistry.readProperties(cfgElement); @@ -132,6 +134,10 @@ public boolean isAuthHidden() { return isAuthHidden; } + public boolean isCaseInsensitive() { + return isCaseInsensitive; + } + public List getConfigurationParameters() { return new ArrayList<>(configurationParameters.values()); } diff --git a/server/bundles/io.cloudbeaver.product.ce/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.product.ce/META-INF/MANIFEST.MF index eab0fb4e5a..3cc02705d6 100644 --- a/server/bundles/io.cloudbeaver.product.ce/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.product.ce/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: DBeaver Corp Bundle-Name: Cloudbeaver Community Product Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true -Bundle-Version: 24.2.5.qualifier -Bundle-Release-Date: 20241118 +Bundle-Version: 24.3.0.qualifier +Bundle-Release-Date: 20241202 Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.product.ce/pom.xml b/server/bundles/io.cloudbeaver.product.ce/pom.xml index 2f72ef816c..19029a80e4 100644 --- a/server/bundles/io.cloudbeaver.product.ce/pom.xml +++ b/server/bundles/io.cloudbeaver.product.ce/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.product.ce - 24.2.5-SNAPSHOT + 24.3.0-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.resources.drivers.base/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.resources.drivers.base/META-INF/MANIFEST.MF index d543d9254a..31f399616b 100644 --- a/server/bundles/io.cloudbeaver.resources.drivers.base/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.resources.drivers.base/META-INF/MANIFEST.MF @@ -2,8 +2,8 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Base JDBC drivers Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true -Bundle-Version: 1.0.110.qualifier -Bundle-Release-Date: 20241118 +Bundle-Version: 1.0.111.qualifier +Bundle-Release-Date: 20241202 Bundle-Vendor: DBeaver Corp Bundle-ActivationPolicy: lazy Automatic-Module-Name: io.cloudbeaver.resources.drivers.base diff --git a/server/bundles/io.cloudbeaver.resources.drivers.base/pom.xml b/server/bundles/io.cloudbeaver.resources.drivers.base/pom.xml index da070a045f..d327cd569d 100644 --- a/server/bundles/io.cloudbeaver.resources.drivers.base/pom.xml +++ b/server/bundles/io.cloudbeaver.resources.drivers.base/pom.xml @@ -9,6 +9,6 @@ ../ io.cloudbeaver.resources.drivers.base - 1.0.110-SNAPSHOT + 1.0.111-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF index 609e4ccc38..76578bc27f 100644 --- a/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: DBeaver Corp Bundle-Name: Cloudbeaver Web Server Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true -Bundle-Version: 24.2.5.qualifier -Bundle-Release-Date: 20241118 +Bundle-Version: 24.3.0.qualifier +Bundle-Release-Date: 20241202 Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator diff --git a/server/bundles/io.cloudbeaver.server/pom.xml b/server/bundles/io.cloudbeaver.server/pom.xml index c1df15d32f..e674aef409 100644 --- a/server/bundles/io.cloudbeaver.server/pom.xml +++ b/server/bundles/io.cloudbeaver.server/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.server - 24.2.5-SNAPSHOT + 24.3.0-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls index db942decd4..a1d12c3dd5 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls @@ -86,6 +86,7 @@ type SQLResultColumn { precision: Int required: Boolean! + autoGenerated: Boolean! readOnly: Boolean! readOnlyStatus: String diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java index 18fde20361..de0f13c5cc 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java @@ -529,7 +529,7 @@ public synchronized void finishConfiguration( } if (isConfigurationMode()) { - finishSecurityServiceConfiguration(adminName, adminPassword, authInfoList); + finishSecurityServiceConfiguration(adminName.toLowerCase(), adminPassword, authInfoList); } // Save runtime configuration diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBAbstractWebSocket.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBAbstractWebSocket.java index 2dac868a86..7af0f380c4 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBAbstractWebSocket.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBAbstractWebSocket.java @@ -25,7 +25,7 @@ public class CBAbstractWebSocket extends Session.Listener.AbstractAutoDemanding { private static final Log log = Log.getLog(CBAbstractWebSocket.class); - protected static final Gson gson = WSUtils.gson; + protected static final Gson gson = WSUtils.clientGson; public void handleEvent(WSEvent event) { if (!isOpen()) { 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 bb9617728e..d2751c20e7 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 @@ -99,6 +99,7 @@ public List getAuthModels(@NotNull WebSession webSession) @Override public List getNetworkHandlers(@NotNull WebSession webSession) { return NetworkHandlerRegistry.getInstance().getDescriptors().stream() + .filter(d -> !d.isDesktopHandler()) .map(d -> new WebNetworkHandlerDescriptor(webSession, d)).collect(Collectors.toList()); } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryResultColumn.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryResultColumn.java index 9dfdc218c8..1f03d70e0b 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryResultColumn.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryResultColumn.java @@ -105,6 +105,11 @@ public boolean isRequired() { return attrMeta.isRequired(); } + @Property + public boolean isAutoGenerated() { + return attrMeta.isAutoGenerated(); + } + @Property public boolean isReadOnly() { return DBExecUtils.isAttributeReadOnly(attrMeta); diff --git a/server/bundles/io.cloudbeaver.service.admin/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.service.admin/META-INF/MANIFEST.MF index e1bf687cda..f753b33cdc 100644 --- a/server/bundles/io.cloudbeaver.service.admin/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.service.admin/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: DBeaver Corp Bundle-Name: Cloudbeaver Web Service - Administration Bundle-SymbolicName: io.cloudbeaver.service.admin;singleton:=true -Bundle-Version: 1.0.109.qualifier -Bundle-Release-Date: 20241118 +Bundle-Version: 1.0.110.qualifier +Bundle-Release-Date: 20241202 Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.service.admin/pom.xml b/server/bundles/io.cloudbeaver.service.admin/pom.xml index 7b69a7997d..71acee8ddf 100644 --- a/server/bundles/io.cloudbeaver.service.admin/pom.xml +++ b/server/bundles/io.cloudbeaver.service.admin/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.service.admin - 1.0.109-SNAPSHOT + 1.0.110-SNAPSHOT eclipse-plugin 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 48e78992a3..51c959e209 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 @@ -161,12 +161,13 @@ public AdminUserInfo createUser( if (userName.isEmpty()) { throw new DBWebException("Empty user name"); } - webSession.addInfoMessage("Create new user - " + userName); + String userId = userName.toLowerCase(); + webSession.addInfoMessage("Create new user - " + userId); try { var securityController = webSession.getAdminSecurityController(); - securityController.createUser(userName, Map.of(), enabled, authRole); - var smUser = securityController.getUserById(userName); + securityController.createUser(userId, Map.of(), enabled, authRole); + var smUser = securityController.getUserById(userId); return new AdminUserInfo(webSession, new WebUser(smUser)); } catch (Exception e) { throw new DBWebException("Error creating new user", e); diff --git a/server/bundles/io.cloudbeaver.service.auth/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.service.auth/META-INF/MANIFEST.MF index 62fff52e87..f54eb321f8 100644 --- a/server/bundles/io.cloudbeaver.service.auth/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.service.auth/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: DBeaver Corp Bundle-Name: Cloudbeaver Web Service - Authentication Bundle-SymbolicName: io.cloudbeaver.service.auth;singleton:=true -Bundle-Version: 1.0.109.qualifier -Bundle-Release-Date: 20241118 +Bundle-Version: 1.0.110.qualifier +Bundle-Release-Date: 20241202 Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.service.auth/pom.xml b/server/bundles/io.cloudbeaver.service.auth/pom.xml index 943eb24778..972be7be9e 100644 --- a/server/bundles/io.cloudbeaver.service.auth/pom.xml +++ b/server/bundles/io.cloudbeaver.service.auth/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.service.auth - 1.0.109-SNAPSHOT + 1.0.110-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.service.data.transfer/META-INF/MANIFEST.MF index 4a317c4f3f..7c899f7153 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.service.data.transfer/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: DBeaver Corp Bundle-Name: Cloudbeaver Web Service - Data Transfer Bundle-SymbolicName: io.cloudbeaver.service.data.transfer;singleton:=true -Bundle-Version: 1.0.110.qualifier -Bundle-Release-Date: 20241118 +Bundle-Version: 1.0.111.qualifier +Bundle-Release-Date: 20241202 Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/pom.xml b/server/bundles/io.cloudbeaver.service.data.transfer/pom.xml index ed68b1bc06..716f6f3734 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/pom.xml +++ b/server/bundles/io.cloudbeaver.service.data.transfer/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.service.data.transfer - 1.0.110-SNAPSHOT + 1.0.111-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.service.fs/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.service.fs/META-INF/MANIFEST.MF index 12b77ec447..4c9a9b339a 100644 --- a/server/bundles/io.cloudbeaver.service.fs/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.service.fs/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: DBeaver Corp Bundle-Name: Cloudbeaver Web Service - File System Bundle-SymbolicName: io.cloudbeaver.service.fs;singleton:=true -Bundle-Version: 1.0.27.qualifier -Bundle-Release-Date: 20241118 +Bundle-Version: 1.0.28.qualifier +Bundle-Release-Date: 20241202 Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.service.fs/pom.xml b/server/bundles/io.cloudbeaver.service.fs/pom.xml index 016ae1e163..c1ae61238b 100644 --- a/server/bundles/io.cloudbeaver.service.fs/pom.xml +++ b/server/bundles/io.cloudbeaver.service.fs/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.service.fs - 1.0.27-SNAPSHOT + 1.0.28-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.service.fs/src/io/cloudbeaver/service/fs/model/WebFSServlet.java b/server/bundles/io.cloudbeaver.service.fs/src/io/cloudbeaver/service/fs/model/WebFSServlet.java index fa2514c98f..9a39ba821d 100644 --- a/server/bundles/io.cloudbeaver.service.fs/src/io/cloudbeaver/service/fs/model/WebFSServlet.java +++ b/server/bundles/io.cloudbeaver.service.fs/src/io/cloudbeaver/service/fs/model/WebFSServlet.java @@ -31,7 +31,6 @@ import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.model.data.json.JSONUtils; import org.jkiss.dbeaver.model.navigator.fs.DBNPathBase; -import org.jkiss.dbeaver.utils.GeneralUtils; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.IOUtils; @@ -100,7 +99,7 @@ private void doPost(WebSession session, HttpServletRequest request, HttpServletR } } catch (Exception e) { throw new DBWebException("File Upload Failed: Unable to Save File to the File System", - GeneralUtils.getRootCause(e)); + CommonUtils.getRootCause(e)); } } } diff --git a/server/bundles/io.cloudbeaver.service.metadata/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.service.metadata/META-INF/MANIFEST.MF index 6ba80756b2..5d88fe8e2d 100644 --- a/server/bundles/io.cloudbeaver.service.metadata/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.service.metadata/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: DBeaver Corp Bundle-Name: Cloudbeaver Web Service - Metadata Bundle-SymbolicName: io.cloudbeaver.service.metadata;singleton:=true -Bundle-Version: 1.0.113.qualifier -Bundle-Release-Date: 20241118 +Bundle-Version: 1.0.114.qualifier +Bundle-Release-Date: 20241202 Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.service.metadata/pom.xml b/server/bundles/io.cloudbeaver.service.metadata/pom.xml index 0a20df5524..1e1e2f3d33 100644 --- a/server/bundles/io.cloudbeaver.service.metadata/pom.xml +++ b/server/bundles/io.cloudbeaver.service.metadata/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.service.metadata - 1.0.113-SNAPSHOT + 1.0.114-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.service.rm.nio/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.service.rm.nio/META-INF/MANIFEST.MF index dbc5f5abb9..60f77cdf24 100644 --- a/server/bundles/io.cloudbeaver.service.rm.nio/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.service.rm.nio/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: DBeaver Corp Bundle-Name: Cloudbeaver Resource manager NIO implementation Bundle-SymbolicName: io.cloudbeaver.service.rm.nio;singleton:=true -Bundle-Version: 1.0.27.qualifier -Bundle-Release-Date: 20241118 +Bundle-Version: 1.0.28.qualifier +Bundle-Release-Date: 20241202 Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.service.rm.nio/pom.xml b/server/bundles/io.cloudbeaver.service.rm.nio/pom.xml index 97b4a6b2da..6457bd6f1e 100644 --- a/server/bundles/io.cloudbeaver.service.rm.nio/pom.xml +++ b/server/bundles/io.cloudbeaver.service.rm.nio/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.service.rm.nio - 1.0.27-SNAPSHOT + 1.0.28-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.service.rm/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.service.rm/META-INF/MANIFEST.MF index c2c7843837..8fc6de2a28 100644 --- a/server/bundles/io.cloudbeaver.service.rm/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.service.rm/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: DBeaver Corp Bundle-Name: Cloudbeaver Web Service - Resource manager Bundle-SymbolicName: io.cloudbeaver.service.rm;singleton:=true -Bundle-Version: 1.0.62.qualifier -Bundle-Release-Date: 20241118 +Bundle-Version: 1.0.63.qualifier +Bundle-Release-Date: 20241202 Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.service.rm/pom.xml b/server/bundles/io.cloudbeaver.service.rm/pom.xml index 4ccace9c42..24653a9748 100644 --- a/server/bundles/io.cloudbeaver.service.rm/pom.xml +++ b/server/bundles/io.cloudbeaver.service.rm/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.service.rm - 1.0.62-SNAPSHOT + 1.0.63-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.service.security/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.service.security/META-INF/MANIFEST.MF index db16db1b1b..374682a3a2 100644 --- a/server/bundles/io.cloudbeaver.service.security/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.service.security/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Name: Cloudbeaver Web Service - Security Bundle-Vendor: DBeaver Corp Bundle-SymbolicName: io.cloudbeaver.service.security;singleton:=true -Bundle-Version: 1.0.65.qualifier -Bundle-Release-Date: 20241118 +Bundle-Version: 1.0.66.qualifier +Bundle-Release-Date: 20241202 Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.service.security/plugin.xml b/server/bundles/io.cloudbeaver.service.security/plugin.xml index e9ef310594..e4b77aab29 100644 --- a/server/bundles/io.cloudbeaver.service.security/plugin.xml +++ b/server/bundles/io.cloudbeaver.service.security/plugin.xml @@ -4,6 +4,7 @@ diff --git a/server/bundles/io.cloudbeaver.service.security/pom.xml b/server/bundles/io.cloudbeaver.service.security/pom.xml index 18a205d5e0..b4567ab6b5 100644 --- a/server/bundles/io.cloudbeaver.service.security/pom.xml +++ b/server/bundles/io.cloudbeaver.service.security/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.service.security - 1.0.65-SNAPSHOT + 1.0.66-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/local/LocalAuthProvider.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/local/LocalAuthProvider.java index fce0269e82..60cf8809cc 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/local/LocalAuthProvider.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/auth/provider/local/LocalAuthProvider.java @@ -68,7 +68,9 @@ public String validateLocalAuth(@NotNull DBRProgressMonitor monitor, throw new DBException("No user password provided"); } String clientPasswordHash = AuthPropertyEncryption.hash.encrypt(userName, clientPassword); - if (!storedPasswordHash.equals(clientPasswordHash)) { + // we also need to check a hash with lower case (CB-5833) + String clientPasswordHashLowerCase = AuthPropertyEncryption.hash.encrypt(userName.toLowerCase(), clientPassword); + if (!storedPasswordHash.equals(clientPasswordHash) && !clientPasswordHashLowerCase.equals(storedPasswordHash)) { throw new DBException("Invalid user name or password"); } diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java index 3827bfa13a..68ba34ae9f 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java @@ -116,6 +116,9 @@ private boolean isSubjectExists(String subjectId) throws DBCException { /////////////////////////////////////////// // Users + /** + * Creates user. Saves user id in database in lower-case. + */ @Override public void createUser( @NotNull String userId, @@ -126,6 +129,7 @@ public void createUser( if (CommonUtils.isEmpty(userId)) { throw new DBCException("Empty user name is not allowed"); } + userId = userId.toLowerCase(); // creating new users only with lowercase if (isSubjectExists(userId)) { throw new DBCException("User or team '" + userId + "' already exists"); } @@ -140,6 +144,9 @@ public void createUser( } } + /** + * Creates user. Saves user id in database as it is. + */ public void createUser( @NotNull Connection dbCon, @NotNull String userId, @@ -844,8 +851,13 @@ public void setUserCredentials( } List transformedCredentials; WebAuthProviderDescriptor authProvider = getAuthProvider(authProviderId); + if (authProvider.isCaseInsensitive() && !isSubjectExists(userId) && isSubjectExists(userId.toLowerCase())) { + log.warn("User with id '" + userId + "' not found, credentials will be set for the user: " + userId.toLowerCase()); + userId = userId.toLowerCase(); + } try { SMAuthCredentialsProfile credProfile = getCredentialProfileByParameters(authProvider, credentials.keySet()); + String finalUserId = userId; transformedCredentials = credentials.entrySet().stream().map(cred -> { String propertyName = cred.getKey(); AuthPropertyDescriptor property = credProfile.getCredentialParameter(propertyName); @@ -853,7 +865,7 @@ public void setUserCredentials( return null; } String encodedValue = CommonUtils.toString(cred.getValue()); - encodedValue = property.getEncryption().encrypt(userId, encodedValue); + encodedValue = property.getEncryption().encrypt(finalUserId, encodedValue); return new String[]{propertyName, encodedValue}; }).toList(); } catch (Exception e) { @@ -910,20 +922,38 @@ private String findUserByCredentials( @NotNull WebAuthProviderDescriptor authProvider, @NotNull Map authParameters, boolean onlyActive // throws exception if user is inactive + ) throws DBException { + String userId = findUserByCredentials(authProvider, authParameters, onlyActive, false); + if (userId == null && authProvider.isCaseInsensitive()) { + // try to find user id with lower case is auth provider is case-insensitive + return findUserByCredentials(authProvider, authParameters, onlyActive, true); + } + return userId; + } + + @Nullable + private String findUserByCredentials( + @NotNull WebAuthProviderDescriptor authProvider, + @NotNull Map authParameters, + boolean onlyActive, + boolean isCaseInsensitive ) throws DBCException { - Map identCredentials = new LinkedHashMap<>(); + Map identCredentials = new LinkedHashMap<>(); String[] propNames = authParameters.keySet().toArray(new String[0]); for (AuthPropertyDescriptor prop : authProvider.getCredentialParameters(propNames)) { if (prop.isIdentifying()) { String propId = CommonUtils.toString(prop.getId()); - Object paramValue = authParameters.get(propId); - if (paramValue == null) { + if (authParameters.get(propId) == null) { throw new DBCException("Authentication parameter '" + prop.getId() + "' is missing"); } if (prop.getEncryption() == AuthPropertyEncryption.hash) { throw new DBCException("Hash encryption can't be used in identifying credentials"); } - identCredentials.put(propId, paramValue); + String paramValue = CommonUtils.toString(authParameters.get(propId)); + identCredentials.put( + propId, + isCaseInsensitive ? paramValue.toLowerCase() : paramValue + ); } } if (identCredentials.isEmpty()) { @@ -947,9 +977,9 @@ private String findUserByCredentials( try (PreparedStatement dbStat = dbCon.prepareStatement(database.normalizeTableNames(sql.toString()))) { dbStat.setString(1, authProvider.getId()); int param = 2; - for (Map.Entry credEntry : identCredentials.entrySet()) { + for (Map.Entry credEntry : identCredentials.entrySet()) { dbStat.setString(param++, credEntry.getKey()); - dbStat.setString(param++, CommonUtils.toString(credEntry.getValue())); + dbStat.setString(param++, credEntry.getValue()); } try (ResultSet dbResult = dbStat.executeQuery()) { @@ -980,6 +1010,15 @@ private String findUserByCredentials( @Override public Map getUserCredentials(String userId, String authProviderId) throws DBCException { WebAuthProviderDescriptor authProvider = getAuthProvider(authProviderId); + Map creds = getUserCredentials(authProvider, userId); + if (creds.isEmpty() && authProvider.isCaseInsensitive()) { + return getUserCredentials(authProvider, userId.toLowerCase()); + } + return creds; + } + + @NotNull + private Map getUserCredentials(WebAuthProviderDescriptor authProvider, String userId) throws DBCException { try (Connection dbCon = database.openConnection()) { try (PreparedStatement dbStat = dbCon.prepareStatement( database.normalizeTableNames("SELECT CRED_ID,CRED_VALUE FROM {table_prefix}CB_USER_CREDENTIALS\n" + @@ -990,7 +1029,6 @@ public Map getUserCredentials(String userId, String authProvider try (ResultSet dbResult = dbStat.executeQuery()) { Map credentials = new LinkedHashMap<>(); - while (dbResult.next()) { credentials.put(dbResult.getString(1), dbResult.getString(2)); } @@ -1182,6 +1220,7 @@ public void createTeam(String teamId, String name, String description, String gr if (CommonUtils.isEmpty(teamId)) { throw new DBCException("Empty team name is not allowed"); } + teamId = teamId.toLowerCase(); if (isSubjectExists(teamId)) { throw new DBCException("User or team '" + teamId + "' already exists"); } @@ -2427,13 +2466,20 @@ private String findOrCreateExternalUserByCredentials( return null; } - userId = userIdFromCredentials; + userId = authProvider.isCaseInsensitive() ? userIdFromCredentials.toLowerCase() : userIdFromCredentials; if (!isSubjectExists(userId)) { - createUser(userId, - Map.of(), - true, - resolveUserAuthRole(null, authRole) - ); + log.debug("Create user: " + userId); + try (Connection dbCon = database.openConnection()) { + createUser( + dbCon, + userId, + Map.of(), + true, + resolveUserAuthRole(null, authRole) + ); + } catch (SQLException e) { + throw new DBException("Error saving user in database", e); + } } setUserCredentials(userId, authProvider.getId(), userCredentials); } else if (userId == null) { diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabaseInitialData.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabaseInitialData.java index 17c42babb2..7f2879f4f6 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabaseInitialData.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabaseInitialData.java @@ -17,6 +17,7 @@ package io.cloudbeaver.service.security.db; import org.jkiss.dbeaver.model.security.user.SMTeam; +import org.jkiss.utils.CommonUtils; import java.util.List; @@ -26,7 +27,7 @@ class CBDatabaseInitialData { private List teams; public String getAdminName() { - return adminName; + return CommonUtils.isEmpty(adminName) ? null : adminName.toLowerCase(); } public String getAdminPassword() { diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/internal/ClearAuthAttemptInfoJob.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/internal/ClearAuthAttemptInfoJob.java index 9c700dcca4..8eb28b5721 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/internal/ClearAuthAttemptInfoJob.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/internal/ClearAuthAttemptInfoJob.java @@ -23,7 +23,7 @@ import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.runtime.AbstractJob; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; -import org.jkiss.dbeaver.utils.GeneralUtils; +import org.jkiss.utils.CommonUtils; public class ClearAuthAttemptInfoJob extends AbstractJob { @@ -45,7 +45,7 @@ protected IStatus run(DBRProgressMonitor monitor) { securityController.clearOldAuthAttemptInfo(); schedule(CHECK_PERIOD); } catch (DBException e) { - log.error("Error to clear the auth attempt info: " + GeneralUtils.getRootCause(e).getMessage()); + log.error("Error to clear the auth attempt info: " + CommonUtils.getRootCause(e).getMessage()); // Check failed. Re-schedule after 5 seconds schedule(RETRY_PERIOD); } diff --git a/server/bundles/io.cloudbeaver.slf4j/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.slf4j/META-INF/MANIFEST.MF index 00ad68d5d8..7e877266d2 100644 --- a/server/bundles/io.cloudbeaver.slf4j/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.slf4j/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: DBeaver Corp Bundle-Name: CloudBeaver SLF4j Binding Bundle-SymbolicName: io.cloudbeaver.slf4j;singleton:=true -Bundle-Version: 1.0.25.qualifier -Bundle-Release-Date: 20241118 +Bundle-Version: 1.0.26.qualifier +Bundle-Release-Date: 20241202 Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.slf4j/pom.xml b/server/bundles/io.cloudbeaver.slf4j/pom.xml index 96b8d8ab41..71d70e2ebd 100644 --- a/server/bundles/io.cloudbeaver.slf4j/pom.xml +++ b/server/bundles/io.cloudbeaver.slf4j/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.slf4j - 1.0.25-SNAPSHOT + 1.0.26-SNAPSHOT eclipse-plugin diff --git a/server/features/io.cloudbeaver.ce.drivers.feature/feature.xml b/server/features/io.cloudbeaver.ce.drivers.feature/feature.xml index ec65c2a21a..16abcc7a31 100644 --- a/server/features/io.cloudbeaver.ce.drivers.feature/feature.xml +++ b/server/features/io.cloudbeaver.ce.drivers.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/server/features/io.cloudbeaver.ce.drivers.feature/pom.xml b/server/features/io.cloudbeaver.ce.drivers.feature/pom.xml index 6f1c5d6ca5..2b38e45be3 100644 --- a/server/features/io.cloudbeaver.ce.drivers.feature/pom.xml +++ b/server/features/io.cloudbeaver.ce.drivers.feature/pom.xml @@ -9,6 +9,6 @@ ../ io.cloudbeaver.ce.drivers.feature - 1.0.133-SNAPSHOT + 1.0.134-SNAPSHOT eclipse-feature diff --git a/server/features/io.cloudbeaver.product.ce.feature/feature.xml b/server/features/io.cloudbeaver.product.ce.feature/feature.xml index 83817d2299..3c40e2869a 100644 --- a/server/features/io.cloudbeaver.product.ce.feature/feature.xml +++ b/server/features/io.cloudbeaver.product.ce.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/server/features/io.cloudbeaver.product.ce.feature/pom.xml b/server/features/io.cloudbeaver.product.ce.feature/pom.xml index 6e938928d4..5046d6a67a 100644 --- a/server/features/io.cloudbeaver.product.ce.feature/pom.xml +++ b/server/features/io.cloudbeaver.product.ce.feature/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.product.ce.feature - 24.2.5-SNAPSHOT + 24.3.0-SNAPSHOT eclipse-feature diff --git a/server/features/io.cloudbeaver.server.feature/feature.xml b/server/features/io.cloudbeaver.server.feature/feature.xml index 9ed477595f..628631759f 100644 --- a/server/features/io.cloudbeaver.server.feature/feature.xml +++ b/server/features/io.cloudbeaver.server.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/server/features/io.cloudbeaver.server.feature/pom.xml b/server/features/io.cloudbeaver.server.feature/pom.xml index ca4d958756..cf2b93d852 100644 --- a/server/features/io.cloudbeaver.server.feature/pom.xml +++ b/server/features/io.cloudbeaver.server.feature/pom.xml @@ -10,6 +10,6 @@ ../ io.cloudbeaver.server.feature - 24.2.5-SNAPSHOT + 24.3.0-SNAPSHOT eclipse-feature diff --git a/server/features/io.cloudbeaver.ws.feature/feature.xml b/server/features/io.cloudbeaver.ws.feature/feature.xml index 63f31ce8cb..ccbee9242a 100644 --- a/server/features/io.cloudbeaver.ws.feature/feature.xml +++ b/server/features/io.cloudbeaver.ws.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/server/features/io.cloudbeaver.ws.feature/pom.xml b/server/features/io.cloudbeaver.ws.feature/pom.xml index 6ae86adae8..03d179e748 100644 --- a/server/features/io.cloudbeaver.ws.feature/pom.xml +++ b/server/features/io.cloudbeaver.ws.feature/pom.xml @@ -10,6 +10,6 @@ ../ io.cloudbeaver.ws.feature - 1.0.63-SNAPSHOT + 1.0.64-SNAPSHOT eclipse-feature diff --git a/server/pom.xml b/server/pom.xml index 43afd2610e..14cc87de26 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -19,7 +19,7 @@ CloudBeaver CE - 24.2.5 + 24.3.0 diff --git a/server/product/web-server/CloudbeaverServer.product b/server/product/web-server/CloudbeaverServer.product index 03517f8893..abc42cf0ce 100644 --- a/server/product/web-server/CloudbeaverServer.product +++ b/server/product/web-server/CloudbeaverServer.product @@ -2,7 +2,7 @@ diff --git a/server/product/web-server/pom.xml b/server/product/web-server/pom.xml index c508ad67b0..0218d3d2c9 100644 --- a/server/product/web-server/pom.xml +++ b/server/product/web-server/pom.xml @@ -9,7 +9,7 @@ 1.0.0-SNAPSHOT ../../ - 24.2.5-SNAPSHOT + 24.3.0-SNAPSHOT web-server eclipse-repository Cloudbeaver Server Product diff --git a/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/test/platform/AuthenticationTest.java b/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/test/platform/AuthenticationTest.java index d642b44e34..e2bebd4db8 100644 --- a/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/test/platform/AuthenticationTest.java +++ b/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/test/platform/AuthenticationTest.java @@ -16,15 +16,19 @@ */ package io.cloudbeaver.test.platform; +import io.cloudbeaver.auth.provider.local.LocalAuthProvider; import io.cloudbeaver.auth.provider.rp.RPAuthProvider; import io.cloudbeaver.test.WebGQLClient; +import org.jkiss.code.NotNull; import org.jkiss.dbeaver.model.auth.SMAuthStatus; import org.jkiss.dbeaver.model.data.json.JSONUtils; +import org.jkiss.utils.SecurityUtils; import org.junit.Assert; import org.junit.Test; import java.util.List; import java.util.Map; +import java.util.Set; public class AuthenticationTest { private static final String GQL_OPEN_SESSION = """ @@ -39,6 +43,12 @@ mutation openSession($defaultLocale: String) { userId } }"""; + private static final String GQL_AUTH_LOGOUT = """ + query authLogoutExtended($provider: ID, $configuration: ID) { + result: authLogoutExtended(provider: $provider, configuration: $configuration) { + redirectLinks + } + }"""; @Test public void testLoginUser() throws Exception { @@ -47,6 +57,34 @@ public void testLoginUser() throws Exception { Assert.assertEquals(SMAuthStatus.SUCCESS.name(), JSONUtils.getString(authInfo, "authStatus")); } + + @Test + public void testLoginUserWithCamelCase() throws Exception { + WebGQLClient client = CEServerTestSuite.createClient(); + for (String userId : Set.of("Test", "tESt", "tesT", "TEST")) { + Map credsWithCamelCase = getUserCredentials(userId); + // authenticating with user + Map authInfo = CEServerTestSuite.authenticateTestUser(client, credsWithCamelCase); + Assert.assertEquals(SMAuthStatus.SUCCESS.name(), JSONUtils.getString(authInfo, "authStatus")); + Map activeUser = client.sendQuery(GQL_ACTIVE_USER, null); + Assert.assertEquals(userId.toLowerCase(), JSONUtils.getString(activeUser, "userId")); + // making logout + client.sendQuery(GQL_AUTH_LOGOUT, Map.of("provider", "local")); + + activeUser = client.sendQuery(GQL_ACTIVE_USER, null); + Assert.assertNotEquals(userId.toLowerCase(), JSONUtils.getString(activeUser, "userId")); + } + } + + @NotNull + private Map getUserCredentials(@NotNull String userId) throws Exception { + return Map.of( + LocalAuthProvider.CRED_USER, userId, + LocalAuthProvider.CRED_PASSWORD, SecurityUtils.makeDigest("test") + ); + } + + @Test public void testReverseProxyAnonymousModeLogin() throws Exception { WebGQLClient client = CEServerTestSuite.createClient(); diff --git a/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/test/platform/CEServerTestSuite.java b/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/test/platform/CEServerTestSuite.java index 3ca615b5ea..b133f1baa5 100644 --- a/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/test/platform/CEServerTestSuite.java +++ b/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/test/platform/CEServerTestSuite.java @@ -101,11 +101,18 @@ public static WebGQLClient createClient(@NotNull HttpClient httpClient) { } public static Map authenticateTestUser(@NotNull WebGQLClient client) throws Exception { + return authenticateTestUser(client, TEST_CREDENTIALS); + } + + public static Map authenticateTestUser( + @NotNull WebGQLClient client, + @NotNull Map credentials + ) throws Exception { return client.sendQuery( WebGQLClient.GQL_AUTHENTICATE, Map.of( "provider", LocalAuthProvider.PROVIDER_ID, - "credentials", TEST_CREDENTIALS + "credentials", credentials ) ); } diff --git a/webapp/packages/core-blocks/src/FormControls/FormContext.ts b/webapp/packages/core-blocks/src/FormControls/FormContext.ts index a676cecee9..67b2fa5c05 100644 --- a/webapp/packages/core-blocks/src/FormControls/FormContext.ts +++ b/webapp/packages/core-blocks/src/FormControls/FormContext.ts @@ -11,7 +11,7 @@ import type { IExecutor, SyncExecutor } from '@cloudbeaver/core-executor'; export type FormChangeValues = string | number | boolean | FileList | null | undefined; export type FormChangeHandler = (value: FormChangeValues, name: string | undefined) => void; -type KeyHandler = (event: React.KeyboardEvent) => void; +type KeyHandler = (event: React.KeyboardEvent) => void; export interface IChangeData { value: FormChangeValues; diff --git a/webapp/packages/core-blocks/src/FormControls/Textarea.tsx b/webapp/packages/core-blocks/src/FormControls/Textarea.tsx index 929ddd47f5..68e8a71fe2 100644 --- a/webapp/packages/core-blocks/src/FormControls/Textarea.tsx +++ b/webapp/packages/core-blocks/src/FormControls/Textarea.tsx @@ -16,6 +16,7 @@ import type { ILayoutSizeProps } from '../Containers/ILayoutSizeProps.js'; import { useTranslate } from '../localization/useTranslate.js'; import { s } from '../s.js'; import { UploadArea } from '../UploadArea.js'; +import { useCombinedHandler } from '../useCombinedHandler.js'; import { useS } from '../useS.js'; import { Field } from './Field.js'; import { FieldDescription } from './FieldDescription.js'; @@ -63,6 +64,7 @@ export const Textarea: TextareaType = observer(function Textarea({ embedded, cursorInitiallyAtEnd, uploadable, + onKeyDown, onChange = () => {}, ...rest }: ControlledProps | ObjectProps) { @@ -72,6 +74,7 @@ export const Textarea: TextareaType = observer(function Textarea({ rest = filterLayoutFakeProps(rest); const styles = useS(textareaStyle); const context = useContext(FormContext); + const handleKeyDown = useCombinedHandler(onKeyDown, context?.keyDown); const handleChange = useCallback( (value: string) => { @@ -110,6 +113,7 @@ export const Textarea: TextareaType = observer(function Textarea({ value={value ?? ''} name={name} data-embedded={embedded} + onKeyDown={handleKeyDown} onChange={event => handleChange(event.target.value)} /> {description && {description}} diff --git a/webapp/packages/core-blocks/src/FormControls/useForm.ts b/webapp/packages/core-blocks/src/FormControls/useForm.ts index 50d67bb655..1ea664b631 100644 --- a/webapp/packages/core-blocks/src/FormControls/useForm.ts +++ b/webapp/packages/core-blocks/src/FormControls/useForm.ts @@ -78,10 +78,17 @@ export function useForm(options?: IOptions): IFormContext { this.onChange.execute({ value, name }); } }, - keyDown(event: React.KeyboardEvent) { + keyDown(event: React.KeyboardEvent) { + const isCtrlEnterSubmit = + event.key === 'Enter' && + (event.ctrlKey || event.metaKey) && + this.disableEnterSubmit === false && + event.target instanceof HTMLTextAreaElement; + const isEnterSubmit = event.key === 'Enter' && this.disableEnterSubmit === false && event.target instanceof HTMLInputElement; + if (this.parent) { this.parent.keyDown(event); - } else if (event.key === 'Enter' && this.disableEnterSubmit === false) { + } else if (isCtrlEnterSubmit || isEnterSubmit) { event.preventDefault(); if (this.ref) { this.ref?.requestSubmit(); diff --git a/webapp/packages/core-sdk/src/queries/grid/getSqlExecuteTaskResults.gql b/webapp/packages/core-sdk/src/queries/grid/getSqlExecuteTaskResults.gql index 9b1ef04431..d0838d2eee 100644 --- a/webapp/packages/core-sdk/src/queries/grid/getSqlExecuteTaskResults.gql +++ b/webapp/packages/core-sdk/src/queries/grid/getSqlExecuteTaskResults.gql @@ -22,6 +22,7 @@ mutation getSqlExecuteTaskResults($taskId: ID!) { position precision required + autoGenerated readOnly readOnlyStatus scale diff --git a/webapp/packages/core-utils/src/index.ts b/webapp/packages/core-utils/src/index.ts index 9344e0c195..e6da19c3d7 100644 --- a/webapp/packages/core-utils/src/index.ts +++ b/webapp/packages/core-utils/src/index.ts @@ -88,3 +88,4 @@ export * from './getProgressPercent.js'; export * from './types/UndefinedToNull.js'; export * from './bindFunctions.js'; export * from './getDomainFromUrl.js'; +export * from './isNumber.js'; diff --git a/webapp/packages/core-utils/src/isNumber.ts b/webapp/packages/core-utils/src/isNumber.ts new file mode 100644 index 0000000000..df54dd65df --- /dev/null +++ b/webapp/packages/core-utils/src/isNumber.ts @@ -0,0 +1,10 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +export function isNumber(value: any): boolean { + return !isNaN(parseFloat(value)) && isFinite(value); +} diff --git a/webapp/packages/plugin-administration/src/ConfigurationWizard/Finish/FinishPage.module.css b/webapp/packages/plugin-administration/src/ConfigurationWizard/Finish/FinishPage.module.css index b7372b3744..51ea02727a 100644 --- a/webapp/packages/plugin-administration/src/ConfigurationWizard/Finish/FinishPage.module.css +++ b/webapp/packages/plugin-administration/src/ConfigurationWizard/Finish/FinishPage.module.css @@ -27,5 +27,5 @@ .message { line-height: 2; - white-space: pre; + white-space: pre-wrap; } diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.module.css b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.module.css index 293ac777b5..fbccc496b0 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.module.css +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.module.css @@ -17,6 +17,10 @@ overflow: auto !important; } +.placeholder { + margin: 0 !important; +} + .loader { z-index: 2; } diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx index 496c6fbf3b..122bff1dac 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx @@ -55,9 +55,11 @@ export const GrantedUsers: TabContainerPanelComponent = observer if (isDefaultTeam) { return ( - - - {translate('plugin_authentication_administration_team_default_users_tooltip')} + + + + {translate('plugin_authentication_administration_team_default_users_tooltip')} + ); @@ -66,10 +68,12 @@ export const GrantedUsers: TabContainerPanelComponent = observer return ( {() => ( - + {!users.resource.values.length ? ( - - {translate('administration_teams_team_granted_users_empty')} + + + {translate('administration_teams_team_granted_users_empty')} + ) : ( <> diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/TeamsTable/TeamEdit.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/TeamsTable/TeamEdit.tsx index 23952f8c8a..3225b4f3d8 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/TeamsTable/TeamEdit.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/TeamsTable/TeamEdit.tsx @@ -31,7 +31,7 @@ export const TeamEdit = observer(function TeamEdit({ item }) { data.config.teamId = item; return ( - + diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/AdministrationUserForm.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/AdministrationUserForm.tsx index 89251c7f6e..19e56943b7 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/AdministrationUserForm.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/AdministrationUserForm.tsx @@ -77,7 +77,11 @@ export const AdministrationUserForm = observer(function AdministrationUse {editing && ( - + )}