getProjects() {
return accessibleProjects;
}
@Nullable
@Override
- public DBPProject getActiveProject() {
+ public WebSessionProjectImpl getActiveProject() {
return activeProject;
}
@Nullable
@Override
- public WebProjectImpl getProject(@NotNull String projectName) {
- for (WebProjectImpl project : accessibleProjects) {
+ public WebSessionProjectImpl getProject(@NotNull String projectName) {
+ for (WebSessionProjectImpl project : accessibleProjects) {
if (project.getName().equals(projectName)) {
return project;
}
@@ -104,11 +105,11 @@ public WebProjectImpl getProject(@NotNull String projectName) {
@Nullable
@Override
- public WebProjectImpl getProjectById(@NotNull String projectId) {
+ public WebSessionProjectImpl getProjectById(@NotNull String projectId) {
if (projectId == null) {
return activeProject;
}
- for (WebProjectImpl project : accessibleProjects) {
+ for (WebSessionProjectImpl project : accessibleProjects) {
if (project.getId().equals(projectId)) {
return project;
}
@@ -122,26 +123,36 @@ public SessionContextImpl getAuthContext() {
return workspaceAuthContext;
}
+ @Override
+ public void initializeProjects() {
+ // noop
+ }
+
@Override
public void dispose() {
clearProjects();
}
- public void setActiveProject(DBPProject activeProject) {
- this.activeProject = (WebProjectImpl) activeProject;
+ @Override
+ public DBPImage getResourceIcon(DBPAdaptable resourceAdapter) {
+ return null;
+ }
+
+ public void setActiveProject(WebSessionProjectImpl activeProject) {
+ this.activeProject = activeProject;
}
- void addProject(WebProjectImpl project) {
+ void addProject(WebSessionProjectImpl project) {
accessibleProjects.add(project);
}
- void removeProject(WebProjectImpl project) {
+ void removeProject(WebSessionProjectImpl project) {
accessibleProjects.remove(project);
}
void clearProjects() {
if (!this.accessibleProjects.isEmpty()) {
- for (WebProjectImpl project : accessibleProjects) {
+ for (WebSessionProjectImpl project : accessibleProjects) {
project.dispose();
}
this.activeProject = null;
diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebUserContext.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebUserContext.java
index 08d2b06eaf..f94462297b 100644
--- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebUserContext.java
+++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebUserContext.java
@@ -46,6 +46,7 @@
* Web user context.
* Contains user state and services based on available permissions
*/
+//TODO: split to authenticated and non authenticated context
public class WebUserContext implements SMCredentialsProvider {
private static final Log log = Log.getLog(WebUserContext.class);
diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java
index 864d765b41..322c1be6bd 100644
--- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java
+++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java
@@ -80,7 +80,7 @@ public String getSignInLink() throws DBException {
}
private String buildRedirectUrl(String baseUrl) {
- return baseUrl + "?" + CBAuthConstants.CB_REDIRECT_URL_REQUEST_PARAM + "=" + WebAppUtils.getWebApplication().getServerURL();
+ return baseUrl + "?" + CBAuthConstants.CB_REDIRECT_URL_REQUEST_PARAM + "=" + WebAppUtils.getFullServerUrl();
}
@Property
@@ -109,6 +109,14 @@ public String getAcsLink() throws DBException {
return instance instanceof SMAuthProviderFederated ? ((SMAuthProviderFederated) instance).getAcsLink(getId(), config.getParameters()) : null;
}
+ @Property
+ public String getEntityIdLink() throws DBException {
+ SMAuthProvider> instance = providerDescriptor.getInstance();
+ return instance instanceof SMAuthProviderFederated
+ ? ((SMAuthProviderFederated) instance).getEntityIdLink(getId(), config.getParameters())
+ : null;
+ }
+
@Override
public String toString() {
return getDisplayName();
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebDriverRegistry.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebDriverRegistry.java
similarity index 100%
rename from server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebDriverRegistry.java
rename to server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebDriverRegistry.java
diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/BaseGQLPlatform.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/BaseGQLPlatform.java
new file mode 100644
index 0000000000..a2ae8040a2
--- /dev/null
+++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/BaseGQLPlatform.java
@@ -0,0 +1,161 @@
+/*
+ * 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.server;
+
+import io.cloudbeaver.DBWConstants;
+import io.cloudbeaver.model.app.WebApplication;
+import org.eclipse.core.runtime.Plugin;
+import org.jkiss.code.NotNull;
+import org.jkiss.dbeaver.Log;
+import org.jkiss.dbeaver.model.DBConstants;
+import org.jkiss.dbeaver.model.app.DBACertificateStorage;
+import org.jkiss.dbeaver.model.app.DBPWorkspace;
+import org.jkiss.dbeaver.model.impl.app.DefaultCertificateStorage;
+import org.jkiss.dbeaver.model.qm.QMRegistry;
+import org.jkiss.dbeaver.model.qm.QMUtils;
+import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
+import org.jkiss.dbeaver.registry.BasePlatformImpl;
+import org.jkiss.dbeaver.registry.DataSourceProviderRegistry;
+import org.jkiss.dbeaver.runtime.SecurityProviderUtils;
+import org.jkiss.dbeaver.runtime.qm.QMLogFileWriter;
+import org.jkiss.dbeaver.runtime.qm.QMRegistryImpl;
+import org.jkiss.dbeaver.utils.ContentUtils;
+import org.jkiss.utils.CommonUtils;
+import org.jkiss.utils.StandardConstants;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public abstract class BaseGQLPlatform extends BasePlatformImpl {
+ private static final Log log = Log.getLog(BaseGQLPlatform.class);
+ public static final String BASE_TEMP_DIR = "dbeaver";
+
+ private Path tempFolder;
+
+ private QMRegistryImpl queryManager;
+ private QMLogFileWriter qmLogWriter;
+ private DBACertificateStorage certificateStorage;
+ private WebGlobalWorkspace workspace;
+
+ @Override
+ protected synchronized void initialize() {
+ // Register BC security provider
+ SecurityProviderUtils.registerSecurityProvider();
+
+ // Register properties adapter
+ this.workspace = new WebGlobalWorkspace(this, (WebApplication) getApplication());
+ this.workspace.initializeProjects();
+ QMUtils.initApplication(this);
+
+ this.queryManager = new QMRegistryImpl();
+
+ this.qmLogWriter = new QMLogFileWriter();
+ this.queryManager.registerMetaListener(qmLogWriter);
+
+ this.certificateStorage = new DefaultCertificateStorage(
+ WebPlatformActivator.getInstance()
+ .getStateLocation()
+ .toFile()
+ .toPath()
+ .resolve(DBConstants.CERTIFICATE_STORAGE_FOLDER));
+ super.initialize();
+ }
+
+ @Override
+ protected Plugin getProductPlugin() {
+ return WebPlatformActivator.getInstance();
+ }
+
+ @NotNull
+ @Override
+ public DBACertificateStorage getCertificateStorage() {
+ return certificateStorage;
+ }
+
+ @NotNull
+ @Override
+ public DBPWorkspace getWorkspace() {
+ return workspace;
+ }
+
+ @NotNull
+ public Path getTempFolder(@NotNull DBRProgressMonitor monitor, @NotNull String name) {
+
+ if (tempFolder == null) {
+ synchronized (this) {
+ if (tempFolder == null) {
+ initTempFolder(monitor);
+ }
+ }
+ }
+ Path folder = tempFolder.resolve(name);
+ if (!Files.exists(folder)) {
+ try {
+ Files.createDirectories(folder);
+ } catch (IOException e) {
+ log.error("Error creating temp folder '" + folder.toAbsolutePath() + "'", e);
+ }
+ }
+ return folder;
+ }
+
+ private void initTempFolder(@NotNull DBRProgressMonitor monitor) {
+ // Make temp folder
+ monitor.subTask("Create temp folder");
+ String sysTempFolder = System.getProperty(StandardConstants.ENV_TMP_DIR);
+ if (CommonUtils.isNotEmpty(sysTempFolder)) {
+ tempFolder = Path.of(sysTempFolder).resolve(BASE_TEMP_DIR).resolve(DBWConstants.WORK_DATA_FOLDER_NAME);
+ } else {
+ //we do not use workspace because it can be in external file system
+ tempFolder = getApplication().getHomeDirectory().resolve(DBWConstants.WORK_DATA_FOLDER_NAME);
+ }
+ }
+
+ @NotNull
+ public abstract WebApplication getApplication();
+
+ @Override
+ public synchronized void dispose() {
+ super.dispose();
+ if (this.qmLogWriter != null) {
+ this.queryManager.unregisterMetaListener(qmLogWriter);
+ this.qmLogWriter.dispose();
+ this.qmLogWriter = null;
+ }
+ if (this.queryManager != null) {
+ this.queryManager.dispose();
+ //queryManager = null;
+ }
+ DataSourceProviderRegistry.dispose();
+
+ // Remove temp folder
+ if (tempFolder != null) {
+
+ if (!ContentUtils.deleteFileRecursive(tempFolder.toFile())) {
+ log.warn("Can't delete temp folder '" + tempFolder.toAbsolutePath() + "'");
+ }
+ tempFolder = null;
+ }
+ }
+
+ @NotNull
+ public QMRegistry getQueryManager() {
+ return queryManager;
+ }
+
+}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBConstants.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/CBConstants.java
similarity index 97%
rename from server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBConstants.java
rename to server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/CBConstants.java
index f87ae5e1cd..0103994c19 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBConstants.java
+++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/CBConstants.java
@@ -87,4 +87,6 @@ public class CBConstants {
public static final String QUOTA_PROP_FILE_LIMIT = "dataExportFileSizeLimit";
public static final String ADMIN_AUTO_GRANT = "auto-grant";
+ public static final String HOST_LOCALHOST = "localhost";
+ public static final String HOST_127_0_0_1 = "127.0.0.1";
}
diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebGlobalProject.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebGlobalProject.java
new file mode 100644
index 0000000000..6cf1faec56
--- /dev/null
+++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebGlobalProject.java
@@ -0,0 +1,85 @@
+/*
+ * 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.server;
+
+import org.jkiss.code.NotNull;
+import org.jkiss.code.Nullable;
+import org.jkiss.dbeaver.Log;
+import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
+import org.jkiss.dbeaver.model.app.DBPWorkspace;
+import org.jkiss.dbeaver.model.auth.SMSessionContext;
+import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
+import org.jkiss.dbeaver.registry.DataSourceRegistry;
+
+import java.nio.file.Path;
+
+/**
+ * Web global project.
+ */
+public class WebGlobalProject extends BaseProjectImpl {
+
+ private static final Log log = Log.getLog(WebGlobalProject.class);
+
+ private final String projectName;
+
+ public WebGlobalProject(
+ @NotNull DBPWorkspace workspace,
+ @Nullable SMSessionContext sessionContext,
+ @NotNull String projectName
+ ) {
+ super(workspace, sessionContext);
+ this.projectName = projectName;
+ }
+
+ @Override
+ public boolean isVirtual() {
+ return false;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return projectName;
+ }
+
+ @NotNull
+ @Override
+ public Path getAbsolutePath() {
+ return getWorkspace().getAbsolutePath().resolve(projectName);
+ }
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public void ensureOpen() {
+
+ }
+
+ @Override
+ public boolean isUseSecretStorage() {
+ return false;
+ }
+
+ @NotNull
+ @Override
+ protected DBPDataSourceRegistry createDataSourceRegistry() {
+ return new DataSourceRegistry(this);
+ }
+}
\ No newline at end of file
diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebGlobalWorkspace.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebGlobalWorkspace.java
index 62e5996c4b..d44262923c 100644
--- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebGlobalWorkspace.java
+++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebGlobalWorkspace.java
@@ -16,23 +16,92 @@
*/
package io.cloudbeaver.server;
-import org.eclipse.core.resources.IWorkspace;
+import io.cloudbeaver.WebProjectImpl;
+import io.cloudbeaver.model.app.WebApplication;
+import io.cloudbeaver.utils.WebAppUtils;
+import org.jkiss.code.NotNull;
+import org.jkiss.code.Nullable;
+import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.app.DBPPlatform;
-import org.jkiss.dbeaver.registry.EclipseWorkspaceImpl;
+import org.jkiss.dbeaver.model.app.DBPProject;
+import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
+import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
+import org.jkiss.utils.CommonUtils;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
/**
* Web global workspace.
- *
- * Basically just a wrapper around Eclipse workspace.
*/
-public class WebGlobalWorkspace extends EclipseWorkspaceImpl {
+public class WebGlobalWorkspace extends BaseWorkspaceImpl {
+
+ private static final Log log = Log.getLog(WebGlobalWorkspace.class);
+
+ protected final Map projects = new LinkedHashMap<>();
+ private WebGlobalProject globalProject;
+
+ private final WebApplication application;
+
+ public WebGlobalWorkspace(
+ @NotNull DBPPlatform platform,
+ @NotNull WebApplication application
+ ) {
+ super(platform, application.getWorkspaceDirectory());
+ this.application = application;
+ }
+
+ @Override
+ public void initializeProjects() {
+ initializeWorkspaceSession();
+
+ // Load global project
+ Path globalProjectPath = getAbsolutePath().resolve(WebAppUtils.getGlobalProjectId());
+ if (!Files.exists(globalProjectPath)) {
+ try {
+ Files.createDirectories(globalProjectPath);
+ } catch (IOException e) {
+ log.error("Error creating global project path: " + globalProject, e);
+ }
+ }
- public WebGlobalWorkspace(DBPPlatform platform, IWorkspace eclipseWorkspace) {
- super(platform, eclipseWorkspace);
+ globalProject = new WebGlobalProject(
+ this,
+ getAuthContext(),
+ CommonUtils.notEmpty(WebAppUtils.getWebApplication().getDefaultProjectName())
+ );
+ activeProject = globalProject;
}
+ @NotNull
@Override
- protected String initWorkspaceId() {
+ public String getWorkspaceId() {
return readWorkspaceIdProperty();
}
+
+ @Nullable
+ @Override
+ public DBPProject getActiveProject() {
+ return super.getActiveProject();
+ }
+
+ @NotNull
+ @Override
+ public List getProjects() {
+ return Collections.singletonList(globalProject);
+ }
+
+ @Nullable
+ @Override
+ public BaseProjectImpl getProject(@NotNull String projectName) {
+ if (globalProject != null && globalProject.getId().equals(projectName)) {
+ return globalProject;
+ }
+ return null;
+ }
}
\ No newline at end of file
diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebPlatformActivator.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebPlatformActivator.java
index 8234bf2122..d13eb03bcf 100644
--- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebPlatformActivator.java
+++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebPlatformActivator.java
@@ -16,8 +16,6 @@
*/
package io.cloudbeaver.server;
-import org.eclipse.core.resources.IWorkspace;
-import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Plugin;
import org.jkiss.dbeaver.ModelPreferences;
import org.jkiss.dbeaver.model.impl.preferences.BundlePreferenceStore;
@@ -25,7 +23,6 @@
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
-import java.io.File;
import java.io.PrintStream;
/**
@@ -35,7 +32,6 @@ public class WebPlatformActivator extends Plugin {
// The shared instance
private static WebPlatformActivator instance;
- private static File configDir;
private PrintStream debugWriter;
private DBPPreferenceStore preferences;
@@ -76,13 +72,6 @@ public DBPPreferenceStore getPreferences() {
return preferences;
}
- /**
- * Returns the workspace instance.
- */
- public static IWorkspace getWorkspace() {
- return ResourcesPlugin.getWorkspace();
- }
-
protected void shutdownPlatform() {
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPreferenceStore.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebServerPreferenceStore.java
similarity index 91%
rename from server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPreferenceStore.java
rename to server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebServerPreferenceStore.java
index 09c98a86cb..681262bf36 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPreferenceStore.java
+++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/server/WebServerPreferenceStore.java
@@ -16,6 +16,7 @@
*/
package io.cloudbeaver.server;
+import io.cloudbeaver.utils.WebAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.impl.preferences.AbstractPreferenceStore;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
@@ -23,16 +24,12 @@
import java.io.IOException;
import java.util.Map;
-public class CBPreferenceStore extends AbstractPreferenceStore {
- @NotNull
- private final CBPlatform cbPlatform;
+public class WebServerPreferenceStore extends AbstractPreferenceStore {
private final DBPPreferenceStore parentStore;
- public CBPreferenceStore(
- @NotNull CBPlatform cbPlatform,
+ public WebServerPreferenceStore(
@NotNull DBPPreferenceStore parentStore
) {
- this.cbPlatform = cbPlatform;
this.parentStore = parentStore;
}
@@ -187,12 +184,8 @@ public void save() throws IOException {
throw new RuntimeException("Not Implemented");
}
- public CBApplication getApp() {
- return cbPlatform.getApplication();
- }
-
private Map productConf() {
- var app = cbPlatform.getApplication();
- return app.getProductConfiguration();
+ var app = WebAppUtils.getWebApplication();
+ return app.getServerConfiguration().getProductSettings();
}
}
diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/DBWServiceBindingServlet.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/DBWServiceBindingServlet.java
index 1018ee3037..5b7e52cc95 100644
--- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/DBWServiceBindingServlet.java
+++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/DBWServiceBindingServlet.java
@@ -23,6 +23,9 @@
* Servlet service
*/
public interface DBWServiceBindingServlet extends DBWServiceBinding {
+ default boolean isApplicable(WebApplication application) {
+ return true;
+ }
void addServlets(APPLICATION application, DBWServletContext servletContext) throws DBException;
}
diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/sql/WebSQLConstants.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/sql/WebSQLConstants.java
index a8ff1bd845..561162394c 100644
--- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/sql/WebSQLConstants.java
+++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/sql/WebSQLConstants.java
@@ -22,7 +22,6 @@
public class WebSQLConstants {
public static final String QUOTA_PROP_ROW_LIMIT = "sqlResultSetRowsLimit";
- public static final String QUOTA_PROP_MEMORY_LIMIT = "sqlResultSetMemoryLimit";
public static final String QUOTA_PROP_QUERY_LIMIT = "sqlMaxRunningQueries";
public static final String QUOTA_PROP_SQL_QUERY_TIMEOUT = "sqlQueryTimeout";
public static final String QUOTA_PROP_TEXT_PREVIEW_MAX_LENGTH = "sqlTextPreviewMaxLength";
diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebAppUtils.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebAppUtils.java
index cd7e6571b3..fdc44975e3 100644
--- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebAppUtils.java
+++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebAppUtils.java
@@ -38,6 +38,8 @@
import java.nio.file.Path;
import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
public class WebAppUtils {
private static final Log log = Log.getLog(WebAppUtils.class);
@@ -199,7 +201,7 @@ public static void addResponseCookie(HttpServletRequest request, HttpServletResp
path = path.concat("; SameSite=" + sameSite);
}
}
-
+ sessionCookie.setHttpOnly(true);
sessionCookie.setPath(path);
response.addCookie(sessionCookie);
}
@@ -269,4 +271,13 @@ private static void flattenArray(Object[] array, Map result, Str
}
}
+ @NotNull
+ public static String getFullServerUrl() {
+ WebApplication application = WebAppUtils.getWebApplication();
+ return Stream.of(application.getServerURL(), application.getRootURI())
+ .map(WebAppUtils::removeSideSlashes)
+ .filter(CommonUtils::isNotEmpty)
+ .collect(Collectors.joining("/"));
+ }
+
}
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 aba7767b06..72c8775cd1 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
@@ -18,9 +18,9 @@
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.DBWebException;
+import io.cloudbeaver.WebSessionProjectImpl;
import io.cloudbeaver.model.WebConnectionInfo;
import io.cloudbeaver.model.WebNetworkHandlerConfigInput;
-import io.cloudbeaver.model.app.WebApplication;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
@@ -37,6 +37,7 @@
import java.util.List;
import java.util.Map;
+import java.util.Optional;
public class WebDataSourceUtils {
@@ -113,14 +114,22 @@ private static void setSecureProperties(DBWHandlerConfiguration handlerConfig, W
@Nullable
public static DBPDataSourceContainer getLocalOrGlobalDataSource(
- WebApplication application, WebSession webSession, @Nullable String projectId, String connectionId
+ WebSession webSession, @Nullable String projectId, String connectionId
) throws DBWebException {
DBPDataSourceContainer dataSource = null;
if (!CommonUtils.isEmpty(connectionId)) {
- dataSource = webSession.getProjectById(projectId).getDataSourceRegistry().getDataSource(connectionId);
- if (dataSource == null && (webSession.hasPermission(DBWConstants.PERMISSION_ADMIN) || application.isConfigurationMode())) {
+ WebSessionProjectImpl project = webSession.getProjectById(projectId);
+ if (project == null) {
+ throw new DBWebException("Project '" + projectId + "' not found");
+ }
+ dataSource = project.getDataSourceRegistry().getDataSource(connectionId);
+ if (dataSource == null &&
+ (webSession.hasPermission(DBWConstants.PERMISSION_ADMIN) || webSession.getApplication().isConfigurationMode())) {
// If called for new connection in admin mode then this connection may absent in session registry yet
- dataSource = getGlobalDataSourceRegistry().getDataSource(connectionId);
+ project = webSession.getGlobalProject();
+ if (project != null) {
+ dataSource = project.getDataSourceRegistry().getDataSource(connectionId);
+ }
}
}
return dataSource;
@@ -157,4 +166,28 @@ public static boolean disconnectDataSource(@NotNull WebSession webSession, @NotN
}
return false;
}
+
+ /**
+ * The method that seeks for web connection in session cache by connection id.
+ * Mostly used when project id is not defined.
+ */
+ @NotNull
+ public static WebConnectionInfo getWebConnectionInfo(
+ @NotNull WebSession webSession,
+ @Nullable String projectId,
+ @NotNull String connectionId
+ ) throws DBWebException {
+ if (projectId == null) {
+ webSession.addWarningMessage("Project id is not defined in request. Try to find it from connection cache");
+ // try to find connection in all accessible projects
+ Optional optional = webSession.getAccessibleProjects().stream()
+ .flatMap(p -> p.getConnections().stream()) // get connection cache from web projects
+ .filter(e -> e.getId().contains(connectionId))
+ .findFirst();
+ if (optional.isPresent()) {
+ return optional.get();
+ }
+ }
+ return webSession.getAccessibleProjectById(projectId).getWebConnectionInfo(connectionId);
+ }
}
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 fc9c336190..c3e4f392e8 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.1.5.qualifier
-Bundle-Release-Date: 20240819
+Bundle-Version: 24.2.4.qualifier
+Bundle-Release-Date: 20241104
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 4dad51a10d..7ce195b084 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.1.5-SNAPSHOT
+ 24.2.4-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 30b10121d3..8b033f910a 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.104.qualifier
-Bundle-Release-Date: 20240819
+Bundle-Version: 1.0.109.qualifier
+Bundle-Release-Date: 20241104
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 1ebd7b15e4..672c61a2d2 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.104-SNAPSHOT
+ 1.0.109-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 fe96f41d69..5f2158d77a 100644
--- a/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF
+++ b/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF
@@ -3,14 +3,13 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Server
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
-Bundle-Version: 24.1.5.qualifier
-Bundle-Release-Date: 20240819
+Bundle-Version: 24.2.4.qualifier
+Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
Bundle-ClassPath: .
Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
- org.eclipse.core.resources;visibility:=reexport,
org.apache.commons.jexl,
org.jkiss.utils;visibility:=reexport,
org.jkiss.dbeaver.model.sql;visibility:=reexport,
@@ -29,8 +28,11 @@ Export-Package: io.cloudbeaver,
io.cloudbeaver.server.events,
io.cloudbeaver.server,
io.cloudbeaver.server.actions,
+ io.cloudbeaver.server.jetty,
+ io.cloudbeaver.server.graphql,
io.cloudbeaver.server.jobs,
io.cloudbeaver.server.servlets,
+ io.cloudbeaver.server.websockets,
io.cloudbeaver.service,
io.cloudbeaver.service.navigator,
io.cloudbeaver.service.session,
diff --git a/server/bundles/io.cloudbeaver.server/pom.xml b/server/bundles/io.cloudbeaver.server/pom.xml
index 214f872d23..80affe3f5e 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.1.5-SNAPSHOT
+ 24.2.4-SNAPSHOT
eclipse-plugin
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 3383195c22..46ff6fa6bb 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebServiceUtils.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebServiceUtils.java
@@ -19,18 +19,18 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
+import com.google.gson.Strictness;
import io.cloudbeaver.model.WebConnectionConfig;
import io.cloudbeaver.model.WebNetworkHandlerConfigInput;
import io.cloudbeaver.model.WebPropertyInfo;
+import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.model.session.WebActionParameters;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
-import io.cloudbeaver.server.CBAppConfig;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBPlatform;
import io.cloudbeaver.service.navigator.WebPropertyFilter;
-import io.cloudbeaver.utils.WebAppUtils;
import io.cloudbeaver.utils.WebCommonUtils;
import io.cloudbeaver.utils.WebDataSourceUtils;
import org.jkiss.code.NotNull;
@@ -98,10 +98,6 @@ public static DBPDataSourceRegistry getGlobalDataSourceRegistry() throws DBWebEx
return WebDataSourceUtils.getGlobalDataSourceRegistry();
}
- public static DBPDataSourceRegistry getGlobalRegistry(WebSession session) {
- return session.getProjectById(WebAppUtils.getGlobalProjectId()).getDataSourceRegistry();
- }
-
public static InputStream openStaticResource(String path) {
return WebServiceUtils.class.getClassLoader().getResourceAsStream(path);
}
@@ -299,7 +295,7 @@ public static void saveAuthProperties(
// Make new Gson parser with type adapters to deserialize into existing credentials
InstanceCreator credTypeAdapter = type -> credentials;
Gson credGson = new GsonBuilder()
- .setLenient()
+ .setStrictness(Strictness.LENIENT)
.registerTypeAdapter(credentials.getClass(), credTypeAdapter)
.create();
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebDatabaseDriverInfo.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebDatabaseDriverInfo.java
index f9e207bbcb..c1bf020243 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebDatabaseDriverInfo.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebDatabaseDriverInfo.java
@@ -18,9 +18,9 @@
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebServiceUtils;
+import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.utils.ConfigurationUtils;
-import io.cloudbeaver.server.CBAppConfig;
import io.cloudbeaver.server.CBApplication;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebDatasourceAccessCheckHandler.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebDatasourceAccessCheckHandler.java
index 2e0fc051d5..ddd680fb03 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebDatasourceAccessCheckHandler.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebDatasourceAccessCheckHandler.java
@@ -17,14 +17,19 @@
package io.cloudbeaver.model;
+import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.model.utils.ConfigurationUtils;
-import io.cloudbeaver.server.CBAppConfig;
import io.cloudbeaver.server.CBApplication;
+import io.cloudbeaver.utils.WebAppUtils;
import org.jkiss.dbeaver.model.connection.DBPDriver;
+//TODO move to a separate CBApplication plugin
public class WebDatasourceAccessCheckHandler extends BaseDatasourceAccessCheckHandler {
@Override
protected boolean isDriverDisabled(DBPDriver driver) {
+ if (!WebAppUtils.getWebApplication().isMultiuser()) {
+ return false;
+ }
CBAppConfig config = CBApplication.getInstance().getAppConfiguration();
return !ConfigurationUtils.isDriverEnabled(
driver,
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java
index 826331eea4..02b99023ec 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java
@@ -16,11 +16,11 @@
*/
package io.cloudbeaver.model;
+import io.cloudbeaver.model.config.PasswordPolicyConfiguration;
import io.cloudbeaver.registry.WebServiceDescriptor;
import io.cloudbeaver.registry.WebServiceRegistry;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBPlatform;
-import io.cloudbeaver.service.security.PasswordPolicyConfiguration;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java
index c3c211c970..e9caa0ea1d 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java
@@ -19,6 +19,7 @@
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.auth.SMAuthProviderFederated;
import io.cloudbeaver.auth.provisioning.SMProvisioner;
+import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.registry.WebAuthProviderConfiguration;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.server.CBApplication;
@@ -64,7 +65,10 @@ public String getDescription() {
}
public boolean isDefaultProvider() {
- return descriptor.getId().equals(CBPlatform.getInstance().getApplication().getAppConfiguration().getDefaultAuthProvider());
+ if (CBPlatform.getInstance().getApplication().getAppConfiguration() instanceof CBAppConfig cbAppConfig) {
+ return descriptor.getId().equals(cbAppConfig.getDefaultAuthProvider());
+ }
+ return false;
}
public boolean isConfigurable() {
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/AppWebSessionManager.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/AppWebSessionManager.java
new file mode 100644
index 0000000000..13cfc7796a
--- /dev/null
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/AppWebSessionManager.java
@@ -0,0 +1,64 @@
+/*
+ * 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.server;
+
+import io.cloudbeaver.DBWebException;
+import io.cloudbeaver.model.session.BaseWebSession;
+import io.cloudbeaver.model.session.WebHeadlessSession;
+import io.cloudbeaver.model.session.WebSession;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Session;
+import org.jkiss.code.NotNull;
+import org.jkiss.code.Nullable;
+import org.jkiss.dbeaver.DBException;
+
+import java.util.Collection;
+
+public interface AppWebSessionManager {
+ BaseWebSession closeSession(@NotNull HttpServletRequest request);
+
+ @NotNull
+ WebSession getWebSession(
+ @NotNull HttpServletRequest request,
+ @NotNull HttpServletResponse response
+ ) throws DBWebException;
+
+ @NotNull
+ WebSession getWebSession(
+ @NotNull HttpServletRequest request,
+ @NotNull HttpServletResponse response,
+ boolean errorOnNoFound
+ ) throws DBWebException;
+
+ @Nullable
+ BaseWebSession getSession(@NotNull String sessionId);
+
+ @Nullable
+ WebSession findWebSession(HttpServletRequest request);
+
+ WebSession findWebSession(HttpServletRequest request, boolean errorOnNoFound) throws DBWebException;
+
+ Collection getAllActiveSessions();
+
+ WebSession getOrRestoreSession(Request httpRequest);
+
+ WebHeadlessSession getHeadlessSession(Request request, Session session, boolean create) throws DBException;
+
+ boolean touchSession(HttpServletRequest request, HttpServletResponse response) throws DBWebException;
+}
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 32cc1ac346..18fde20361 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
@@ -21,12 +21,14 @@
import io.cloudbeaver.model.app.BaseWebApplication;
import io.cloudbeaver.model.app.WebAuthApplication;
import io.cloudbeaver.model.app.WebAuthConfiguration;
+import io.cloudbeaver.model.config.CBAppConfig;
+import io.cloudbeaver.model.config.CBServerConfig;
+import io.cloudbeaver.model.config.SMControllerConfiguration;
import io.cloudbeaver.registry.WebDriverRegistry;
import io.cloudbeaver.registry.WebServiceRegistry;
import io.cloudbeaver.server.jetty.CBJettyServer;
import io.cloudbeaver.service.DBWServiceInitializer;
import io.cloudbeaver.service.DBWServiceServerConfigurator;
-import io.cloudbeaver.service.security.SMControllerConfiguration;
import io.cloudbeaver.service.session.WebSessionManager;
import io.cloudbeaver.utils.WebDataSourceUtils;
import org.eclipse.core.runtime.Platform;
@@ -41,13 +43,13 @@
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
+import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.security.SMAdminController;
import org.jkiss.dbeaver.model.security.SMConstants;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.websocket.event.WSEventController;
import org.jkiss.dbeaver.model.websocket.event.WSServerConfigurationChangedEvent;
-import org.jkiss.dbeaver.registry.BaseApplicationImpl;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.dbeaver.runtime.ui.DBPPlatformUI;
import org.jkiss.dbeaver.utils.GeneralUtils;
@@ -57,24 +59,22 @@
import org.jkiss.utils.StandardConstants;
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.URL;
import java.net.UnknownHostException;
+import java.nio.file.Files;
import java.nio.file.Path;
-import java.security.Permission;
-import java.security.Policy;
-import java.security.ProtectionDomain;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* This class controls all aspects of the application's execution
*/
-public abstract class CBApplication extends BaseWebApplication implements WebAuthApplication {
+public abstract class CBApplication extends
+ BaseWebApplication implements WebAuthApplication, GQLApplicationAdapter {
private static final Log log = Log.getLog(CBApplication.class);
@@ -83,8 +83,6 @@ public abstract class CBApplication extends BaseWebApp
* In configuration mode sessions expire after a week
*/
private static final long CONFIGURATION_MODE_SESSION_IDLE_TIME = 60 * 60 * 1000 * 24 * 7;
- public static final String HOST_LOCALHOST = "localhost";
- public static final String HOST_127_0_0_1 = "127.0.0.1";
static {
@@ -111,6 +109,8 @@ public static CBApplication getInstance() {
private final Map initActions = new ConcurrentHashMap<>();
+ private CBJettyServer jettyServer;
+
public CBApplication() {
this.homeDirectory = new File(initHomeFolder());
}
@@ -203,6 +203,7 @@ protected void startServer() {
if (!loadServerConfiguration()) {
return;
}
+
if (CommonUtils.isEmpty(this.getAppConfiguration().getDefaultUserTeam())) {
throw new DBException("Default user team must be specified");
}
@@ -210,6 +211,7 @@ protected void startServer() {
log.error(e);
return;
}
+
refreshDisabledDriversConfig();
configurationMode = CommonUtils.isEmpty(getServerConfiguration().getServerName());
@@ -221,9 +223,9 @@ protected void startServer() {
if (CommonUtils.isEmpty(localHostAddress)) {
localHostAddress = System.getProperty(CBConstants.VAR_CB_LOCAL_HOST_ADDR);
}
- if (CommonUtils.isEmpty(localHostAddress) || HOST_127_0_0_1.equals(localHostAddress) || "::0".equals(
+ if (CommonUtils.isEmpty(localHostAddress) || CBConstants.HOST_127_0_0_1.equals(localHostAddress) || "::0".equals(
localHostAddress)) {
- localHostAddress = HOST_LOCALHOST;
+ localHostAddress = CBConstants.HOST_LOCALHOST;
}
final Runtime runtime = Runtime.getRuntime();
@@ -231,7 +233,7 @@ protected void startServer() {
Location instanceLoc = Platform.getInstanceLocation();
try {
- if (!instanceLoc.isSet()) {
+ if (!instanceLoc.isSet()) { // always false?
URL wsLocationURL = new URL(
"file", //$NON-NLS-1$
null,
@@ -305,7 +307,7 @@ protected void startServer() {
if (configurationMode) {
// Try to configure automatically
- performAutoConfiguration(getMainConfigurationFilePath().toFile().getParentFile());
+ performAutoConfiguration(getMainConfigurationFilePath().getParent());
} else if (!isMultiNode()) {
var appConfiguration = getServerConfigurationController().getAppConfiguration();
if (appConfiguration.isGrantConnectionsAccessToAnonymousTeam()) {
@@ -314,16 +316,6 @@ protected void startServer() {
grantPermissionsToConnections();
}
- if (getServerConfiguration().isEnableSecurityManager()) {
- Policy.setPolicy(new Policy() {
- @Override
- public boolean implies(ProtectionDomain domain, Permission permission) {
- return true;
- }
- });
- System.setSecurityManager(new SecurityManager());
- }
-
eventController.scheduleCheckJob();
runWebServer();
@@ -343,7 +335,7 @@ protected void initializeAdditionalConfiguration() {
*
* @param configPath
*/
- protected void performAutoConfiguration(File configPath) {
+ protected void performAutoConfiguration(Path configPath) {
String autoServerName = System.getenv(CBConstants.VAR_AUTO_CB_SERVER_NAME);
String autoServerURL = System.getenv(CBConstants.VAR_AUTO_CB_SERVER_URL);
String autoAdminName = System.getenv(CBConstants.VAR_AUTO_CB_ADMIN_NAME);
@@ -352,11 +344,11 @@ protected void performAutoConfiguration(File configPath) {
if (CommonUtils.isEmpty(autoServerName) || CommonUtils.isEmpty(autoAdminName) || CommonUtils.isEmpty(
autoAdminPassword)) {
// Try to load from auto config file
- if (configPath.exists()) {
- File autoConfigFile = new File(configPath, CBConstants.AUTO_CONFIG_FILE_NAME);
- if (autoConfigFile.exists()) {
+ if (Files.exists(configPath)) {
+ Path autoConfigFile = configPath.resolve(CBConstants.AUTO_CONFIG_FILE_NAME);
+ if (Files.exists(autoConfigFile)) {
Properties autoProps = new Properties();
- try (InputStream is = new FileInputStream(autoConfigFile)) {
+ try (InputStream is = Files.newInputStream(autoConfigFile)) {
autoProps.load(is);
autoServerName = autoProps.getProperty(CBConstants.VAR_AUTO_CB_SERVER_NAME);
@@ -364,7 +356,7 @@ protected void performAutoConfiguration(File configPath) {
autoAdminName = autoProps.getProperty(CBConstants.VAR_AUTO_CB_ADMIN_NAME);
autoAdminPassword = autoProps.getProperty(CBConstants.VAR_AUTO_CB_ADMIN_PASSWORD);
} catch (IOException e) {
- log.error("Error loading auto configuration file '" + autoConfigFile.getAbsolutePath() + "'",
+ log.error("Error loading auto configuration file '" + autoConfigFile + "'",
e);
}
}
@@ -451,11 +443,6 @@ public Path getDataDirectory(boolean create) {
return dataDir.toPath();
}
- @Override
- public Path getWorkspaceDirectory() {
- return Path.of(getServerConfiguration().getWorkspaceLocation());
- }
-
private void initializeSecurityController() throws DBException {
securityController = createGlobalSecurityController();
}
@@ -480,7 +467,8 @@ private void runWebServer() {
getServerPort(),
CommonUtils.isEmpty(getServerHost()) ? "all interfaces" : getServerHost())
);
- new CBJettyServer(this).runServer();
+ this.jettyServer = new CBJettyServer(this);
+ this.jettyServer.runServer();
}
@@ -580,6 +568,9 @@ public synchronized void reloadConfiguration(@Nullable SMCredentialsProvider cre
sendConfigChangedEvent(credentialsProvider);
eventController.setForceSkipEvents(isConfigurationMode());
+ if (this.jettyServer != null) {
+ this.jettyServer.refreshJettyConfig();
+ }
}
protected abstract void finishSecurityServiceConfiguration(
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplicationCE.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplicationCE.java
index 7a00e3474c..1ee1b5c0f8 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplicationCE.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplicationCE.java
@@ -17,6 +17,7 @@
package io.cloudbeaver.server;
import io.cloudbeaver.auth.NoAuthCredentialsProvider;
+import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.model.rm.local.LocalResourceController;
import io.cloudbeaver.service.security.CBEmbeddedSecurityController;
import io.cloudbeaver.service.security.EmbeddedSecurityControllerFactory;
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatform.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatform.java
index 4238fb2e70..0cc1833109 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatform.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatform.java
@@ -22,40 +22,24 @@
import io.cloudbeaver.server.jobs.WebDataSourceMonitorJob;
import io.cloudbeaver.server.jobs.WebSessionMonitorJob;
import io.cloudbeaver.service.session.WebSessionManager;
-import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
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.DBFileController;
-import org.jkiss.dbeaver.model.app.DBACertificateStorage;
-import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.connection.DBPDataSourceProviderDescriptor;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.connection.DBPDriverLibrary;
-import org.jkiss.dbeaver.model.impl.app.DefaultCertificateStorage;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
-import org.jkiss.dbeaver.model.qm.QMRegistry;
-import org.jkiss.dbeaver.model.qm.QMUtils;
import org.jkiss.dbeaver.model.runtime.AbstractJob;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
-import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
-import org.jkiss.dbeaver.registry.BasePlatformImpl;
import org.jkiss.dbeaver.registry.DataSourceProviderRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
-import org.jkiss.dbeaver.runtime.SecurityProviderUtils;
-import org.jkiss.dbeaver.runtime.qm.QMLogFileWriter;
-import org.jkiss.dbeaver.runtime.qm.QMRegistryImpl;
-import org.jkiss.dbeaver.utils.ContentUtils;
import org.jkiss.utils.IOUtils;
import java.io.IOException;
import java.nio.file.Files;
-import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -63,7 +47,7 @@
/**
* CBPlatform
*/
-public class CBPlatform extends BasePlatformImpl {
+public class CBPlatform extends BaseGQLPlatform {
// The plug-in ID
public static final String PLUGIN_ID = "io.cloudbeaver.server"; //$NON-NLS-1$
@@ -72,28 +56,20 @@ public class CBPlatform extends BasePlatformImpl {
public static final String TEMP_FILE_FOLDER = "temp-sql-upload-files";
public static final String TEMP_FILE_IMPORT_FOLDER = "temp-import-files";
- public static final String WORK_DATA_FOLDER_NAME = ".work-data";
-
@Nullable
- private static CBApplication> application = null;
-
- private Path tempFolder;
+ private static GQLApplicationAdapter application = null;
- private QMRegistryImpl queryManager;
- private QMLogFileWriter qmLogWriter;
- private DBACertificateStorage certificateStorage;
- private WebGlobalWorkspace workspace;
- private CBPreferenceStore preferenceStore;
- private final List applicableDrivers = new ArrayList<>();
+ private WebServerPreferenceStore preferenceStore;
+ protected final List applicableDrivers = new ArrayList<>();
public static CBPlatform getInstance() {
return (CBPlatform) DBWorkbench.getPlatform();
}
- CBPlatform() {
+ protected CBPlatform() {
}
- public static void setApplication(CBApplication application) {
+ public static void setApplication(@NotNull GQLApplicationAdapter application) {
CBPlatform.application = application;
}
@@ -101,33 +77,24 @@ public static void setApplication(CBApplication application) {
protected synchronized void initialize() {
long startTime = System.currentTimeMillis();
log.info("Initialize web platform...: ");
- this.preferenceStore = new CBPreferenceStore(this, WebPlatformActivator.getInstance().getPreferences());
- // Register BC security provider
- SecurityProviderUtils.registerSecurityProvider();
-
- // Register properties adapter
- this.workspace = new WebGlobalWorkspace(this, ResourcesPlugin.getWorkspace());
- this.workspace.initializeProjects();
-
- QMUtils.initApplication(this);
- this.queryManager = new QMRegistryImpl();
-
- this.qmLogWriter = new QMLogFileWriter();
- this.queryManager.registerMetaListener(qmLogWriter);
-
- this.certificateStorage = new DefaultCertificateStorage(
- WebPlatformActivator.getInstance().getStateLocation().toFile().toPath().resolve(DBConstants.CERTIFICATE_STORAGE_FOLDER));
+ this.preferenceStore = new WebServerPreferenceStore(WebPlatformActivator.getInstance().getPreferences());
super.initialize();
-
refreshApplicableDrivers();
- new WebSessionMonitorJob(this)
- .scheduleMonitor();
+ scheduleServerJobs();
+ log.info("Web platform initialized (" + (System.currentTimeMillis() - startTime) + "ms)");
+ }
- new SessionStateJob(this)
- .scheduleMonitor();
+ protected void scheduleServerJobs() {
+ if (getSessionManager() instanceof WebSessionManager webSessionManager) {
+ new WebSessionMonitorJob(this, webSessionManager)
+ .scheduleMonitor();
- new WebDataSourceMonitorJob(this)
+ new SessionStateJob(this, webSessionManager)
+ .scheduleMonitor();
+ }
+
+ new WebDataSourceMonitorJob(this, getSessionManager())
.scheduleMonitor();
new AbstractJob("Delete temp folder") {
@@ -142,7 +109,6 @@ protected IStatus run(DBRProgressMonitor monitor) {
return Status.OK_STATUS;
}
}.schedule();
- log.info("Web platform initialized (" + (System.currentTimeMillis() - startTime) + "ms)");
}
public synchronized void dispose() {
@@ -151,34 +117,6 @@ public synchronized void dispose() {
super.dispose();
- if (this.qmLogWriter != null) {
- this.queryManager.unregisterMetaListener(qmLogWriter);
- this.qmLogWriter.dispose();
- this.qmLogWriter = null;
- }
- if (this.queryManager != null) {
- this.queryManager.dispose();
- //queryManager = null;
- }
- DataSourceProviderRegistry.dispose();
-
- if (workspace != null) {
- try {
- workspace.save(new VoidProgressMonitor());
- } catch (DBException ex) {
- log.error("Can't save workspace", ex); //$NON-NLS-1$
- }
- }
-
- // Remove temp folder
- if (tempFolder != null) {
-
- if (!ContentUtils.deleteFileRecursive(tempFolder.toFile())) {
- log.warn("Can't delete temp folder '" + tempFolder.toAbsolutePath() + "'");
- }
- tempFolder = null;
- }
-
CBPlatform.application = null;
System.gc();
log.debug("Shutdown completed in " + (System.currentTimeMillis() - startTime) + "ms");
@@ -186,13 +124,7 @@ public synchronized void dispose() {
@NotNull
@Override
- public DBPWorkspace getWorkspace() {
- return workspace;
- }
-
- @NotNull
- @Override
- public CBApplication> getApplication() {
+ public GQLApplicationAdapter getApplication() {
return application;
}
@@ -200,10 +132,6 @@ public List getApplicableDrivers() {
return applicableDrivers;
}
- @NotNull
- public QMRegistry getQueryManager() {
- return queryManager;
- }
@NotNull
@Override
@@ -211,48 +139,12 @@ public DBPPreferenceStore getPreferenceStore() {
return preferenceStore;
}
- @NotNull
- @Override
- public DBACertificateStorage getCertificateStorage() {
- return certificateStorage;
- }
-
- @NotNull
- public Path getTempFolder(@NotNull DBRProgressMonitor monitor, @NotNull String name) {
- if (tempFolder == null) {
- // Make temp folder
- monitor.subTask("Create temp folder");
- tempFolder = workspace.getAbsolutePath().resolve(WORK_DATA_FOLDER_NAME);
- }
- if (!Files.exists(tempFolder)) {
- try {
- Files.createDirectories(tempFolder);
- } catch (IOException e) {
- log.error("Can't create temp directory " + tempFolder, e);
- }
- }
- Path folder = tempFolder.resolve(name);
- if (!Files.exists(folder)) {
- try {
- Files.createDirectories(folder);
- } catch (IOException e) {
- log.error("Error creating temp folder '" + folder.toAbsolutePath() + "'", e);
- }
- }
- return folder;
- }
-
- @Override
- protected Plugin getProductPlugin() {
- return WebPlatformActivator.getInstance();
- }
-
@Override
public boolean isShuttingDown() {
return false;
}
- public WebSessionManager getSessionManager() {
+ public AppWebSessionManager getSessionManager() {
return application.getSessionManager();
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatformActivator.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatformActivator.java
index 8bfd80106d..cee656629f 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatformActivator.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatformActivator.java
@@ -28,7 +28,7 @@ public class CBPlatformActivator extends WebPlatformActivator {
protected void shutdownPlatform() {
try {
// Dispose core
- if (DBWorkbench.getPlatform() instanceof CBPlatform cbPlatform) {
+ if (DBWorkbench.isPlatformStarted() && DBWorkbench.getPlatform() instanceof CBPlatform cbPlatform) {
cbPlatform.dispose();
}
} catch (Throwable e) {
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerConfigurationController.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerConfigurationController.java
index b4233baf3f..637ea382d3 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerConfigurationController.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerConfigurationController.java
@@ -16,13 +16,13 @@
*/
package io.cloudbeaver.server;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.InstanceCreator;
+import com.google.gson.*;
import io.cloudbeaver.model.app.BaseServerConfigurationController;
import io.cloudbeaver.model.app.BaseWebApplication;
-import io.cloudbeaver.service.security.PasswordPolicyConfiguration;
-import io.cloudbeaver.service.security.SMControllerConfiguration;
+import io.cloudbeaver.model.config.CBAppConfig;
+import io.cloudbeaver.model.config.CBServerConfig;
+import io.cloudbeaver.model.config.PasswordPolicyConfiguration;
+import io.cloudbeaver.model.config.SMControllerConfiguration;
import io.cloudbeaver.utils.WebAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
@@ -40,6 +40,7 @@
import org.jkiss.dbeaver.utils.PrefUtils;
import org.jkiss.dbeaver.utils.SystemVariablesResolver;
import org.jkiss.utils.CommonUtils;
+import org.jkiss.utils.IOUtils;
import java.io.*;
import java.net.InetAddress;
@@ -66,6 +67,7 @@ public abstract class CBServerConfigurationController
private final Map originalConfigurationProperties = new LinkedHashMap<>();
protected CBServerConfigurationController(@NotNull T serverConfiguration, @NotNull Path homeDirectory) {
+ super(homeDirectory);
this.serverConfiguration = serverConfiguration;
this.homeDirectory = homeDirectory;
}
@@ -89,17 +91,25 @@ public void loadServerConfiguration(Path configPath) throws DBException {
loadConfiguration(configPath);
}
+ initWorkspacePath();
+
// Try to load configuration from runtime app config file
Path runtimeConfigPath = getRuntimeAppConfigPath();
if (Files.exists(runtimeConfigPath)) {
log.debug("Runtime configuration [" + runtimeConfigPath.toAbsolutePath() + "]");
loadConfiguration(runtimeConfigPath);
}
-
// Set default preferences
PrefUtils.setDefaultPreferenceValue(DBWorkbench.getPlatform().getPreferenceStore(),
ModelPreferences.UI_DRIVERS_HOME,
getServerConfiguration().getDriversLocation());
+ validateFinalServerConfiguration();
+ }
+
+ @NotNull
+ @Override
+ protected String getWorkspaceLocation() {
+ return getServerConfiguration().getWorkspaceLocation();
}
public void loadConfiguration(Path configPath) throws DBException {
@@ -144,7 +154,7 @@ protected void parseConfiguration(Map configProps) throws DBExce
);
// App config
Map appConfig = JSONUtils.getObject(configProps, "app");
- validateConfiguration(appConfig);
+ preValidateAppConfiguration(appConfig);
gson.fromJson(gson.toJson(appConfig), CBAppConfig.class);
readProductConfiguration(serverConfig, gson);
}
@@ -158,7 +168,7 @@ public T parseServerConfiguration() {
hostName = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
log.debug("Error resolving localhost address: " + e.getMessage());
- hostName = CBApplication.HOST_LOCALHOST;
+ hostName = CBConstants.HOST_LOCALHOST;
}
}
config.setServerURL("http://" + hostName + ":" + config.getServerPort());
@@ -167,7 +177,6 @@ public T parseServerConfiguration() {
config.setContentRoot(WebAppUtils.getRelativePath(config.getContentRoot(), homeDirectory));
config.setRootURI(readRootUri(config.getRootURI()));
config.setDriversLocation(WebAppUtils.getRelativePath(config.getDriversLocation(), homeDirectory));
- config.setWorkspaceLocation(WebAppUtils.getRelativePath(config.getWorkspaceLocation(), homeDirectory));
String staticContentsFile = config.getStaticContent();
if (!CommonUtils.isEmpty(staticContentsFile)) {
@@ -180,10 +189,11 @@ public T parseServerConfiguration() {
return config;
}
- protected void validateConfiguration(Map appConfig) throws DBException {
+ protected void preValidateAppConfiguration(Map appConfig) throws DBException {
}
+
private void readExternalProperties(Map serverConfig) {
String externalPropertiesFile = JSONUtils.getString(serverConfig, CBConstants.PARAM_EXTERNAL_PROPERTIES);
if (!CommonUtils.isEmpty(externalPropertiesFile)) {
@@ -246,19 +256,21 @@ protected void readProductConfiguration(Map serverConfig, Gson g
}
}
- // Add product config from runtime
- File rtConfig = getRuntimeProductConfigFilePath().toFile();
- if (rtConfig.exists()) {
- log.debug("Load product runtime configuration from '" + rtConfig.getAbsolutePath() + "'");
- try (Reader reader = new InputStreamReader(new FileInputStream(rtConfig), StandardCharsets.UTF_8)) {
- var runtimeProductSettings = JSONUtils.parseMap(gson, reader);
- var productSettings = serverConfiguration.getProductSettings();
- runtimeProductSettings.putAll(productSettings);
- Map flattenConfig = WebAppUtils.flattenMap(runtimeProductSettings);
- productSettings.clear();
- productSettings.putAll(flattenConfig);
- } catch (Exception e) {
- throw new DBException("Error reading product runtime configuration", e);
+ if (workspacePath != null && IOUtils.isFileFromDefaultFS(getWorkspacePath())) {
+ // Add product config from runtime
+ Path rtConfig = getRuntimeProductConfigFilePath();
+ if (Files.exists(rtConfig)) {
+ log.debug("Load product runtime configuration from '" + rtConfig + "'");
+ try (Reader reader = new InputStreamReader(Files.newInputStream(rtConfig), StandardCharsets.UTF_8)) {
+ var runtimeProductSettings = JSONUtils.parseMap(gson, reader);
+ var productSettings = serverConfiguration.getProductSettings();
+ runtimeProductSettings.putAll(productSettings);
+ Map flattenConfig = WebAppUtils.flattenMap(runtimeProductSettings);
+ productSettings.clear();
+ productSettings.putAll(flattenConfig);
+ } catch (Exception e) {
+ throw new DBException("Error reading product runtime configuration", e);
+ }
}
}
}
@@ -305,13 +317,14 @@ protected Map readConfiguration(Path configPath) throws DBExcept
}
public Map readConfigurationFile(Path path) throws DBException {
- try (Reader reader = new InputStreamReader(new FileInputStream(path.toFile()), StandardCharsets.UTF_8)) {
+ try (Reader reader = new InputStreamReader(Files.newInputStream(path), StandardCharsets.UTF_8)) {
return JSONUtils.parseMap(getGson(), reader);
} catch (Exception e) {
throw new DBException("Error parsing server configuration", e);
}
}
+ @NotNull
protected GsonBuilder getGsonBuilder() {
// Stupid way to populate existing objects but ok google (https://github.com/google/gson/issues/431)
InstanceCreator appConfigCreator = type -> appConfiguration;
@@ -322,7 +335,8 @@ protected GsonBuilder getGsonBuilder() {
InstanceCreator smPasswordPoliceConfigCreator =
type -> securityManagerConfiguration.getPasswordPolicyConfiguration();
return new GsonBuilder()
- .setLenient()
+ .setStrictness(Strictness.LENIENT)
+ .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.registerTypeAdapter(getServerConfiguration().getClass(), serverConfigCreator)
.registerTypeAdapter(CBAppConfig.class, appConfigCreator)
.registerTypeAdapter(DataSourceNavigatorSettings.class, navSettingsCreator)
@@ -356,10 +370,9 @@ private synchronized void writeRuntimeConfig(Path runtimeConfigPath, Map productConfiguration) throws DBException {
@@ -625,4 +640,15 @@ private String readRootUri(String uri) {
}
return uri;
}
+
+ @NotNull
+ @Override
+ public Map getOriginalConfigurationProperties() {
+ return originalConfigurationProperties;
+ }
+
+ @Override
+ public void validateFinalServerConfiguration() throws DBException {
+
+ }
}
\ No newline at end of file
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerConfigurationControllerEmbedded.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerConfigurationControllerEmbedded.java
index 600b7aeed4..87d28cd7d1 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerConfigurationControllerEmbedded.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerConfigurationControllerEmbedded.java
@@ -19,7 +19,8 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
-import io.cloudbeaver.service.security.db.WebDatabaseConfig;
+import io.cloudbeaver.model.config.CBServerConfig;
+import io.cloudbeaver.model.config.WebDatabaseConfig;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
@@ -91,6 +92,7 @@ private void savePasswordPolicyConfig(Map originServerConfig, Ma
}
}
+ @NotNull
@Override
protected GsonBuilder getGsonBuilder() {
GsonBuilder gsonBuilder = super.getGsonBuilder();
@@ -99,6 +101,4 @@ protected GsonBuilder getGsonBuilder() {
return gsonBuilder
.registerTypeAdapter(WebDatabaseConfig.class, dbConfigCreator);
}
-
-
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/GQLApplicationAdapter.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/GQLApplicationAdapter.java
new file mode 100644
index 0000000000..f747216547
--- /dev/null
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/GQLApplicationAdapter.java
@@ -0,0 +1,44 @@
+/*
+ * 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.server;
+
+import io.cloudbeaver.model.app.WebApplication;
+import io.cloudbeaver.registry.WebDriverRegistry;
+import org.jkiss.code.NotNull;
+
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Map;
+
+//FIXME: this interface should not exist,
+// the logic of platforms and applications should be separated from each other
+public interface GQLApplicationAdapter extends WebApplication {
+ AppWebSessionManager getSessionManager();
+
+ WebDriverRegistry getDriverRegistry();
+
+ @NotNull
+ Map getProductConfiguration();
+
+ List getLocalInetAddresses();
+
+ Map getInitActions();
+
+ boolean isLicenseValid();
+
+ String getLicenseStatus();
+}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/actions/AbstractActionServletHandler.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/actions/AbstractActionServletHandler.java
index f6c68805f8..bcf97f7aaf 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/actions/AbstractActionServletHandler.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/actions/AbstractActionServletHandler.java
@@ -18,6 +18,7 @@
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.service.DBWServletHandler;
+import io.cloudbeaver.utils.WebAppUtils;
import jakarta.servlet.Servlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -43,7 +44,7 @@ protected void createActionFromParams(WebSession session, HttpServletRequest req
action.saveInSession(session);
// Redirect to home
- response.sendRedirect("/");
+ response.sendRedirect(WebAppUtils.getWebApplication().getServerConfiguration().getRootURI());
}
protected abstract String getActionConsole();
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSDataSourceUpdatedEventHandlerImpl.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSDataSourceUpdatedEventHandlerImpl.java
index 81d54ab417..21928f0203 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSDataSourceUpdatedEventHandlerImpl.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSDataSourceUpdatedEventHandlerImpl.java
@@ -16,7 +16,7 @@
*/
package io.cloudbeaver.server.events;
-import io.cloudbeaver.WebProjectImpl;
+import io.cloudbeaver.WebSessionProjectImpl;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.code.NotNull;
@@ -34,15 +34,13 @@ public class WSDataSourceUpdatedEventHandlerImpl extends WSAbstractProjectEventH
@Override
protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull WSDataSourceEvent event) {
var sendEvent = true;
- if (activeUserSession instanceof WebSession) {
- var webSession = (WebSession) activeUserSession;
- WebProjectImpl project = webSession.getProjectById(event.getProjectId());
+ if (activeUserSession instanceof WebSession webSession) {
+ WebSessionProjectImpl project = webSession.getProjectById(event.getProjectId());
if (project == null) {
log.debug("Project " + event.getProjectId() + " is not found in session " + webSession.getSessionId());
return;
}
- sendEvent = webSession.updateProjectDataSources(
- project,
+ sendEvent = project.updateProjectDataSources(
event.getDataSourceIds(),
WSEventType.valueById(event.getId())
);
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSObjectPermissionUpdatedEventHandler.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSObjectPermissionUpdatedEventHandler.java
index 0968876b3e..7f4ebeb740 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSObjectPermissionUpdatedEventHandler.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSObjectPermissionUpdatedEventHandler.java
@@ -16,102 +16,165 @@
*/
package io.cloudbeaver.server.events;
+import io.cloudbeaver.WebSessionGlobalProjectImpl;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
+import io.cloudbeaver.server.CBApplication;
+import io.cloudbeaver.server.CBPlatform;
+import io.cloudbeaver.service.security.SMUtils;
import io.cloudbeaver.utils.WebAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
-import org.jkiss.dbeaver.model.security.SMObjectType;
+import org.jkiss.dbeaver.model.security.SMAdminController;
+import org.jkiss.dbeaver.model.security.SMObjectPermissionsGrant;
import org.jkiss.dbeaver.model.websocket.event.WSEventType;
import org.jkiss.dbeaver.model.websocket.event.WSProjectUpdateEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.model.websocket.event.permissions.WSObjectPermissionEvent;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
public class WSObjectPermissionUpdatedEventHandler extends WSDefaultEventHandler {
private static final Log log = Log.getLog(WSObjectPermissionUpdatedEventHandler.class);
@Override
- protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull WSObjectPermissionEvent event) {
- try {
+ public void handleEvent(@NotNull WSObjectPermissionEvent event) {
+ String objectId = event.getObjectId();
+ Consumer runnable = switch (event.getSmObjectType()) {
+ case project:
+ yield getUpdateUserProjectsInfoConsumer(event, objectId);
+ case datasource:
+ try {
+ SMAdminController smController = CBApplication.getInstance().getSecurityController();
+ Set dataSourcePermissions = smController.getObjectPermissionGrants(event.getObjectId(), event.getSmObjectType())
+ .stream()
+ .map(SMObjectPermissionsGrant::getSubjectId).collect(Collectors.toSet());
+ yield getUpdateUserDataSourcesInfoConsumer(event, objectId, dataSourcePermissions);
+ } catch (DBException e) {
+ log.error("Error getting permissions for data source " + objectId, e);
+ yield null;
+ }
+ };
+ if (runnable == null) {
+ return;
+ }
+ log.debug(event.getTopicId() + " event handled");
+ Collection allSessions = CBPlatform.getInstance().getSessionManager().getAllActiveSessions();
+ for (var activeUserSession : allSessions) {
+ if (!isAcceptableInSession(activeUserSession, event)) {
+ log.debug("Cannot handle %s event '%s' in session %s".formatted(
+ event.getTopicId(),
+ event.getId(),
+ activeUserSession.getSessionId()
+ ));
+ continue;
+ }
+ log.debug("%s event '%s' handled".formatted(event.getTopicId(), event.getId()));
+ runnable.accept(activeUserSession);
+ }
+ }
+
+ @NotNull
+ private Consumer getUpdateUserDataSourcesInfoConsumer(
+ @NotNull WSObjectPermissionEvent event,
+ @NotNull String dataSourceId,
+ @NotNull Set dataSourcePermissions
+ ) {
+ return (activeUserSession) -> {
// we have accessible data sources only in web session
- if (event.getSmObjectType() == SMObjectType.datasource && !(activeUserSession instanceof WebSession)) {
+ // admins already have access for all shared connections
+ if (!(activeUserSession instanceof WebSession webSession) || SMUtils.isAdmin(webSession)) {
return;
}
- var objectId = event.getObjectId();
-
- boolean isAccessibleNow;
- switch (event.getSmObjectType()) {
- case project:
- if (WSEventType.OBJECT_PERMISSIONS_UPDATED.getEventId().equals(event.getId())) {
- var accessibleProjectIds = activeUserSession.getUserContext().getAccessibleProjectIds();
- if (accessibleProjectIds.contains(event.getObjectId())) {
- return;
- }
- activeUserSession.addSessionProject(objectId);
- activeUserSession.addSessionEvent(
- WSProjectUpdateEvent.create(
- event.getSessionId(),
- event.getUserId(),
- objectId
- )
- );
- } else if (WSEventType.OBJECT_PERMISSIONS_DELETED.getEventId().equals(event.getId())) {
- activeUserSession.removeSessionProject(objectId);
- activeUserSession.addSessionEvent(
- WSProjectUpdateEvent.delete(
- event.getSessionId(),
- event.getUserId(),
- objectId
- )
- );
- }
- break;
- case datasource:
- var webSession = (WebSession) activeUserSession;
- var dataSources = List.of(objectId);
+ if (!isAcceptableInSession(webSession, event)) {
+ return;
+ }
+ var user = activeUserSession.getUserContext().getUser();
+ var userSubjects = new HashSet<>(Set.of(user.getTeams()));
+ userSubjects.add(user.getUserId());
+ boolean shouldBeAccessible = dataSourcePermissions.stream().anyMatch(userSubjects::contains);
+ List dataSources = List.of(dataSourceId);
+ WebSessionGlobalProjectImpl project = webSession.getGlobalProject();
+ if (project == null) {
+ log.error("Project " + WebAppUtils.getGlobalProjectId() +
+ " is not found in session " + activeUserSession.getSessionId());
+ return;
+ }
+ boolean isAccessibleNow = project.findWebConnectionInfo(dataSourceId) != null;
+ if (WSEventType.OBJECT_PERMISSIONS_UPDATED.getEventId().equals(event.getId())) {
+ if (isAccessibleNow || !shouldBeAccessible) {
+ return;
+ }
+ project.addAccessibleConnectionToCache(dataSourceId);
+ webSession.addSessionEvent(
+ WSDataSourceEvent.create(
+ event.getSessionId(),
+ event.getUserId(),
+ project.getId(),
+ dataSources,
+ WSDataSourceProperty.CONFIGURATION
+ )
+ );
+ } else if (WSEventType.OBJECT_PERMISSIONS_DELETED.getEventId().equals(event.getId())) {
+ if (!isAccessibleNow || shouldBeAccessible) {
+ return;
+ }
+ project.removeAccessibleConnectionFromCache(dataSourceId);
+ webSession.addSessionEvent(
+ WSDataSourceEvent.delete(
+ event.getSessionId(),
+ event.getUserId(),
+ project.getId(),
+ dataSources,
+ WSDataSourceProperty.CONFIGURATION
+ )
+ );
+ }
+ };
+ }
- var project = webSession.getProjectById(WebAppUtils.getGlobalProjectId());
- if (project == null) {
- log.error("Project " + WebAppUtils.getGlobalProjectId() +
- " is not found in session " + activeUserSession.getSessionId());
+ @NotNull
+ private Consumer getUpdateUserProjectsInfoConsumer(
+ @NotNull WSObjectPermissionEvent event,
+ @NotNull String projectId
+ ) {
+ return (activeUserSession) -> {
+ try {
+ if (WSEventType.OBJECT_PERMISSIONS_UPDATED.getEventId().equals(event.getId())) {
+ var accessibleProjectIds = activeUserSession.getUserContext().getAccessibleProjectIds();
+ if (accessibleProjectIds.contains(event.getObjectId())) {
return;
}
- if (WSEventType.OBJECT_PERMISSIONS_UPDATED.getEventId().equals(event.getId())) {
- isAccessibleNow = webSession.findWebConnectionInfo(project.getId(), objectId) != null;
- if (isAccessibleNow) {
- return;
- }
- webSession.addAccessibleConnectionToCache(objectId);
- webSession.addSessionEvent(
- WSDataSourceEvent.create(
- event.getSessionId(),
- event.getUserId(),
- WebAppUtils.getGlobalProjectId(),
- dataSources,
- WSDataSourceProperty.CONFIGURATION
- )
- );
- } else if (WSEventType.OBJECT_PERMISSIONS_DELETED.getEventId().equals(event.getId())) {
- webSession.removeAccessibleConnectionFromCache(objectId);
- webSession.addSessionEvent(
- WSDataSourceEvent.delete(
- event.getSessionId(),
- event.getUserId(),
- WebAppUtils.getGlobalProjectId(),
- dataSources,
- WSDataSourceProperty.CONFIGURATION
- )
- );
- }
+ activeUserSession.addSessionProject(projectId);
+ activeUserSession.addSessionEvent(
+ WSProjectUpdateEvent.create(
+ event.getSessionId(),
+ event.getUserId(),
+ projectId
+ )
+ );
+ } else if (WSEventType.OBJECT_PERMISSIONS_DELETED.getEventId().equals(event.getId())) {
+ activeUserSession.removeSessionProject(projectId);
+ activeUserSession.addSessionEvent(
+ WSProjectUpdateEvent.delete(
+ event.getSessionId(),
+ event.getUserId(),
+ projectId
+ )
+ );
+ }
+ } catch (DBException e) {
+ log.error("Error on changing permissions for project " +
+ event.getObjectId() + " in session " + activeUserSession.getSessionId(), e);
}
- } catch (DBException e) {
- log.error("Error on changing permissions for project " +
- event.getObjectId() + " in session " + activeUserSession.getSessionId(), e);
- }
+ };
}
@Override
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSRmResourceUpdatedEventHandlerImpl.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSRmResourceUpdatedEventHandlerImpl.java
index 48dccc7e55..3466b93b0d 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSRmResourceUpdatedEventHandlerImpl.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSRmResourceUpdatedEventHandlerImpl.java
@@ -53,13 +53,13 @@ private void acceptChangesInNavigatorTree(WSEventType eventType, String resource
if (eventType == WSEventType.RM_RESOURCE_CREATED) {
RMEventManager.fireEvent(
new RMEvent(RMEvent.Action.RESOURCE_ADD,
- project.getRmProject(),
+ project.getRMProject(),
resourcePath)
);
} else if (eventType == WSEventType.RM_RESOURCE_DELETED) {
RMEventManager.fireEvent(
new RMEvent(RMEvent.Action.RESOURCE_DELETE,
- project.getRmProject(),
+ project.getRMProject(),
resourcePath)
);
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSUserEventHandler.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSUserEventHandler.java
index 4188468da5..14dc4cdb76 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSUserEventHandler.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSUserEventHandler.java
@@ -32,7 +32,11 @@ public void handleEvent(@NotNull EVENT event) {
if (eventType == null) {
return;
}
- WebSessionManager sessionManager = CBPlatform.getInstance().getSessionManager();
+ var appSessionManager = CBPlatform.getInstance().getSessionManager();
+ if (!(appSessionManager instanceof WebSessionManager)) {
+ return;
+ }
+ var sessionManager = (WebSessionManager) appSessionManager;
switch (eventType) {
case CLOSE_USER_SESSIONS:
if (event instanceof WSUserCloseSessionsEvent closeSessionsEvent) {
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSUserSecretEventHandlerImpl.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSUserSecretEventHandlerImpl.java
index ade211b040..438a1ef858 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSUserSecretEventHandlerImpl.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSUserSecretEventHandlerImpl.java
@@ -16,6 +16,7 @@
*/
package io.cloudbeaver.server.events;
+import io.cloudbeaver.WebSessionProjectImpl;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.code.NotNull;
@@ -36,11 +37,16 @@ public class WSUserSecretEventHandlerImpl extends WSDefaultEventHandler " + apiCall);
+ } else if (DEBUG) {
+ log.debug("API > " + query);
}
}
ExecutionInput executionInput = contextBuilder.build();
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServer.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServer.java
index 2eb86a3757..e3c94cce88 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServer.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServer.java
@@ -16,26 +16,27 @@
*/
package io.cloudbeaver.server.jetty;
+import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.registry.WebServiceRegistry;
import io.cloudbeaver.server.CBApplication;
-import io.cloudbeaver.server.CBServerConfig;
+import io.cloudbeaver.server.GQLApplicationAdapter;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
import io.cloudbeaver.server.servlets.CBImageServlet;
import io.cloudbeaver.server.servlets.CBStaticServlet;
import io.cloudbeaver.server.servlets.CBStatusServlet;
import io.cloudbeaver.server.websockets.CBJettyWebSocketManager;
import io.cloudbeaver.service.DBWServiceBindingServlet;
+import io.cloudbeaver.service.DBWServiceBindingWebSocket;
+import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler;
+import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee10.servlet.ServletHolder;
+import org.eclipse.jetty.ee10.servlet.ServletMapping;
import org.eclipse.jetty.server.*;
-import org.eclipse.jetty.server.session.DefaultSessionCache;
-import org.eclipse.jetty.server.session.DefaultSessionIdManager;
-import org.eclipse.jetty.server.session.NullSessionDataStore;
-import org.eclipse.jetty.server.session.SessionHandler;
-import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.servlet.ServletMapping;
-import org.eclipse.jetty.util.resource.PathResource;
-import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
+import org.eclipse.jetty.session.DefaultSessionCache;
+import org.eclipse.jetty.session.DefaultSessionIdManager;
+import org.eclipse.jetty.session.NullSessionDataStore;
+import org.eclipse.jetty.util.resource.ResourceFactory;
+import org.eclipse.jetty.websocket.server.WebSocketUpgradeHandler;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
@@ -59,6 +60,7 @@ public class CBJettyServer {
}
private final CBApplication> application;
+ private Server server;
public CBJettyServer(@NotNull CBApplication> application) {
this.application = application;
@@ -67,15 +69,14 @@ public CBJettyServer(@NotNull CBApplication> application) {
public void runServer() {
try {
CBServerConfig serverConfiguration = application.getServerConfiguration();
- JettyServer server;
int serverPort = serverConfiguration.getServerPort();
String serverHost = serverConfiguration.getServerHost();
Path sslPath = getSslConfigurationPath();
boolean sslConfigurationExists = sslPath != null && Files.exists(sslPath);
if (sslConfigurationExists) {
- server = new JettyServer();
- XmlConfiguration sslConfiguration = new XmlConfiguration(new PathResource(sslPath));
+ server = new Server();
+ XmlConfiguration sslConfiguration = new XmlConfiguration(ResourceFactory.of(server).newResource(sslPath));
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// method sslConfiguration.configure() does not see the context class of the Loader,
// so we have to configure it manually, then return the old classLoader.
@@ -84,23 +85,31 @@ public void runServer() {
Thread.currentThread().setContextClassLoader(classLoader);
} else {
if (CommonUtils.isEmpty(serverHost)) {
- server = new JettyServer(serverPort);
+ server = new Server(serverPort);
} else {
- server = new JettyServer(
+ server = new Server(
InetSocketAddress.createUnresolved(serverHost, serverPort));
}
}
{
// Handler configuration
+ Path contentRootPath = Path.of(serverConfiguration.getContentRoot());
ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
- servletContextHandler.setResourceBase(serverConfiguration.getContentRoot());
+ servletContextHandler.setBaseResourceAsPath(contentRootPath);
String rootURI = serverConfiguration.getRootURI();
servletContextHandler.setContextPath(rootURI);
- ServletHolder staticServletHolder = new ServletHolder("static", new CBStaticServlet());
+ ServletHolder staticServletHolder = new ServletHolder(
+ "static", new CBStaticServlet(Path.of(serverConfiguration.getContentRoot()))
+ );
staticServletHolder.setInitParameter("dirAllowed", "false");
- servletContextHandler.addServlet(staticServletHolder, "/*");
+ staticServletHolder.setInitParameter("cacheControl", "public, max-age=" + CBStaticServlet.STATIC_CACHE_SECONDS);
+ servletContextHandler.addServlet(staticServletHolder, "/");
+
+ if (Files.isSymbolicLink(contentRootPath)) {
+ servletContextHandler.addAliasCheck(new CBSymLinkContentAllowedAliasChecker(contentRootPath));
+ }
ServletHolder imagesServletHolder = new ServletHolder("images", new CBImageServlet());
servletContextHandler.addServlet(imagesServletHolder, serverConfiguration.getServicesURI() + "images/*");
@@ -108,33 +117,56 @@ public void runServer() {
servletContextHandler.addServlet(new ServletHolder("status", new CBStatusServlet()), "/status");
servletContextHandler.addServlet(new ServletHolder("graphql", new GraphQLEndpoint()), serverConfiguration.getServicesURI() + "gql/*");
- servletContextHandler.addEventListener(new CBServerContextListener());
+ servletContextHandler.addEventListener(new CBServerContextListener(application));
// Add extensions from services
CBJettyServletContext servletContext = new CBJettyServletContext(servletContextHandler);
- for (DBWServiceBindingServlet wsd : WebServiceRegistry.getInstance().getWebServices(DBWServiceBindingServlet.class)) {
- try {
- wsd.addServlets(this.application, servletContext);
- } catch (DBException e) {
- log.error(e.getMessage(), e);
+ for (DBWServiceBindingServlet wsd : WebServiceRegistry.getInstance()
+ .getWebServices(DBWServiceBindingServlet.class)
+ ) {
+ if (wsd.isApplicable(this.application)) {
+ try {
+ wsd.addServlets(this.application, servletContext);
+ } catch (DBException e) {
+ log.error(e.getMessage(), e);
+ }
}
}
- initSessionManager(this.application, servletContextHandler);
-
- server.setHandler(servletContextHandler);
+ CBJettyWebSocketContext webSocketContext = new CBJettyWebSocketContext(server, servletContextHandler);
+ for (DBWServiceBindingWebSocket wsb : WebServiceRegistry.getInstance()
+ .getWebServices(DBWServiceBindingWebSocket.class)
+ ) {
+ if (wsb.isApplicable(this.application)) {
+ try {
+ wsb.addWebSockets(this.application, webSocketContext);
+ } catch (DBException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+ }
- JettyWebSocketServletContainerInitializer.configure(servletContextHandler,
- (context, wsContainer) -> {
+ WebSocketUpgradeHandler webSocketHandler = WebSocketUpgradeHandler.from(server, servletContextHandler, (wsContainer) -> {
wsContainer.setIdleTimeout(Duration.ofMinutes(5));
// Add websockets
wsContainer.addMapping(
- serverConfiguration.getServicesURI() + "ws/*",
+ serverConfiguration.getServicesURI() + "ws",
new CBJettyWebSocketManager(this.application.getSessionManager())
);
}
);
+ servletContextHandler.insertHandler(webSocketHandler);
+
+ initSessionManager(
+ this.application.getMaxSessionIdleTime(),
+ this.application,
+ server,
+ servletContextHandler
+ );
+
+ server.setHandler(servletContextHandler);
+
ErrorPageErrorHandler errorHandler = new ErrorPageErrorHandler();
//errorHandler.addErrorPage(404, "/missing.html");
servletContextHandler.setErrorHandler(errorHandler);
@@ -144,6 +176,11 @@ public void runServer() {
log.debug("\t" + sm.getServletName() + ": " + Arrays.toString(sm.getPathSpecs())); //$NON-NLS-1$
}
+ log.debug("Active websocket mappings:");
+ for (String mapping : webSocketContext.getMappings()) {
+ log.debug("\t" + mapping);
+ }
+
}
boolean forwardProxy = application.getAppConfiguration().isEnabledForwardProxy();
@@ -161,7 +198,7 @@ public void runServer() {
}
}
}
-
+ refreshJettyConfig();
server.start();
server.join();
} catch (Exception e) {
@@ -179,13 +216,15 @@ private Path getSslConfigurationPath() {
return sslConfiguration.isAbsolute() ? sslConfiguration : application.getHomeDirectory().resolve(sslConfiguration);
}
- private void initSessionManager(
- @NotNull CBApplication> application,
+ public static void initSessionManager(
+ long maxIdleTime,
+ @NotNull GQLApplicationAdapter application,
+ @NotNull Server server,
@NotNull ServletContextHandler servletContextHandler
) {
// Init sessions persistence
- SessionHandler sessionHandler = new SessionHandler();
- var maxIdleTime = application.getMaxSessionIdleTime();
+ CBSessionHandler sessionHandler = new CBSessionHandler(application);
+ sessionHandler.setRefreshCookieAge(CBSessionHandler.ONE_MINUTE);
int intMaxIdleSeconds;
if (maxIdleTime > Integer.MAX_VALUE) {
log.warn("Max session idle time value is greater than Integer.MAX_VALUE. Integer.MAX_VALUE will be used instead");
@@ -194,33 +233,29 @@ private void initSessionManager(
intMaxIdleSeconds = (int) (maxIdleTime / 1000);
log.debug("Max http session idle time: " + intMaxIdleSeconds + "s");
sessionHandler.setMaxInactiveInterval(intMaxIdleSeconds);
+ sessionHandler.setMaxCookieAge(intMaxIdleSeconds);
DefaultSessionCache sessionCache = new DefaultSessionCache(sessionHandler);
sessionCache.setSessionDataStore(new NullSessionDataStore());
sessionHandler.setSessionCache(sessionCache);
-
servletContextHandler.setSessionHandler(sessionHandler);
- }
- public static class JettyServer extends Server {
- public JettyServer(int serverPort) {
- super(serverPort);
- }
+ DefaultSessionIdManager idMgr = new DefaultSessionIdManager(server);
+ idMgr.setWorkerName(null);
+ server.addBean(idMgr, true);
+ }
- public JettyServer() {
- super();
+ public synchronized void refreshJettyConfig() {
+ if (server == null) {
+ return;
}
- public JettyServer(InetSocketAddress addr) {
- super(addr);
- }
-
- @Override
- public void setSessionIdManager(SessionIdManager sessionIdManager) {
- if (sessionIdManager instanceof DefaultSessionIdManager) {
- // Nullify worker name to avoid dummy prefixes in session ID cookie
- ((DefaultSessionIdManager) sessionIdManager).setWorkerName(null);
- }
- super.setSessionIdManager(sessionIdManager);
+ log.info("Refreshing Jetty configuration");
+ if (server.getHandler() instanceof ServletContextHandler servletContextHandler
+ && servletContextHandler.getSessionHandler() instanceof CBSessionHandler cbSessionHandler
+ ) {
+ cbSessionHandler.setMaxCookieAge((int) (application.getMaxSessionIdleTime() / 1000));
+ var serverUrl = this.application.getServerURL();
+ cbSessionHandler.setSecureCookies(serverUrl != null && serverUrl.startsWith("https://"));
}
}
}
\ No newline at end of file
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServletContext.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServletContext.java
index 0579ff22b0..b04374442c 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServletContext.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServletContext.java
@@ -19,8 +19,8 @@
import io.cloudbeaver.service.DBWServletContext;
import jakarta.servlet.http.HttpServlet;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee10.servlet.ServletHolder;
public class CBJettyServletContext implements DBWServletContext {
private final ServletContextHandler contextHandler;
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyWebSocketContext.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyWebSocketContext.java
new file mode 100644
index 0000000000..8a19513a84
--- /dev/null
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyWebSocketContext.java
@@ -0,0 +1,56 @@
+/*
+ * 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.server.jetty;
+
+import io.cloudbeaver.service.DBWWebSocketContext;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.websocket.api.Configurable;
+import org.eclipse.jetty.websocket.server.WebSocketCreator;
+import org.eclipse.jetty.websocket.server.WebSocketUpgradeHandler;
+import org.jkiss.code.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+public class CBJettyWebSocketContext implements DBWWebSocketContext {
+ private final List mappings = new ArrayList<>();
+
+ private final Server server;
+ private final ContextHandler handler;
+
+ public CBJettyWebSocketContext(@NotNull Server server, @NotNull ContextHandler handler) {
+ this.server = server;
+ this.handler = handler;
+ }
+
+ @Override
+ public void addWebSocket(@NotNull String mapping, @NotNull Function configurator) {
+ handler.insertHandler(WebSocketUpgradeHandler.from(
+ server,
+ handler,
+ container -> container.addMapping(mapping, configurator.apply(container))
+ ));
+ mappings.add(mapping);
+ }
+
+ @NotNull
+ public List getMappings() {
+ return mappings;
+ }
+}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBServerContextListener.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBServerContextListener.java
index 9bab018ef3..af6215b0ff 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBServerContextListener.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBServerContextListener.java
@@ -26,17 +26,22 @@ public class CBServerContextListener implements ServletContextListener {
// One week
//private static final int CB_SESSION_LIFE_TIME = 60 * 60 * 24 * 7;
+ private final CBApplication> application;
+
+ public CBServerContextListener(CBApplication> application) {
+ this.application = application;
+ }
public void contextInitialized(ServletContextEvent sce) {
- SessionCookieConfig scf = sce.getServletContext().getSessionCookieConfig();
+ SessionCookieConfig cookieConfig = sce.getServletContext().getSessionCookieConfig();
- scf.setComment("Cloudbeaver Session ID");
+ cookieConfig.setComment("Cloudbeaver Session ID");
//scf.setDomain(domain);
- //scf.setHttpOnly(httpOnly);
//scf.setMaxAge(CB_SESSION_LIFE_TIME);
- scf.setPath(CBApplication.getInstance().getRootURI());
- //scf.setSecure(isSecure);
- scf.setName(CBConstants.CB_SESSION_COOKIE_NAME);
+ cookieConfig.setPath(CBApplication.getInstance().getRootURI());
+// cookieConfig.setSecure(application.getServerURL().startsWith("https"));
+ cookieConfig.setHttpOnly(true);
+ cookieConfig.setName(CBConstants.CB_SESSION_COOKIE_NAME);
}
public void contextDestroyed(ServletContextEvent sce) {
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBSessionHandler.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBSessionHandler.java
new file mode 100644
index 0000000000..b8539264f7
--- /dev/null
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBSessionHandler.java
@@ -0,0 +1,29 @@
+/*
+ * 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.server.jetty;
+
+import io.cloudbeaver.server.GQLApplicationAdapter;
+import org.eclipse.jetty.ee10.servlet.SessionHandler;
+
+public class CBSessionHandler extends SessionHandler {
+ static final int ONE_MINUTE = 60;
+ private final GQLApplicationAdapter application;
+
+ public CBSessionHandler(GQLApplicationAdapter application) {
+ this.application = application;
+ }
+}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBSymLinkContentAllowedAliasChecker.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBSymLinkContentAllowedAliasChecker.java
new file mode 100644
index 0000000000..ecfc108878
--- /dev/null
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBSymLinkContentAllowedAliasChecker.java
@@ -0,0 +1,38 @@
+/*
+ * 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.server.jetty;
+
+import org.eclipse.jetty.server.AliasCheck;
+import org.eclipse.jetty.util.resource.Resource;
+import org.jkiss.code.NotNull;
+
+import java.nio.file.Path;
+
+public class CBSymLinkContentAllowedAliasChecker implements AliasCheck {
+ @NotNull
+ private final Path contentRootPath;
+
+ public CBSymLinkContentAllowedAliasChecker(@NotNull Path contentRootPath) {
+ this.contentRootPath = contentRootPath;
+ }
+
+ @Override
+ public boolean checkAlias(String pathInContext, Resource resource) {
+ Path resourcePath = resource.getPath();
+ return resourcePath != null && resourcePath.startsWith(contentRootPath);
+ }
+}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/PeriodicSystemJob.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/PeriodicSystemJob.java
deleted file mode 100644
index 053749b7d8..0000000000
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/PeriodicSystemJob.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.server.jobs;
-
-import io.cloudbeaver.server.CBPlatform;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
-import org.jkiss.code.NotNull;
-import org.jkiss.dbeaver.model.runtime.AbstractJob;
-import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
-
-public abstract class PeriodicSystemJob extends AbstractJob {
-
- @NotNull
- protected final CBPlatform platform;
- private final long periodMs;
-
- public PeriodicSystemJob(@NotNull String name, @NotNull CBPlatform platform, long periodMs) {
- super(name);
- this.platform = platform;
- this.periodMs = periodMs;
-
- setUser(false);
- setSystem(true);
- }
-
- @Override
- protected IStatus run(@NotNull DBRProgressMonitor monitor) {
- if (platform.isShuttingDown()) {
- return Status.OK_STATUS;
- }
-
- doJob(monitor);
-
- // If the platform is still running after the job is completed, reschedule the job
- if (!platform.isShuttingDown()) {
- scheduleMonitor();
- }
-
- return Status.OK_STATUS;
- }
-
- protected abstract void doJob(@NotNull DBRProgressMonitor monitor);
-
- public void scheduleMonitor() {
- schedule(periodMs);
- }
-}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/SessionStateJob.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/SessionStateJob.java
index 5f2d9a6eaa..01aaffb15a 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/SessionStateJob.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/SessionStateJob.java
@@ -16,23 +16,27 @@
*/
package io.cloudbeaver.server.jobs;
-import io.cloudbeaver.server.CBPlatform;
+import io.cloudbeaver.service.session.WebSessionManager;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
+import org.jkiss.dbeaver.model.app.DBPPlatform;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
+import org.jkiss.dbeaver.model.runtime.PeriodicJob;
-public class SessionStateJob extends PeriodicSystemJob {
+public class SessionStateJob extends PeriodicJob {
private static final Log log = Log.getLog(SessionStateJob.class);
private static final int PERIOD_MS = 30_000; // once per 30 seconds
+ private final WebSessionManager sessionManager;
- public SessionStateJob(@NotNull CBPlatform platform) {
+ public SessionStateJob(@NotNull DBPPlatform platform, WebSessionManager sessionManager) {
super("Session state sender", platform, PERIOD_MS);
+ this.sessionManager = sessionManager;
}
@Override
protected void doJob(@NotNull DBRProgressMonitor monitor) {
try {
- platform.getSessionManager().sendSessionsStates();
+ sessionManager.sendSessionsStates();
} catch (Exception e) {
log.error("Error sending session state", e);
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebDataSourceMonitorJob.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebDataSourceMonitorJob.java
index 5a244b6156..961bf38ce6 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebDataSourceMonitorJob.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebDataSourceMonitorJob.java
@@ -16,9 +16,9 @@
*/
package io.cloudbeaver.server.jobs;
+import io.cloudbeaver.server.AppWebSessionManager;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
-import io.cloudbeaver.server.CBPlatform;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.DBPDataSource;
import org.jkiss.dbeaver.model.app.DBPPlatform;
@@ -34,14 +34,19 @@
* Web data source monitor job.
*/
public class WebDataSourceMonitorJob extends DataSourceMonitorJob {
+ private final AppWebSessionManager sessionManager;
- public WebDataSourceMonitorJob(DBPPlatform platform) {
+ public WebDataSourceMonitorJob(
+ @NotNull DBPPlatform platform,
+ @NotNull AppWebSessionManager sessionManager
+ ) {
super(platform);
+ this.sessionManager = sessionManager;
}
@Override
protected void doJob() {
- Collection allSessions = CBPlatform.getInstance().getSessionManager().getAllActiveSessions();
+ Collection allSessions = sessionManager.getAllActiveSessions();
allSessions.parallelStream().forEach(s -> {
checkDataSourceAliveInWorkspace(s.getWorkspace(), s.getLastAccessTimeMillis());
});
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebSessionMonitorJob.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebSessionMonitorJob.java
index 11fc79c569..e73dacdf62 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebSessionMonitorJob.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebSessionMonitorJob.java
@@ -16,26 +16,30 @@
*/
package io.cloudbeaver.server.jobs;
-import io.cloudbeaver.server.CBPlatform;
+import io.cloudbeaver.service.session.WebSessionManager;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
+import org.jkiss.dbeaver.model.app.DBPPlatform;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
+import org.jkiss.dbeaver.model.runtime.PeriodicJob;
/**
* WebSessionMonitorJob
*/
-public class WebSessionMonitorJob extends PeriodicSystemJob {
+public class WebSessionMonitorJob extends PeriodicJob {
private static final Log log = Log.getLog(WebSessionMonitorJob.class);
private static final int MONITOR_INTERVAL = 10000; // once per 10 seconds
+ private final WebSessionManager sessionManager;
- public WebSessionMonitorJob(@NotNull CBPlatform platform) {
+ public WebSessionMonitorJob(@NotNull DBPPlatform platform, @NotNull WebSessionManager sessionManager) {
super("Web session monitor", platform, MONITOR_INTERVAL);
+ this.sessionManager = sessionManager;
}
@Override
protected void doJob(@NotNull DBRProgressMonitor monitor) {
try {
- platform.getSessionManager().expireIdleSessions();
+ sessionManager.expireIdleSessions();
} catch (Exception e) {
log.error("Error on expire idle sessions", e);
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/servlets/CBStaticServlet.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/servlets/CBStaticServlet.java
index 11ad45034e..232d57f3f9 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/servlets/CBStaticServlet.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/servlets/CBStaticServlet.java
@@ -20,26 +20,23 @@
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.auth.CBAuthConstants;
import io.cloudbeaver.auth.SMAuthProviderFederated;
+import io.cloudbeaver.model.config.CBAppConfig;
+import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.model.session.WebActionParameters;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import io.cloudbeaver.registry.WebHandlerRegistry;
import io.cloudbeaver.registry.WebServletHandlerDescriptor;
-import io.cloudbeaver.server.CBAppConfig;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBPlatform;
-import io.cloudbeaver.server.CBServerConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.http.HttpContent;
-import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.ee10.servlet.DefaultServlet;
import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.server.ResourceService;
-import org.eclipse.jetty.servlet.DefaultServlet;
-import org.eclipse.jetty.util.resource.Resource;
+import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.auth.SMAuthInfo;
@@ -48,9 +45,13 @@
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.IOUtils;
-import java.io.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
-import java.util.Enumeration;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Map;
@WebServlet(urlPatterns = "/")
@@ -62,8 +63,11 @@ public class CBStaticServlet extends DefaultServlet {
private static final Log log = Log.getLog(CBStaticServlet.class);
- public CBStaticServlet() {
- super(makeResourceService());
+ @NotNull
+ private final Path contentRoot;
+
+ public CBStaticServlet(@NotNull Path contentRoot) {
+ this.contentRoot = contentRoot;
}
@Override
@@ -95,7 +99,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
} catch (DBWebException e) {
log.error("Error reading websession", e);
}
- super.doGet(request, response);
+ patchStaticContentIfNeeded(request, response);
}
private void performAutoLoginIfNeeded(HttpServletRequest request, WebSession webSession) {
@@ -189,46 +193,40 @@ private boolean processSessionStart(HttpServletRequest request, HttpServletRespo
return false;
}
- private static ResourceService makeResourceService() {
- ResourceService resourceService = new ProxyResourceService();
- resourceService.setCacheControl(new HttpField(HttpHeader.CACHE_CONTROL, "public, max-age=" + STATIC_CACHE_SECONDS));
- return resourceService;
- }
-
+ private void patchStaticContentIfNeeded(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ String pathInContext = request.getServletPath();
- private static class ProxyResourceService extends ResourceService {
- @Override
- protected boolean sendData(HttpServletRequest request, HttpServletResponse response, boolean include, HttpContent content, Enumeration reqRanges) throws IOException {
- String resourceName = content.getResource().getName();
- if (resourceName.endsWith("index.html") || resourceName.endsWith("sso.html")) {
- return patchIndexHtml(response, content);
- }
- return super.sendData(request, response, include, content, reqRanges);
+ if ("/".equals(pathInContext)) {
+ pathInContext = "index.html";
}
- private boolean patchIndexHtml(HttpServletResponse response, HttpContent content) throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- Resource resource = content.getResource();
- File file = resource.getFile();
- try (InputStream fis = new FileInputStream(file)) {
- IOUtils.copyStream(fis, baos);
- }
- String indexContents = baos.toString(StandardCharsets.UTF_8);
- CBServerConfig serverConfig = CBApplication.getInstance().getServerConfiguration();
- indexContents = indexContents
- .replace("{ROOT_URI}", serverConfig.getRootURI())
- .replace("{STATIC_CONTENT}", serverConfig.getStaticContent());
- byte[] indexBytes = indexContents.getBytes(StandardCharsets.UTF_8);
-
- putHeaders(response, content, indexBytes.length);
- // Disable cache for index.html
- response.setHeader(HttpHeader.CACHE_CONTROL.toString(), "no-cache, no-store, must-revalidate");
- response.setHeader(HttpHeader.EXPIRES.toString(), "0");
-
- response.getOutputStream().write(indexBytes);
+ if (pathInContext == null || !pathInContext.endsWith("index.html")
+ && !pathInContext.endsWith("sso.html")
+ && !pathInContext.endsWith("ssoError.html")
+ ) {
+ super.doGet(request, response);
+ return;
+ }
- return true;
+ if (pathInContext.startsWith("/")) {
+ pathInContext = pathInContext.substring(1);
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ var filePath = contentRoot.resolve(pathInContext);
+ try (InputStream fis = Files.newInputStream(filePath)) {
+ IOUtils.copyStream(fis, baos);
}
+ String indexContents = baos.toString(StandardCharsets.UTF_8);
+ CBServerConfig serverConfig = CBApplication.getInstance().getServerConfiguration();
+ indexContents = indexContents
+ .replace("{ROOT_URI}", serverConfig.getRootURI())
+ .replace("{STATIC_CONTENT}", serverConfig.getStaticContent());
+ byte[] indexBytes = indexContents.getBytes(StandardCharsets.UTF_8);
+
+ // Disable cache for index.html
+ response.setHeader(HttpHeader.CACHE_CONTROL.toString(), "no-cache, no-store, must-revalidate");
+ response.setHeader(HttpHeader.EXPIRES.toString(), "0");
+ response.getOutputStream().write(ByteBuffer.wrap(indexBytes));
}
}
\ No newline at end of file
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/servlets/CBStatusServlet.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/servlets/CBStatusServlet.java
index ceb4fdf748..78cd34091c 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/servlets/CBStatusServlet.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/servlets/CBStatusServlet.java
@@ -19,12 +19,12 @@
import com.google.gson.stream.JsonWriter;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBConstants;
+import io.cloudbeaver.server.CBPlatform;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.servlet.DefaultServlet;
-import org.jkiss.dbeaver.DBException;
+import org.eclipse.jetty.ee10.servlet.DefaultServlet;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.utils.GeneralUtils;
@@ -46,7 +46,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
infoMap.put("health", "ok");
infoMap.put("product.name", GeneralUtils.getProductName());
infoMap.put("product.version", GeneralUtils.getProductVersion().toString());
- CBApplication.getInstance().getStatusInfo(infoMap);
+ CBPlatform.getInstance().getApplication().getStatusInfo(infoMap);
try (JsonWriter writer = new JsonWriter(response.getWriter())) {
JSONUtils.serializeMap(writer, infoMap);
}
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 807814ea19..2dac868a86 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
@@ -17,29 +17,31 @@
package io.cloudbeaver.server.websockets;
import com.google.gson.Gson;
-import org.eclipse.jetty.websocket.api.WebSocketAdapter;
+import org.eclipse.jetty.websocket.api.Callback;
+import org.eclipse.jetty.websocket.api.Session;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.websocket.WSUtils;
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
-import java.io.IOException;
-
-public class CBAbstractWebSocket extends WebSocketAdapter {
+public class CBAbstractWebSocket extends Session.Listener.AbstractAutoDemanding {
private static final Log log = Log.getLog(CBAbstractWebSocket.class);
protected static final Gson gson = WSUtils.gson;
public void handleEvent(WSEvent event) {
- if (isNotConnected()) {
+ if (!isOpen()) {
return;
}
- try {
- getRemote().sendString(gson.toJson(event));
- } catch (IOException e) {
- handleEventException(e);
- }
+ Session session = getSession();
+ session.sendText(gson.toJson(event), new Callback() {
+ @Override
+ public void fail(Throwable e) {
+ handleEventException(e);
+ }
+ });
+
}
- protected void handleEventException(Exception e) {
+ protected void handleEventException(Throwable e) {
log.error("Failed to send websocket message", e);
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsWebSocket.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsWebSocket.java
index 697011c5d4..b99ff76e93 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsWebSocket.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsWebSocket.java
@@ -20,8 +20,8 @@
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.websocket.CBWebSessionEventHandler;
+import org.eclipse.jetty.websocket.api.Callback;
import org.eclipse.jetty.websocket.api.Session;
-import org.eclipse.jetty.websocket.api.WriteCallback;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
@@ -36,7 +36,7 @@ public class CBEventsWebSocket extends CBAbstractWebSocket implements CBWebSessi
@NotNull
private final BaseWebSession webSession;
@NotNull
- private final WriteCallback callback;
+ private final Callback callback;
public CBEventsWebSocket(@NotNull BaseWebSession webSession) {
this.webSession = webSession;
@@ -45,8 +45,8 @@ public CBEventsWebSocket(@NotNull BaseWebSession webSession) {
}
@Override
- public void onWebSocketConnect(Session session) {
- super.onWebSocketConnect(session);
+ public void onWebSocketOpen(Session session) {
+ super.onWebSocketOpen(session);
this.webSession.addEventHandler(this);
handleEvent(new WSSocketConnectedEvent(webSession.getApplication().getApplicationRunId()));
log.debug("EventWebSocket connected to the " + webSession.getSessionId() + " session");
@@ -109,7 +109,7 @@ public void handleWebSessionEvent(WSEvent event) {
super.handleEvent(event);
}
@Override
- protected void handleEventException(Exception e) {
+ protected void handleEventException(Throwable e) {
super.handleEventException(e);
webSession.addSessionError(e);
}
@@ -120,7 +120,7 @@ public BaseWebSession getWebSession() {
}
@NotNull
- public WriteCallback getCallback() {
+ public Callback getCallback() {
return callback;
}
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBExpiredSessionWebSocket.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBExpiredSessionWebSocket.java
index ec294713eb..643197d7e3 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBExpiredSessionWebSocket.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBExpiredSessionWebSocket.java
@@ -21,8 +21,8 @@
public class CBExpiredSessionWebSocket extends CBAbstractWebSocket {
@Override
- public void onWebSocketConnect(Session session) {
- super.onWebSocketConnect(session);
+ public void onWebSocketOpen(Session session) {
+ super.onWebSocketOpen(session);
handleEvent(new WSAccessTokenExpiredEvent());
close();
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java
index 02c730dd10..06cd85f1c1 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java
@@ -16,20 +16,21 @@
*/
package io.cloudbeaver.server.websockets;
+import io.cloudbeaver.server.AppWebSessionManager;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebHeadlessSession;
+import io.cloudbeaver.model.session.WebHttpRequestInfo;
import io.cloudbeaver.server.CBPlatform;
-import io.cloudbeaver.service.session.WebSessionManager;
-import jakarta.servlet.http.HttpServletRequest;
-import org.eclipse.jetty.websocket.server.JettyServerUpgradeRequest;
-import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse;
-import org.eclipse.jetty.websocket.server.JettyWebSocketCreator;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.websocket.server.ServerUpgradeRequest;
+import org.eclipse.jetty.websocket.server.ServerUpgradeResponse;
+import org.eclipse.jetty.websocket.server.WebSocketCreator;
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.security.exception.SMAccessTokenExpiredException;
-import org.jkiss.dbeaver.runtime.DBWorkbench;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@@ -38,12 +39,12 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
-public class CBJettyWebSocketManager implements JettyWebSocketCreator {
+public class CBJettyWebSocketManager implements WebSocketCreator {
private static final Log log = Log.getLog(CBJettyWebSocketManager.class);
private final Map> socketBySessionId = new ConcurrentHashMap<>();
- private final WebSessionManager webSessionManager;
+ private final AppWebSessionManager webSessionManager;
- public CBJettyWebSocketManager(@NotNull WebSessionManager webSessionManager) {
+ public CBJettyWebSocketManager(@NotNull AppWebSessionManager webSessionManager) {
this.webSessionManager = webSessionManager;
new WebSocketPingPongJob(CBPlatform.getInstance(), this).scheduleMonitor();
@@ -51,17 +52,22 @@ public CBJettyWebSocketManager(@NotNull WebSessionManager webSessionManager) {
@Nullable
@Override
- public Object createWebSocket(@NotNull JettyServerUpgradeRequest request, JettyServerUpgradeResponse resp) {
- var httpRequest = request.getHttpServletRequest();
- var webSession = webSessionManager.getOrRestoreSession(httpRequest);
+ public Object createWebSocket(@NotNull ServerUpgradeRequest request, ServerUpgradeResponse resp, Callback callback) {
+ var webSession = webSessionManager.getOrRestoreSession(request);
+ var requestInfo = new WebHttpRequestInfo(
+ request.getId(),
+ request.getAttribute("locale"),
+ Request.getRemoteAddr(request),
+ request.getHeaders().get("User-Agent")
+ );
if (webSession != null) {
- webSession.updateSessionParameters(httpRequest);
+ webSession.updateSessionParameters(requestInfo);
// web client session
return createNewEventsWebSocket(webSession);
}
// possible desktop client session
try {
- var headlessSession = createHeadlessSession(httpRequest);
+ var headlessSession = createHeadlessSession(request);
if (headlessSession == null) {
log.debug("Couldn't create headless session");
return null;
@@ -86,21 +92,21 @@ private CBEventsWebSocket createNewEventsWebSocket(@NotNull BaseWebSession webSe
}
@Nullable
- private WebHeadlessSession createHeadlessSession(@NotNull HttpServletRequest request) throws DBException {
- var httpSession = request.getSession(false);
- if (httpSession == null) {
+ private WebHeadlessSession createHeadlessSession(@NotNull Request request) throws DBException {
+ var requestSession = request.getSession(false);
+ if (requestSession == null) {
log.debug("CloudBeaver web session not exist, try to create headless session");
} else {
- log.debug("CloudBeaver session not found with id " + httpSession.getId() + ", try to create headless session");
+ log.debug("CloudBeaver session not found with id " + requestSession.getId() + ", try to create headless session");
}
- return webSessionManager.getHeadlessSession(request, true);
+ return webSessionManager.getHeadlessSession(request, requestSession, true);
}
public void sendPing() {
//remove expired sessions
socketBySessionId.entrySet()
.removeIf(entry -> {
- entry.getValue().removeIf(ws -> !ws.isConnected());
+ entry.getValue().removeIf(ws -> !ws.isOpen());
return webSessionManager.getSession(entry.getKey()) == null ||
entry.getValue().isEmpty();
}
@@ -115,7 +121,7 @@ public void sendPing() {
var webSockets = entry.getValue();
for (CBEventsWebSocket webSocket : webSockets) {
try {
- webSocket.getRemote().sendPing(
+ webSocket.getSession().sendPing(
ByteBuffer.wrap("cb-ping".getBytes(StandardCharsets.UTF_8)),
webSocket.getCallback()
);
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/WebSocketPingPongCallback.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/WebSocketPingPongCallback.java
index 8530e963c1..b19741df9a 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/WebSocketPingPongCallback.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/WebSocketPingPongCallback.java
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
- * Copyright (C) 2010-2022 DBeaver Corp and others
+ * 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.
@@ -18,10 +18,10 @@
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebHeadlessSession;
-import org.eclipse.jetty.websocket.api.WriteCallback;
+import org.eclipse.jetty.websocket.api.Callback;
import org.jkiss.code.NotNull;
-public class WebSocketPingPongCallback implements WriteCallback {
+public class WebSocketPingPongCallback implements Callback {
@NotNull
private final BaseWebSession webSession;
@@ -30,7 +30,7 @@ public WebSocketPingPongCallback(@NotNull BaseWebSession webSession) {
}
@Override
- public void writeSuccess() {
+ public void succeed() {
if (webSession instanceof WebHeadlessSession) {
webSession.touchSession();
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/DBWServiceBindingWebSocket.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/DBWServiceBindingWebSocket.java
new file mode 100644
index 0000000000..9d62ef7a5f
--- /dev/null
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/DBWServiceBindingWebSocket.java
@@ -0,0 +1,29 @@
+/*
+ * 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.service;
+
+import io.cloudbeaver.model.app.WebApplication;
+import org.jkiss.code.NotNull;
+import org.jkiss.dbeaver.DBException;
+
+public interface DBWServiceBindingWebSocket extends DBWServiceBinding {
+ default boolean isApplicable(@NotNull WebApplication application) {
+ return true;
+ }
+
+ void addWebSockets(@NotNull APPLICATION application, @NotNull DBWWebSocketContext context) throws DBException;
+}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/DBWWebSocketContext.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/DBWWebSocketContext.java
new file mode 100644
index 0000000000..4da0e61f05
--- /dev/null
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/DBWWebSocketContext.java
@@ -0,0 +1,28 @@
+/*
+ * 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.service;
+
+import org.eclipse.jetty.websocket.api.Configurable;
+import org.eclipse.jetty.websocket.server.WebSocketCreator;
+import org.jkiss.code.NotNull;
+import org.jkiss.dbeaver.DBException;
+
+import java.util.function.Function;
+
+public interface DBWWebSocketContext {
+ void addWebSocket(@NotNull String mapping, @NotNull Function configurator) throws DBException;
+}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java
index 7a85a39e97..e6609823b2 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java
@@ -27,6 +27,7 @@
import io.cloudbeaver.server.CBPlatform;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
import io.cloudbeaver.service.security.SMUtils;
+import io.cloudbeaver.utils.WebDataSourceUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jkiss.code.NotNull;
@@ -136,7 +137,7 @@ public static WebSession findWebSession(DataFetchingEnvironment env, boolean err
@NotNull
public static WebConnectionInfo getWebConnection(WebSession session, String projectId, String connectionId) throws DBWebException {
- return session.getWebConnectionInfo(projectId, connectionId);
+ return WebDataSourceUtils.getWebConnectionInfo(session, projectId, connectionId);
}
private class ServiceInvocationHandler implements InvocationHandler {
@@ -229,7 +230,7 @@ private void checkObjectActionPermissions(Method method, WebProjectAction object
if (project == null) {
throw new DBException("Project not found:" + projectId);
}
- RMProject rmProject = project.getRmProject();
+ RMProject rmProject = project.getRMProject();
for (String reqProjectPermission : requireProjectPermissions) {
if (!rmProject.hasProjectPermission(reqProjectPermission)) {
@@ -250,7 +251,7 @@ private void checkServicePermissions(Method method, WebActionSet actionSet) thro
}
private void checkActionPermissions(@NotNull Method method, @NotNull WebAction webAction) throws DBWebException {
- CBApplication> application = CBApplication.getInstance();
+ var application = CBPlatform.getInstance().getApplication();
if (application.isInitializationMode() && webAction.initializationRequired()) {
String message = "Server initialization in progress: "
+ String.join(",", application.getInitActions().values()) + ".\nDo not restart the server.";
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceServletBase.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceServletBase.java
index c6644a5223..3768ff0617 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceServletBase.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceServletBase.java
@@ -1,7 +1,24 @@
+/*
+ * 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.service;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
+import io.cloudbeaver.model.app.WebApplication;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBPlatform;
@@ -27,13 +44,13 @@ public abstract class WebServiceServletBase extends HttpServlet {
.setPrettyPrinting()
.create();
- private final CBApplication application;
+ private final WebApplication application;
- public WebServiceServletBase(CBApplication application) {
+ public WebServiceServletBase(WebApplication application) {
this.application = application;
}
- public CBApplication getApplication() {
+ public WebApplication getApplication() {
return application;
}
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 a04c44847f..ea6b0264b8 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
@@ -29,7 +29,6 @@
import io.cloudbeaver.service.DBWBindingContext;
import io.cloudbeaver.service.WebServiceBindingBase;
import io.cloudbeaver.service.core.impl.WebServiceCore;
-import io.cloudbeaver.service.session.WebSessionManager;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -50,7 +49,7 @@ public WebServiceBindingCore() {
@Override
public void bindWiring(DBWBindingContext model) throws DBWebException {
CBPlatform platform = CBPlatform.getInstance();
- WebSessionManager sessionManager = platform.getSessionManager();
+ var sessionManager = platform.getSessionManager();
model.getQueryType()
.dataFetcher("serverConfig", env -> getService(env).getServerConfig())
.dataFetcher("productSettings", env -> getService(env).getProductSettings(getWebSession(env)))
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 f77c7cf6ab..77a5a40fc7 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
@@ -17,10 +17,7 @@
package io.cloudbeaver.service.core.impl;
-import io.cloudbeaver.DBWConstants;
-import io.cloudbeaver.DBWebException;
-import io.cloudbeaver.WebProjectImpl;
-import io.cloudbeaver.WebServiceUtils;
+import io.cloudbeaver.*;
import io.cloudbeaver.model.*;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.registry.WebHandlerRegistry;
@@ -29,7 +26,7 @@
import io.cloudbeaver.server.CBPlatform;
import io.cloudbeaver.service.core.DBWServiceCore;
import io.cloudbeaver.service.security.SMUtils;
-import io.cloudbeaver.service.session.WebSessionManager;
+import io.cloudbeaver.utils.WebAppUtils;
import io.cloudbeaver.utils.WebConnectionFolderUtils;
import io.cloudbeaver.utils.WebDataSourceUtils;
import io.cloudbeaver.utils.WebEventUtils;
@@ -118,15 +115,17 @@ public List getUserConnections(
return Collections.singletonList(connectionInfo);
}
}
- var stream = webSession.getConnections().stream();
+ var stream = webSession.getAccessibleProjects().stream();
if (projectId != null) {
- stream = stream.filter(c -> c.getProjectId().equals(projectId));
+ stream = stream.filter(c -> c.getId().equals(projectId));
}
if (projectIds != null) {
- stream = stream.filter(c -> projectIds.contains(c.getProjectId()));
+ stream = stream.filter(c -> projectIds.contains(c.getId()));
}
Set applicableDrivers = WebServiceUtils.getApplicableDriversIds();
- return stream.filter(c -> applicableDrivers.contains(c.getDataSourceContainer().getDriver().getId()))
+ return stream
+ .flatMap(p -> p.getConnections().stream())
+ .filter(c -> applicableDrivers.contains(c.getDataSourceContainer().getDriver().getId()))
.toList();
}
@@ -154,27 +153,30 @@ public List getTemplateDataSources() throws DBWebException
public List getTemplateConnections(
@NotNull WebSession webSession, @Nullable String projectId
) throws DBWebException {
+ if (webSession.getApplication().isDistributed()) {
+ return List.of();
+ }
List result = new ArrayList<>();
if (projectId == null) {
- for (DBPProject project : webSession.getAccessibleProjects()) {
+ for (WebSessionProjectImpl project : webSession.getAccessibleProjects()) {
getTemplateConnectionsFromProject(webSession, project, result);
}
} else {
- DBPProject project = getProjectById(webSession, projectId);
+ WebSessionProjectImpl project = getProjectById(webSession, projectId);
getTemplateConnectionsFromProject(webSession, project, result);
}
- webSession.filterAccessibleConnections(result);
return result;
}
private void getTemplateConnectionsFromProject(
@NotNull WebSession webSession,
- @NotNull DBPProject project,
+ @NotNull WebSessionProjectImpl project,
List result
) {
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
for (DBPDataSourceContainer ds : registry.getDataSources()) {
if (ds.isTemplate() &&
+ project.getDataSourceFilter().filter(ds) &&
CBPlatform.getInstance().getApplicableDrivers().contains(ds.getDriver())) {
result.add(new WebConnectionInfo(webSession, ds));
}
@@ -208,7 +210,7 @@ private List getConnectionFoldersFromProject(
@Override
public String[] getSessionPermissions(@NotNull WebSession webSession) throws DBWebException {
- if (CBApplication.getInstance().isConfigurationMode()) {
+ if (WebAppUtils.getWebApplication().isConfigurationMode()) {
return new String[]{
DBWConstants.PERMISSION_ADMIN
};
@@ -291,7 +293,7 @@ public boolean touchSession(@NotNull HttpServletRequest request, @NotNull HttpSe
@Deprecated
public WebSession updateSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response)
throws DBWebException {
- WebSessionManager sessionManager = CBPlatform.getInstance().getSessionManager();
+ var sessionManager = CBPlatform.getInstance().getSessionManager();
sessionManager.touchSession(request, response);
return sessionManager.getWebSession(request, response, true);
}
@@ -317,9 +319,11 @@ public boolean changeSessionLanguage(@NotNull WebSession webSession, String loca
@Override
public WebConnectionInfo getConnectionState(
- WebSession webSession, @Nullable String projectId, String connectionId
+ @NotNull WebSession webSession,
+ @Nullable String projectId,
+ @NotNull String connectionId
) throws DBWebException {
- return webSession.getWebConnectionInfo(projectId, connectionId);
+ return WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, connectionId);
}
@@ -334,7 +338,7 @@ public WebConnectionInfo initConnection(
@Nullable Boolean sharedCredentials,
@Nullable String selectedSecretId
) throws DBWebException {
- WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(projectId, connectionId);
+ WebConnectionInfo connectionInfo = WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, connectionId);
connectionInfo.setSavedCredentials(authProperties, networkCredentials);
var dataSourceContainer = (DataSourceDescriptor) connectionInfo.getDataSourceContainer();
@@ -430,11 +434,11 @@ public WebConnectionInfo createConnection(
@Nullable String projectId,
@NotNull WebConnectionConfig connectionConfig
) throws DBWebException {
- var project = getProjectById(webSession, projectId);
- var rmProject = project.getRmProject();
+ WebSessionProjectImpl project = getProjectById(webSession, projectId);
+ var rmProject = project.getRMProject();
if (rmProject.getType() == RMProjectType.USER
&& !webSession.hasPermission(DBWConstants.PERMISSION_ADMIN)
- && !CBApplication.getInstance().getAppConfiguration().isSupportsCustomConnections()
+ && !WebAppUtils.getWebApplication().getAppConfiguration().isSupportsCustomConnections()
) {
throw new DBWebException("New connection create is restricted by server configuration");
}
@@ -460,8 +464,7 @@ public WebConnectionInfo createConnection(
throw new DBWebException("Failed to create connection", e);
}
- WebConnectionInfo connectionInfo = new WebConnectionInfo(webSession, newDataSource);
- webSession.addConnection(connectionInfo);
+ WebConnectionInfo connectionInfo = project.addConnection(newDataSource);
webSession.addInfoMessage("New connection was created - " + WebServiceUtils.getConnectionContainerInfo(
newDataSource));
WebEventUtils.addDataSourceUpdatedEvent(
@@ -485,9 +488,8 @@ public WebConnectionInfo updateConnection(
// if (!CBApplication.getInstance().getAppConfiguration().isSupportsCustomConnections()) {
// throw new DBWebException("Connection edit is restricted by server configuration");
// }
- DBPDataSourceRegistry sessionRegistry = getProjectById(webSession, projectId).getDataSourceRegistry();
- WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(projectId, config.getConnectionId());
+ WebConnectionInfo connectionInfo = WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, config.getConnectionId());
DBPDataSourceContainer dataSource = connectionInfo.getDataSourceContainer();
webSession.addInfoMessage("Update connection - " + WebServiceUtils.getConnectionContainerInfo(dataSource));
var oldDataSource = new DataSourceDescriptor((DataSourceDescriptor) dataSource, dataSource.getRegistry());
@@ -500,6 +502,8 @@ public WebConnectionInfo updateConnection(
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());
@@ -576,7 +580,7 @@ private WSDataSourceProperty getDatasourceEventProperty(
public boolean deleteConnection(
@NotNull WebSession webSession, @Nullable String projectId, @NotNull String connectionId
) throws DBWebException {
- WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(projectId, connectionId);
+ 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");
}
@@ -600,7 +604,8 @@ public WebConnectionInfo createConnectionFromTemplate(
@NotNull String templateId,
@Nullable String connectionName
) throws DBWebException {
- DBPDataSourceRegistry templateRegistry = getProjectById(webSession, projectId).getDataSourceRegistry();
+ WebSessionProjectImpl project = getProjectById(webSession, projectId);
+ DBPDataSourceRegistry templateRegistry = project.getDataSourceRegistry();
DBPDataSourceContainer dataSourceTemplate = templateRegistry.getDataSource(templateId);
if (dataSourceTemplate == null) {
throw new DBWebException("Template data source '" + templateId + "' not found");
@@ -623,9 +628,7 @@ public WebConnectionInfo createConnectionFromTemplate(
throw new DBWebException(e.getMessage(), e);
}
- WebConnectionInfo connectionInfo = new WebConnectionInfo(webSession, newDataSource);
- webSession.addConnection(connectionInfo);
- return connectionInfo;
+ return project.addConnection(newDataSource);
}
@Override
@@ -637,7 +640,8 @@ public WebConnectionInfo copyConnectionFromNode(
) throws DBWebException {
try {
DBNModel navigatorModel = webSession.getNavigatorModel();
- DBPDataSourceRegistry dataSourceRegistry = getProjectById(webSession, projectId).getDataSourceRegistry();
+ WebSessionProjectImpl project = getProjectById(webSession, projectId);
+ DBPDataSourceRegistry dataSourceRegistry = project.getDataSourceRegistry();
DBNNode srcNode = navigatorModel.getNodeByPath(webSession.getProgressMonitor(), nodePath);
if (srcNode == null) {
@@ -663,9 +667,8 @@ public WebConnectionInfo copyConnectionFromNode(
dataSourceRegistry.addDataSource(newDataSource);
- WebConnectionInfo connectionInfo = new WebConnectionInfo(webSession, newDataSource);
dataSourceRegistry.checkForErrors();
- webSession.addConnection(connectionInfo);
+ WebConnectionInfo connectionInfo = project.addConnection(newDataSource);
WebEventUtils.addDataSourceUpdatedEvent(
webSession.getProjectById(projectId),
webSession,
@@ -688,7 +691,7 @@ public WebConnectionInfo testConnection(
connectionConfig.setSaveCredentials(true); // It is used in createConnectionFromConfig
DataSourceDescriptor dataSource = (DataSourceDescriptor) WebDataSourceUtils.getLocalOrGlobalDataSource(
- CBApplication.getInstance(), webSession, projectId, connectionId);
+ webSession, projectId, connectionId);
WebProjectImpl project = getProjectById(webSession, projectId);
DBPDataSourceRegistry sessionRegistry = project.getDataSourceRegistry();
@@ -824,12 +827,13 @@ private WebConnectionInfo closeAndDeleteConnection(
@NotNull String connectionId,
boolean forceDelete
) throws DBWebException {
- WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(projectId, connectionId);
+ WebSessionProjectImpl project = getProjectById(webSession, projectId);
+ WebConnectionInfo connectionInfo = project.getWebConnectionInfo(connectionId);
DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer();
boolean disconnected = WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer);
if (forceDelete) {
- DBPDataSourceRegistry registry = getProjectById(webSession, projectId).getDataSourceRegistry();
+ DBPDataSourceRegistry registry = project.getDataSourceRegistry();
registry.removeDataSource(dataSourceContainer);
try {
registry.checkForErrors();
@@ -841,7 +845,7 @@ private WebConnectionInfo closeAndDeleteConnection(
}
throw new DBWebException("Failed to delete connection", e);
}
- webSession.removeConnection(connectionInfo);
+ project.removeConnection(dataSourceContainer);
} else {
// Just reset saved credentials
connectionInfo.clearCache();
@@ -854,7 +858,7 @@ private WebConnectionInfo closeAndDeleteConnection(
@Override
public List getProjects(@NotNull WebSession session) {
var customConnectionsEnabled =
- CBApplication.getInstance().getAppConfiguration().isSupportsCustomConnections()
+ WebAppUtils.getWebApplication().getAppConfiguration().isSupportsCustomConnections()
|| SMUtils.isRMAdmin(session);
return session.getAccessibleProjects().stream()
.map(pr -> new WebProjectInfo(session, pr, customConnectionsEnabled))
@@ -955,7 +959,7 @@ public boolean deleteConnectionFolder(
public WebConnectionInfo setConnectionNavigatorSettings(
WebSession webSession, @Nullable String projectId, String id, DBNBrowseSettings settings
) throws DBWebException {
- WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(projectId, id);
+ WebConnectionInfo connectionInfo = WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, id);
DataSourceDescriptor dataSourceDescriptor = ((DataSourceDescriptor) connectionInfo.getDataSourceContainer());
dataSourceDescriptor.setNavigatorSettings(settings);
dataSourceDescriptor.persistConfiguration();
@@ -984,8 +988,8 @@ public WebProductSettings getProductSettings(@NotNull WebSession webSession) {
return new WebProductSettings(webSession, ProductSettingsRegistry.getInstance().getSettings());
}
- private WebProjectImpl getProjectById(WebSession webSession, String projectId) throws DBWebException {
- WebProjectImpl project = webSession.getProjectById(projectId);
+ private WebSessionProjectImpl getProjectById(WebSession webSession, String projectId) throws DBWebException {
+ WebSessionProjectImpl project = webSession.getProjectById(projectId);
if (project == null) {
throw new DBWebException("Project '" + projectId + "' not found");
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/WebDatabaseObjectInfo.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/WebDatabaseObjectInfo.java
index 7d251b5755..7fe7cd1d01 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/WebDatabaseObjectInfo.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/WebDatabaseObjectInfo.java
@@ -16,15 +16,17 @@
*/
package io.cloudbeaver.service.navigator;
+import io.cloudbeaver.WebProjectImpl;
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.model.WebPropertyInfo;
import io.cloudbeaver.model.session.WebSession;
+import io.cloudbeaver.service.security.SMUtils;
+import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.*;
import org.jkiss.dbeaver.model.meta.Property;
-import org.jkiss.dbeaver.model.navigator.DBNDataSource;
-import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
+import org.jkiss.dbeaver.model.rm.RMProjectPermission;
import org.jkiss.dbeaver.model.struct.*;
import org.jkiss.dbeaver.model.struct.rdb.DBSCatalog;
import org.jkiss.dbeaver.model.struct.rdb.DBSSchema;
@@ -91,9 +93,22 @@ public WebPropertyInfo[] getProperties() {
@Property
public WebPropertyInfo[] filterProperties(@Nullable WebPropertyFilter filter) {
+ if (object instanceof DBPDataSourceContainer container && !isDataSourceEditable(container)) {
+ // If user cannot edit a connection, then return only name
+ filter = new WebPropertyFilter();
+ filter.setFeatures(List.of(DBConstants.PROP_FEATURE_NAME));
+ }
return WebServiceUtils.getObjectFilteredProperties(session, object, filter);
}
+ private boolean isDataSourceEditable(@NotNull DBPDataSourceContainer container) {
+ WebProjectImpl project = session.getProjectById(container.getProject().getId());
+ if (project == null) {
+ return false;
+ }
+ return SMUtils.hasProjectPermission(session, project.getRMProject(), RMProjectPermission.DATA_SOURCES_EDIT);
+ }
+
///////////////////////////////////
// Advanced
@@ -183,8 +198,7 @@ private void getObjectFeatures(DBSObject object, List features) {
features.add(OBJECT_FEATURE_OBJECT_CONTAINER);
try {
Class extends DBSObject> childType = objectContainer.getPrimaryChildType(null);
- Collection extends DBSObject> childrenCollection = objectContainer.getChildren(session.getProgressMonitor());
- if (DBSTable.class.isAssignableFrom(childType) && childrenCollection != null) {
+ if (DBSTable.class.isAssignableFrom(childType)) {
features.add(OBJECT_FEATURE_ENTITY_CONTAINER);
}
} catch (Exception e) {
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/WebNavigatorNodeInfo.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/WebNavigatorNodeInfo.java
index 6f22110e9f..511b0743f6 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/WebNavigatorNodeInfo.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/WebNavigatorNodeInfo.java
@@ -36,8 +36,6 @@
import org.jkiss.dbeaver.model.navigator.*;
import org.jkiss.dbeaver.model.navigator.fs.DBNFileSystem;
import org.jkiss.dbeaver.model.navigator.fs.DBNPathBase;
-import org.jkiss.dbeaver.model.navigator.meta.DBXTreeFolder;
-import org.jkiss.dbeaver.model.navigator.meta.DBXTreeItem;
import org.jkiss.dbeaver.model.navigator.meta.DBXTreeNode;
import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
@@ -113,7 +111,7 @@ public String getPlainName() { // for renaming node
@Property
public String getProjectId() {
- DBPProject ownerProject = node.getOwnerProject();
+ DBPProject ownerProject = node.getOwnerProjectOrNull();
return ownerProject == null ? null : ownerProject.getId();
}
@@ -121,11 +119,11 @@ public String getProjectId() {
@Deprecated
public String getFullName() {
String nodeName;
- if (node instanceof DBNDatabaseNode && !(node instanceof DBNDataSource)) {
- DBSObject object = ((DBNDatabaseNode) node).getObject();
+ if (node instanceof DBNDatabaseNode dbNode && !(node instanceof DBNDataSource)) {
+ DBSObject object = dbNode.getObject();
nodeName = DBUtils.getObjectFullName(object, DBPEvaluationContext.UI);
- } else if (node instanceof DBNDataSource) {
- DBPDataSourceContainer object = ((DBNDataSource) node).getDataSourceContainer();
+ } else if (node instanceof DBNDataSource dataSource) {
+ DBPDataSourceContainer object = dataSource.getDataSourceContainer();
nodeName = object.getName();
} else {
nodeName = node.getNodeTargetName();
@@ -182,18 +180,20 @@ public boolean isHasChildren() {
@Association
public String[] getFeatures() {
List features = new ArrayList<>();
+ boolean isLeaf = false;
if (node instanceof DBNDatabaseItem) {
features.add(NODE_FEATURE_ITEM);
DBSObject object = ((DBNDatabaseItem) node).getObject();
if (object instanceof DBSEntity || object instanceof DBSProcedure) {
features.add(NODE_FEATURE_LEAF);
+ isLeaf = true;
}
}
if (node instanceof DBNContainer) {
features.add(NODE_FEATURE_CONTAINER);
}
boolean isShared = false;
- if (node instanceof DBNDatabaseNode) {
+ if (node instanceof DBNDatabaseNode && !isLeaf) {
if (node instanceof DBNDataSource dataSource) {
if (dataSource.getDataSourceContainer().getDataSource() != null) {
boolean hasNonFolderNode = DBXTreeNode.hasNonFolderNode(dataSource.getMeta().getChildren(null));
@@ -235,7 +235,7 @@ public String[] getFeatures() {
if (objectManager != null && objectManager.canDeleteObject(object)) {
features.add(NODE_FEATURE_CAN_DELETE);
}
- if (objectManager instanceof DBEObjectRenamer && ((DBEObjectRenamer) objectManager).canRenameObject(object)) {
+ if (objectManager instanceof DBEObjectRenamer renamer && renamer.canRenameObject(object)) {
if (!object.getDataSource().getContainer().getNavigatorSettings().isShowOnlyEntities()) {
features.add(NODE_FEATURE_CAN_RENAME);
}
@@ -259,7 +259,7 @@ private boolean hasNodePermission(RMProjectPermission permission) {
if (project == null) {
return false;
}
- RMProject rmProject = project.getRmProject();
+ RMProject rmProject = project.getRMProject();
return SMUtils.hasProjectPermission(session, rmProject, permission);
}
@@ -282,9 +282,10 @@ private boolean isDistributedSpecialFolderNode() {
@Property
public WebPropertyInfo[] getNodeDetails() throws DBWebException {
- if (node instanceof DBPObjectWithDetails) {
+ if (node instanceof DBPObjectWithDetails objectWithDetails) {
try {
- DBPObject objectDetails = ((DBPObjectWithDetails) node).getObjectDetails(session.getProgressMonitor(), session.getSessionContext(), node);
+ DBPObject objectDetails = objectWithDetails.getObjectDetails(
+ session.getProgressMonitor(), session.getSessionContext(), node);
if (objectDetails != null) {
return WebServiceUtils.getObjectProperties(session, objectDetails);
}
@@ -301,8 +302,8 @@ public WebPropertyInfo[] getNodeDetails() throws DBWebException {
@Property
public WebDatabaseObjectInfo getObject() {
- if (node instanceof DBNDatabaseNode) {
- DBSObject object = ((DBNDatabaseNode) node).getObject();
+ if (node instanceof DBNDatabaseNode dbNode) {
+ DBSObject object = dbNode.getObject();
return object == null ? null : new WebDatabaseObjectInfo(session, object);
}
return null;
@@ -320,10 +321,10 @@ public String getObjectId() {
@Property
public DBSObjectFilter getFilter() throws DBWebException {
- if (!(node instanceof DBNDatabaseNode)) {
+ if (!(node instanceof DBNDatabaseNode dbNode)) {
throw new DBWebException("Invalid navigator node type: " + node.getClass().getName());
}
- DBSObjectFilter filter = ((DBNDatabaseNode) node).getNodeFilter(((DBNDatabaseNode) node).getItemsMeta(), true);
+ DBSObjectFilter filter = dbNode.getNodeFilter(dbNode.getItemsMeta(), true);
return filter == null || filter.isEmpty() || !filter.isEnabled() ? null : filter;
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java
index 28f54520c8..67efb63def 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java
@@ -142,7 +142,7 @@ public List getNavigatorNodeChildren(
return result.subList(offset, result.size());
}
} catch (DBException e) {
- throw new DBWebException(e);
+ throw new DBWebException(e.getMessage(), e);
}
}
@@ -228,11 +228,12 @@ public boolean setNavigatorNodeFilter(
filter.setExclude(exclude);
}
filter.setEnabled(true);
- ((DBNDatabaseNode) node).setNodeFilter(
- ((DBNDatabaseNode) node).getItemsMeta(), filter, true);
- if (hasNodeEditPermission(webSession, node, ((WebProjectImpl) node.getOwnerProject()).getRmProject())) {
- // Save settings
- ((DBNDatabaseNode) node).getDataSourceContainer().persistConfiguration();
+ if (node instanceof DBNDatabaseNode dbNode) {
+ dbNode.setNodeFilter(dbNode.getItemsMeta(), filter, true);
+ if (hasNodeEditPermission(webSession, node, ((WebProjectImpl) node.getOwnerProject()).getRMProject())) {
+ // Save settings
+ dbNode.getDataSourceContainer().persistConfiguration();
+ }
}
} catch (DBException e) {
if (e instanceof DBWebException) {
@@ -255,14 +256,14 @@ public boolean refreshNavigatorNode(
if (node == null) {
throw new DBWebException("Navigator node '" + nodePath + "' not found");
}
- if (node instanceof DBNDataSource) {
+ if (node instanceof DBNDataSource dbnDataSource) {
// Do not refresh entire tree - just clear child nodes
// Otherwise refresh may fail if navigator settings were changed.
- DBPDataSource dataSource = ((DBNDataSource) node).getDataSource();
- if (dataSource instanceof DBPRefreshableObject) {
- ((DBPRefreshableObject) dataSource).refreshObject(monitor);
+ DBPDataSource dataSource = dbnDataSource.getDataSource();
+ if (dataSource instanceof DBPRefreshableObject refreshableObject) {
+ refreshableObject.refreshObject(monitor);
}
- ((DBNDataSource) node).cleanupNode();
+ dbnDataSource.cleanupNode();
} else if (node instanceof DBNLocalFolder) {
// Refresh can't be applied to the local folder node
return true;
@@ -425,7 +426,7 @@ private String renameConnectionFolder(@NotNull WebSession session, DBNNode node,
List siblings = Arrays.stream(
((DBNLocalFolder) node).getLogicalParent().getChildren(session.getProgressMonitor()))
.filter(n -> n instanceof DBNLocalFolder)
- .map(DBNNode::getName).collect(Collectors.toList());
+ .map(DBNNode::getName).toList();
if (siblings.contains(newName)) {
throw new DBWebException("Name " + newName + " is unavailable or invalid");
}
@@ -570,7 +571,7 @@ public int deleteNodes(
private void checkProjectEditAccess(DBNNode node, WebSession session) throws DBException {
BaseWebProjectImpl project = (BaseWebProjectImpl) node.getOwnerProject();
- if (project == null || !hasNodeEditPermission(session, node, project.getRmProject())) {
+ if (project == null || !hasNodeEditPermission(session, node, project.getRMProject())) {
throw new DBException("Access denied");
}
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java
index 40dc20ae00..92017155d1 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java
@@ -18,18 +18,19 @@
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.auth.SMTokenCredentialProvider;
-import io.cloudbeaver.model.session.BaseWebSession;
-import io.cloudbeaver.model.session.WebHeadlessSession;
-import io.cloudbeaver.model.session.WebSession;
-import io.cloudbeaver.model.session.WebSessionAuthProcessor;
+import io.cloudbeaver.server.AppWebSessionManager;
+import io.cloudbeaver.model.session.*;
import io.cloudbeaver.registry.WebHandlerRegistry;
import io.cloudbeaver.registry.WebSessionHandlerDescriptor;
import io.cloudbeaver.server.CBApplication;
+import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.events.WSWebUtils;
import io.cloudbeaver.service.DBWSessionHandler;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Session;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
@@ -47,7 +48,7 @@
/**
* Web session manager
*/
-public class WebSessionManager {
+public class WebSessionManager implements AppWebSessionManager {
private static final Log log = Log.getLog(WebSessionManager.class);
@@ -61,6 +62,7 @@ public WebSessionManager(CBApplication application) {
/**
* Closes Web Session, associated to HttpSession from {@code request}
*/
+ @Override
public BaseWebSession closeSession(@NotNull HttpServletRequest request) {
HttpSession session = request.getSession();
if (session != null) {
@@ -82,20 +84,27 @@ protected CBApplication getApplication() {
}
@Deprecated
- public boolean touchSession(@NotNull HttpServletRequest request,
- @NotNull HttpServletResponse response) throws DBWebException {
+ public boolean touchSession(
+ @NotNull HttpServletRequest request,
+ @NotNull HttpServletResponse response
+ ) throws DBWebException {
WebSession webSession = getWebSession(request, response, false);
- webSession.updateSessionParameters(request);
+ var requestInfo = new WebHttpRequestInfo(request);
+ webSession.updateSessionParameters(requestInfo);
webSession.updateInfo(!request.getSession().isNew());
return true;
}
+ @Override
@NotNull
- public WebSession getWebSession(@NotNull HttpServletRequest request,
- @NotNull HttpServletResponse response) throws DBWebException {
+ public WebSession getWebSession(
+ @NotNull HttpServletRequest request,
+ @NotNull HttpServletResponse response
+ ) throws DBWebException {
return getWebSession(request, response, true);
}
+ @Override
@NotNull
public WebSession getWebSession(
@NotNull HttpServletRequest request,
@@ -109,14 +118,14 @@ public WebSession getWebSession(
var baseWebSession = sessionMap.get(sessionId);
if (baseWebSession == null && CBApplication.getInstance().isConfigurationMode()) {
try {
- webSession = createWebSessionImpl(request);
+ webSession = createWebSessionImpl(new WebHttpRequestInfo(request));
} catch (DBException e) {
throw new DBWebException("Failed to create web session", e);
}
sessionMap.put(sessionId, webSession);
} else if (baseWebSession == null) {
try {
- webSession = createWebSessionImpl(request);
+ webSession = createWebSessionImpl(new WebHttpRequestInfo(request));
} catch (DBException e) {
throw new DBWebException("Failed to create web session", e);
}
@@ -154,13 +163,15 @@ public WebSession getWebSession(
* @return WebSession object or null, if session expired or invalid
*/
@Nullable
- public WebSession getOrRestoreSession(@NotNull HttpServletRequest request) {
- var httpSession = request.getSession();
- if (httpSession == null) {
+ public WebSession getOrRestoreSession(@NotNull Request request) {
+ var sessionIdCookie = Request.getCookies(request).stream().filter(
+ c -> c.getName().equals(CBConstants.CB_SESSION_COOKIE_NAME)
+ ).findAny().orElse(null);
+ if (sessionIdCookie == null) {
log.debug("Http session is null. No Web Session returned");
return null;
}
- var sessionId = httpSession.getId();
+ var sessionId = sessionIdCookie.getValue();
WebSession webSession;
synchronized (sessionMap) {
if (sessionMap.containsKey(sessionId)) {
@@ -178,7 +189,12 @@ public WebSession getOrRestoreSession(@NotNull HttpServletRequest request) {
return null;
}
- webSession = createWebSessionImpl(request);
+ webSession = createWebSessionImpl(new WebHttpRequestInfo(
+ request.getId(),
+ request.getAttribute("locale"),
+ Request.getRemoteAddr(request),
+ request.getHeaders().get("User-Agent")
+ ));
restorePreviousUserSession(webSession, oldAuthInfo);
sessionMap.put(sessionId, webSession);
@@ -212,7 +228,7 @@ private void restorePreviousUserSession(
}
@NotNull
- protected WebSession createWebSessionImpl(@NotNull HttpServletRequest request) throws DBException {
+ protected WebSession createWebSessionImpl(@NotNull WebHttpRequestInfo request) throws DBException {
return new WebSession(request, application, getSessionHandlers());
}
@@ -223,6 +239,7 @@ protected Map getSessionHandlers() {
.collect(Collectors.toMap(WebSessionHandlerDescriptor::getId, WebSessionHandlerDescriptor::getInstance));
}
+ @Override
@Nullable
public BaseWebSession getSession(@NotNull String sessionId) {
synchronized (sessionMap) {
@@ -230,6 +247,7 @@ public BaseWebSession getSession(@NotNull String sessionId) {
}
}
+ @Override
@Nullable
public WebSession findWebSession(HttpServletRequest request) {
String sessionId = request.getSession().getId();
@@ -242,6 +260,7 @@ public WebSession findWebSession(HttpServletRequest request) {
}
}
+ @Override
public WebSession findWebSession(HttpServletRequest request, boolean errorOnNoFound) throws DBWebException {
WebSession webSession = findWebSession(request);
if (webSession != null) {
@@ -274,6 +293,7 @@ public void expireIdleSessions() {
}
}
+ @Override
public Collection getAllActiveSessions() {
synchronized (sessionMap) {
return new ArrayList<>(sessionMap.values());
@@ -281,16 +301,15 @@ public Collection getAllActiveSessions() {
}
@Nullable
- public WebHeadlessSession getHeadlessSession(HttpServletRequest request, boolean create) throws DBException {
- String smAccessToken = request.getHeader(WSConstants.WS_AUTH_HEADER);
+ public WebHeadlessSession getHeadlessSession(Request request, Session session, boolean create) throws DBException {
+ String smAccessToken = request.getHeaders().get(WSConstants.WS_AUTH_HEADER);
if (CommonUtils.isEmpty(smAccessToken)) {
return null;
}
synchronized (sessionMap) {
- var httpSession = request.getSession();
var tempCredProvider = new SMTokenCredentialProvider(smAccessToken);
SMAuthPermissions authPermissions = application.createSecurityController(tempCredProvider).getTokenPermissions();
- var sessionId = httpSession != null ? httpSession.getId()
+ var sessionId = session != null ? session.getId()
: authPermissions.getSessionId();
var existSession = sessionMap.get(sessionId);
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLDataLOBReceiver.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLDataLOBReceiver.java
index fbfced3597..2d3a9e5fa5 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLDataLOBReceiver.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLDataLOBReceiver.java
@@ -20,7 +20,7 @@
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.CBPlatform;
import org.jkiss.dbeaver.Log;
-import org.jkiss.dbeaver.model.exec.*;
+import org.jkiss.dbeaver.model.exec.DBCException;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
import org.jkiss.dbeaver.model.sql.DBQuotaException;
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLFileLoaderServlet.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLFileLoaderServlet.java
index 5ea8b52716..34f70e363f 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLFileLoaderServlet.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLFileLoaderServlet.java
@@ -20,6 +20,7 @@
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import io.cloudbeaver.DBWebException;
+import io.cloudbeaver.model.app.WebApplication;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBPlatform;
@@ -29,7 +30,7 @@
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.ee10.servlet.ServletContextRequest;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
@@ -60,7 +61,7 @@ public class WebSQLFileLoaderServlet extends WebServiceServletBase {
.setPrettyPrinting()
.create();
- public WebSQLFileLoaderServlet(CBApplication application) {
+ public WebSQLFileLoaderServlet(WebApplication application) {
super(application);
}
@@ -84,7 +85,7 @@ protected void processServiceRequest(
.resolve(session.getSessionId());
MultipartConfigElement multiPartConfig = new MultipartConfigElement(tempFolder.toString());
- request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, multiPartConfig);
+ request.setAttribute(ServletContextRequest.MULTIPART_CONFIG_ELEMENT, multiPartConfig);
Map variables = gson.fromJson(request.getParameter(REQUEST_PARAM_VARIABLES), MAP_STRING_OBJECT_TYPE);
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLProcessor.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLProcessor.java
index d49d4ec414..a58ab10961 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLProcessor.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLProcessor.java
@@ -259,6 +259,8 @@ public WebSQLExecuteInfo processQuery(
}
}
});
+ } else {
+ executeInfo.setResults(new WebSQLQueryResults[0]);
}
} catch (DBException e) {
throw new DBWebException("Error executing query", e);
@@ -793,6 +795,10 @@ public Object convertInputCellValue(DBCSession session, DBDAttributeBinding upda
cellRawValue,
false,
true);
+ //FIXME: fix array editing for nosql databases
+ if (realCellValue == null && cellRawValue != null && updateAttribute.getDataKind() == DBPDataKind.ARRAY) {
+ throw new DBCException("Array update is not supported");
+ }
} catch (DBCException e) {
//checks if this function is used only for script generation
if (justGenerateScript) {
@@ -1138,7 +1144,8 @@ private static DBCExecutionPurpose resolveQueryPurpose(DBDDataFilter filter) {
return filter.hasFilters() ? DBCExecutionPurpose.USER_FILTERED : DBCExecutionPurpose.USER;
}
- private Object setCellRowValue(Object cellRow, WebSession webSession, DBCSession dbcSession, DBDAttributeBinding allAttributes, boolean withoutExecution) {
+ private Object setCellRowValue(Object cellRow, WebSession webSession, DBCSession dbcSession, DBDAttributeBinding allAttributes, boolean withoutExecution)
+ throws DBException {
if (cellRow instanceof Map, ?>) {
Map variables = (Map) cellRow;
if (variables.get(FILE_ID) != null) {
@@ -1151,14 +1158,10 @@ private Object setCellRowValue(Object cellRow, WebSession webSession, DBCSession
var file = Files.newInputStream(path);
return convertInputCellValue(dbcSession, allAttributes, file, withoutExecution);
} catch (IOException | DBCException e) {
- return new DBException(e.getMessage());
+ throw new DBException(e.getMessage());
}
}
}
- try {
- return convertInputCellValue(dbcSession, allAttributes, cellRow, withoutExecution);
- } catch (DBCException e) {
- return new DBException(e.getMessage());
- }
+ return convertInputCellValue(dbcSession, allAttributes, cellRow, withoutExecution);
}
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryDataContainer.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryDataContainer.java
index b8ab7b7af7..1529bbde64 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryDataContainer.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryDataContainer.java
@@ -26,11 +26,11 @@
import org.jkiss.dbeaver.model.data.DBDDataFilter;
import org.jkiss.dbeaver.model.data.DBDDataReceiver;
import org.jkiss.dbeaver.model.exec.*;
-import org.jkiss.dbeaver.model.impl.sql.SQLQueryTransformerCount;
import org.jkiss.dbeaver.model.sql.SQLQuery;
import org.jkiss.dbeaver.model.sql.SQLScriptContext;
import org.jkiss.dbeaver.model.sql.SQLSyntaxManager;
import org.jkiss.dbeaver.model.sql.data.SQLQueryDataContainer;
+import org.jkiss.dbeaver.model.sql.transformers.SQLQueryTransformerCount;
import org.jkiss.dbeaver.model.struct.DBSDataContainer;
import org.jkiss.dbeaver.model.struct.DBSObject;
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryDataReceiver.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryDataReceiver.java
index 6a5800cb20..89bdc1e5f1 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryDataReceiver.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryDataReceiver.java
@@ -17,7 +17,7 @@
package io.cloudbeaver.service.sql;
import io.cloudbeaver.model.session.WebSession;
-import io.cloudbeaver.server.CBApplication;
+import io.cloudbeaver.utils.WebAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
@@ -58,7 +58,9 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
this.contextInfo = contextInfo;
this.dataContainer = dataContainer;
this.dataFormat = dataFormat;
- rowLimit = CBApplication.getInstance().getAppConfiguration().getResourceQuota(WebSQLConstants.QUOTA_PROP_ROW_LIMIT);
+ rowLimit = WebAppUtils.getWebApplication()
+ .getAppConfiguration()
+ .getResourceQuota(WebSQLConstants.QUOTA_PROP_ROW_LIMIT);
}
public WebSQLQueryResultSet getResultSet() {
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLResultServlet.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLResultServlet.java
index a0d231525e..42652e9843 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLResultServlet.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLResultServlet.java
@@ -1,22 +1,39 @@
+/*
+ * 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.service.sql;
import io.cloudbeaver.DBWebException;
+import io.cloudbeaver.model.app.WebApplication;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.servlets.CBStaticServlet;
import io.cloudbeaver.service.WebServiceServletBase;
-import org.eclipse.jetty.server.Request;
-import org.jkiss.dbeaver.DBException;
-import org.jkiss.dbeaver.Log;
-import org.jkiss.utils.CommonUtils;
-import org.jkiss.utils.IOUtils;
-
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
+import org.eclipse.jetty.ee10.servlet.ServletContextRequest;
+import org.jkiss.dbeaver.DBException;
+import org.jkiss.dbeaver.Log;
+import org.jkiss.utils.CommonUtils;
+import org.jkiss.utils.IOUtils;
+
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
@@ -35,14 +52,14 @@ public class WebSQLResultServlet extends WebServiceServletBase {
private final DBWServiceSQL sqlService;
- public WebSQLResultServlet(CBApplication application, DBWServiceSQL sqlService) {
+ public WebSQLResultServlet(WebApplication application, DBWServiceSQL sqlService) {
super(application);
this.sqlService = sqlService;
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, MULTI_PART_CONFIG);
+ request.setAttribute(ServletContextRequest.MULTIPART_CONFIG_ELEMENT, MULTI_PART_CONFIG);
String fileName = UUID.randomUUID().toString();
for (Part part : request.getParts()) {
part.write(WebSQLDataLOBReceiver.DATA_EXPORT_FOLDER + "/" + fileName);
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLUtils.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLUtils.java
index 667539783d..9bf207fa4c 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLUtils.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLUtils.java
@@ -16,11 +16,11 @@
*/
package io.cloudbeaver.service.sql;
+import io.cloudbeaver.model.app.WebAppConfiguration;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.registry.WebServiceRegistry;
-import io.cloudbeaver.server.CBAppConfig;
-import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.utils.CBModelConstants;
+import io.cloudbeaver.utils.WebAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.data.*;
@@ -151,9 +151,11 @@ private static Object serializeContentValue(WebSession session, DBDContent value
if (ContentUtils.isTextContent(value)) {
String stringValue = ContentUtils.getContentStringValue(session.getProgressMonitor(), value);
int textPreviewMaxLength = CommonUtils.toInt(
- CBApplication.getInstance().getAppConfiguration().getResourceQuota(
- WebSQLConstants.QUOTA_PROP_TEXT_PREVIEW_MAX_LENGTH,
- WebSQLConstants.TEXT_PREVIEW_MAX_LENGTH));
+ WebAppUtils.getWebApplication()
+ .getAppConfiguration()
+ .getResourceQuota(WebSQLConstants.QUOTA_PROP_TEXT_PREVIEW_MAX_LENGTH),
+ WebSQLConstants.TEXT_PREVIEW_MAX_LENGTH
+ );
if (stringValue != null && stringValue.length() > textPreviewMaxLength) {
stringValue = stringValue.substring(0, textPreviewMaxLength);
}
@@ -164,12 +166,11 @@ private static Object serializeContentValue(WebSession session, DBDContent value
if (binaryValue != null) {
byte[] previewValue = binaryValue;
// gets parameters from the configuration file
- CBAppConfig config = CBApplication.getInstance().getAppConfiguration();
+ WebAppConfiguration config = WebAppUtils.getWebApplication().getAppConfiguration();
// the max length of the text preview
int textPreviewMaxLength = CommonUtils.toInt(
config.getResourceQuota(
- WebSQLConstants.QUOTA_PROP_TEXT_PREVIEW_MAX_LENGTH,
- WebSQLConstants.TEXT_PREVIEW_MAX_LENGTH));
+ WebSQLConstants.QUOTA_PROP_TEXT_PREVIEW_MAX_LENGTH), WebSQLConstants.TEXT_PREVIEW_MAX_LENGTH);
if (previewValue.length > textPreviewMaxLength) {
previewValue = Arrays.copyOf(previewValue, textPreviewMaxLength);
}
@@ -177,8 +178,8 @@ private static Object serializeContentValue(WebSession session, DBDContent value
// the max length of the binary preview
int binaryPreviewMaxLength = CommonUtils.toInt(
config.getResourceQuota(
- WebSQLConstants.QUOTA_PROP_BINARY_PREVIEW_MAX_LENGTH,
- WebSQLConstants.BINARY_PREVIEW_MAX_LENGTH));
+ WebSQLConstants.QUOTA_PROP_BINARY_PREVIEW_MAX_LENGTH),
+ WebSQLConstants.BINARY_PREVIEW_MAX_LENGTH);
byte[] inlineValue = binaryValue;
if (inlineValue.length > binaryPreviewMaxLength) {
inlineValue = Arrays.copyOf(inlineValue, textPreviewMaxLength);
@@ -214,9 +215,11 @@ private static Object serializeGeometryValue(DBGeometry value) {
*/
public static Object serializeStringValue(Object value) {
int textPreviewMaxLength = CommonUtils.toInt(
- CBApplication.getInstance().getAppConfiguration().getResourceQuota(
- WebSQLConstants.QUOTA_PROP_TEXT_PREVIEW_MAX_LENGTH,
- WebSQLConstants.TEXT_PREVIEW_MAX_LENGTH));
+ WebAppUtils.getWebApplication()
+ .getAppConfiguration()
+ .getResourceQuota(WebSQLConstants.QUOTA_PROP_TEXT_PREVIEW_MAX_LENGTH),
+ WebSQLConstants.TEXT_PREVIEW_MAX_LENGTH
+ );
String stringValue = value.toString();
if (stringValue.length() < textPreviewMaxLength) {
return value.toString();
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java
index 60c471f348..f2bc4e41ad 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java
@@ -19,6 +19,7 @@
import graphql.schema.DataFetchingEnvironment;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.WebConnectionInfo;
+import io.cloudbeaver.model.app.WebApplication;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.service.DBWBindingContext;
@@ -39,7 +40,7 @@
/**
* Web service implementation
*/
-public class WebServiceBindingSQL extends WebServiceBindingBase implements DBWServiceBindingServlet {
+public class WebServiceBindingSQL extends WebServiceBindingBase implements DBWServiceBindingServlet {
public WebServiceBindingSQL() {
super(DBWServiceSQL.class, new WebServiceSQL(), "schema/service.sql.graphqls");
@@ -299,7 +300,7 @@ public static WebSQLContextInfo getSQLContext(WebSQLProcessor processor, String
}
@Override
- public void addServlets(CBApplication application, DBWServletContext servletContext) throws DBException {
+ public void addServlets(WebApplication application, DBWServletContext servletContext) throws DBException {
servletContext.addServlet(
"sqlResultValueViewer",
new WebSQLResultServlet(application, getServiceImpl()),
@@ -312,6 +313,11 @@ public void addServlets(CBApplication application, DBWServletContext servletCont
);
}
+ @Override
+ public boolean isApplicable(WebApplication application) {
+ return application.isMultiuser();
+ }
+
private static class WebSQLConfiguration {
private final Map processors = new HashMap<>();
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java
index 45bad1c55d..cc512d8a9a 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java
@@ -85,7 +85,7 @@ public WebSQLContextInfo[] listContexts(
WebConnectionInfo webConnection = WebServiceBindingBase.getWebConnection(session, projectId, connectionId);
conToRead.add(webConnection);
} else {
- conToRead.addAll(session.getConnections());
+ conToRead.addAll(session.getAccessibleProjects().stream().flatMap(p -> p.getConnections().stream()).toList());
}
List contexts = new ArrayList<>();
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 a91459c9bc..f0e730e532 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.103.qualifier
-Bundle-Release-Date: 20240819
+Bundle-Version: 1.0.108.qualifier
+Bundle-Release-Date: 20241104
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 c7ad4e19dd..80411660ad 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.103-SNAPSHOT
+ 1.0.108-SNAPSHOT
eclipse-plugin
diff --git a/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls b/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls
index 559b9b90e4..20f53605dd 100644
--- a/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls
+++ b/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls
@@ -89,6 +89,7 @@ type AdminAuthProviderConfiguration {
redirectLink: String
metadataLink: String
acsLink: String
+ entityIdLink: String @since(version: "24.2.1")
}
type WebFeatureSet {
diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/AdminServerConfig.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/AdminServerConfig.java
index 0ba9adf93e..e459b2d2a0 100644
--- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/AdminServerConfig.java
+++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/AdminServerConfig.java
@@ -16,7 +16,7 @@
*/
package io.cloudbeaver.service.admin;
-import io.cloudbeaver.server.CBAppConfig;
+import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.server.CBApplication;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java
index 973f010f6d..8a9bca7de0 100644
--- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java
+++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java
@@ -202,6 +202,9 @@ public void bindWiring(DBWBindingContext model) throws DBWebException {
@Override
public void addServlets(CBApplication application, DBWServletContext servletContext) throws DBException {
+ if(!application.isMultiuser()) {
+ return;
+ }
servletContext.addServlet("adminLogs", new WebAdminLogsServlet(application), application.getServicesURI() + "logs/*");
}
diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/ConnectionSearcher.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/ConnectionSearcher.java
index 711acc19b1..8292df8c2c 100644
--- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/ConnectionSearcher.java
+++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/ConnectionSearcher.java
@@ -16,9 +16,9 @@
*/
package io.cloudbeaver.service.admin.impl;
+import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.utils.ConfigurationUtils;
-import io.cloudbeaver.server.CBAppConfig;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBPlatform;
import io.cloudbeaver.service.admin.AdminConnectionSearchInfo;
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 bfbd2778b2..48e78992a3 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
@@ -22,11 +22,15 @@
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.auth.provider.local.LocalAuthProvider;
import io.cloudbeaver.model.WebPropertyInfo;
+import io.cloudbeaver.model.config.CBAppConfig;
+import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.model.session.WebAuthInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.user.WebUser;
import io.cloudbeaver.registry.*;
-import io.cloudbeaver.server.*;
+import io.cloudbeaver.server.CBApplication;
+import io.cloudbeaver.server.CBConstants;
+import io.cloudbeaver.server.CBPlatform;
import io.cloudbeaver.service.DBWServiceServerConfigurator;
import io.cloudbeaver.service.admin.*;
import io.cloudbeaver.service.security.SMUtils;
@@ -39,6 +43,7 @@
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.auth.AuthInfo;
+import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
import org.jkiss.dbeaver.model.rm.RMProjectType;
@@ -72,6 +77,9 @@ public class WebServiceAdmin implements DBWServiceAdmin {
public AdminUserInfo getUserById(@NotNull WebSession webSession, @NotNull String userId) throws DBWebException {
try {
SMUser smUser = webSession.getAdminSecurityController().getUserById(userId);
+ if (smUser == null) {
+ throw new DBException("User '" + userId + "' not found");
+ }
return new AdminUserInfo(webSession, new WebUser(smUser));
} catch (Exception e) {
throw new DBWebException("Error getting user - " + userId, e);
@@ -540,7 +548,7 @@ public boolean configureServer(WebSession webSession, Map params
appConfig.setAdminCredentialsSaveEnabled(config.isAdminCredentialsSaveEnabled());
appConfig.setEnabledFeatures(config.getEnabledFeatures().toArray(new String[0]));
// custom logic for enabling embedded drivers
- appConfig.updateDisabledDriversConfig(config.getDisabledDrivers());
+ updateDisabledDriversConfig(appConfig, config.getDisabledDrivers());
appConfig.setResourceManagerEnabled(config.isResourceManagerEnabled());
if (CommonUtils.isEmpty(config.getEnabledAuthProviders())) {
@@ -618,6 +626,36 @@ public boolean configureServer(WebSession webSession, Map params
return true;
}
+ // we disable embedded drivers by default and enable it in enabled drivers list
+ // that's why we need so complicated logic for disabling drivers
+ private void updateDisabledDriversConfig(CBAppConfig appConfig, String[] disabledDriversConfig) {
+ Set disabledIds = new LinkedHashSet<>(Arrays.asList(disabledDriversConfig));
+ Set enabledIds = new LinkedHashSet<>(Arrays.asList(appConfig.getEnabledDrivers()));
+
+ // remove all disabled embedded drivers from enabled drivers list
+ enabledIds.removeAll(disabledIds);
+
+ // enable embedded driver if it is not in disabled drivers list
+ for (String driverId : appConfig.getDisabledDrivers()) {
+ if (disabledIds.contains(driverId)) {
+ // driver is also disabled
+ continue;
+ }
+ // driver is removed from disabled list
+ // we need to enable if it is embedded
+ try {
+ DBPDriver driver = WebServiceUtils.getDriverById(driverId);
+ if (driver.isEmbedded()) {
+ enabledIds.add(driverId);
+ }
+ } catch (DBWebException e) {
+ log.error("Failed to find driver by id", e);
+ }
+ }
+ appConfig.setDisabledDrivers(disabledDriversConfig);
+ appConfig.setEnabledDrivers(enabledIds.toArray(String[]::new));
+ }
+
@Override
public boolean setDefaultNavigatorSettings(WebSession webSession, DBNBrowseSettings settings) throws DBWebException {
CBApplication.getInstance().getAppConfiguration().setDefaultNavigatorSettings(settings);
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 025840bf4e..d6d81abf2f 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.103.qualifier
-Bundle-Release-Date: 20240819
+Bundle-Version: 1.0.108.qualifier
+Bundle-Release-Date: 20241104
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 2cee174fa8..7ec5e3be44 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.103-SNAPSHOT
+ 1.0.108-SNAPSHOT
eclipse-plugin
diff --git a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls
index eb5b4b4d43..f1a86e224e 100644
--- a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls
+++ b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls
@@ -42,6 +42,7 @@ type AuthProviderConfiguration {
signOutLink: String
metadataLink: String
acsLink: String
+ entityIdLink: String @since(version: "24.2.1")
}
type AuthProviderCredentialsProfile {
@@ -136,6 +137,9 @@ type UserInfo {
configurationParameters: Object!
# User teams
teams: [UserTeamInfo!]!
+
+ @since(version: "24.2.3")
+ isAnonymous: Boolean!
}
type UserTeamInfo {
diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java
index 5bda8362a2..8420f4e8ba 100644
--- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java
+++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/ReverseProxyConfigurator.java
@@ -71,7 +71,7 @@ private void migrateConfiguration(
smReverseProxyProviderConfiguration.setProvider(RPAuthProvider.AUTH_PROVIDER);
smReverseProxyProviderConfiguration.setDisplayName("Reverse Proxy");
smReverseProxyProviderConfiguration.setDescription(
- "Automatically created provider after changing Reverse Proxy configuration way in 23.3.4 version"
+ "This provider was created automatically"
);
smReverseProxyProviderConfiguration .setIconURL("");
Map parameters = new HashMap<>();
diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserInfo.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserInfo.java
index 8e9c44e00c..d7cbaf650c 100644
--- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserInfo.java
+++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserInfo.java
@@ -69,6 +69,9 @@ public List getAuthTokens() {
@Property
public List getLinkedAuthProviders() throws DBWebException {
+ if (isAnonymous()) {
+ return List.of();
+ }
if (linkedProviders == null) {
try {
linkedProviders = session.getSecurityController().getCurrentUserLinkedProviders();
@@ -104,4 +107,9 @@ public List getTeams() throws DBWebException {
return List.of();
}
}
+
+ @Property
+ public boolean isAnonymous() {
+ return session.getUser() == null;
+ }
}
diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java
index ca7466b619..2ea5cdcd91 100644
--- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java
+++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java
@@ -21,6 +21,7 @@
import io.cloudbeaver.auth.SMSignOutLinkProvider;
import io.cloudbeaver.auth.provider.local.LocalAuthProvider;
import io.cloudbeaver.model.WebPropertyInfo;
+import io.cloudbeaver.model.app.WebAppConfiguration;
import io.cloudbeaver.model.session.WebAuthInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.session.WebSessionAuthProcessor;
@@ -189,7 +190,12 @@ public WebLogoutInfo authLogout(
@Override
public WebUserInfo activeUser(@NotNull WebSession webSession) throws DBWebException {
if (webSession.getUser() == null) {
- return null;
+ WebAppConfiguration appConfiguration = webSession.getApplication().getAppConfiguration();
+ if (!appConfiguration.isAnonymousAccessEnabled()) {
+ return null;
+ }
+ SMUser anonymous = new SMUser("anonymous", true, null);
+ return new WebUserInfo(webSession, new WebUser(anonymous));
}
try {
// Read user from security controller. It will also read meta parameters
diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/local/LocalServletHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/local/LocalServletHandler.java
index 9f31bf115f..42d2024e83 100644
--- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/local/LocalServletHandler.java
+++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/local/LocalServletHandler.java
@@ -40,7 +40,7 @@ public class LocalServletHandler extends AbstractActionServletHandler {
@Override
public boolean handleRequest(Servlet servlet, HttpServletRequest request, HttpServletResponse response) throws DBException, IOException {
- if (URI_PREFIX.equals(WebAppUtils.removeSideSlashes(request.getPathInfo()))) {
+ if (URI_PREFIX.equals(WebAppUtils.removeSideSlashes(request.getServletPath()))) {
try {
WebSession webSession = CBPlatform.getInstance().getSessionManager().getWebSession(request, response, true);
createActionFromParams(webSession, request, response);
diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/local/LocalSessionHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/local/LocalSessionHandler.java
index 6707fe84ec..c2261f8e5f 100644
--- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/local/LocalSessionHandler.java
+++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/local/LocalSessionHandler.java
@@ -54,10 +54,9 @@ protected void openDatabaseConsole(WebSession webSession, CBServerAction action)
String connectionId = action.getParameter(LocalServletHandler.PARAM_CONNECTION_ID);
String connectionName = action.getParameter(LocalServletHandler.PARAM_CONNECTION_NAME);
String connectionURL = action.getParameter(LocalServletHandler.PARAM_CONNECTION_URL);
- Stream stream = webSession.getConnections().stream();
- if (projectId != null) {
- stream = stream.filter(c -> c.getProjectId().equals(projectId));
- }
+ Stream stream = webSession.getAccessibleProjects().stream()
+ .filter(c -> projectId == null || c.getId().equals(projectId))
+ .flatMap(p -> p.getConnections().stream());
if (connectionId != null) {
stream = stream.filter(c -> c.getId().equals(connectionId));
} else if (connectionName != null) {
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 63190e4490..025209c00b 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.104.qualifier
-Bundle-Release-Date: 20240819
+Bundle-Version: 1.0.109.qualifier
+Bundle-Release-Date: 20241104
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 3f11db754e..081944d88c 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.104-SNAPSHOT
+ 1.0.109-SNAPSHOT
eclipse-plugin
diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebServiceBindingDataTransfer.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebServiceBindingDataTransfer.java
index 5c6daa5356..10151d5467 100644
--- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebServiceBindingDataTransfer.java
+++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebServiceBindingDataTransfer.java
@@ -66,6 +66,9 @@ public void bindWiring(DBWBindingContext model) {
@Override
public void addServlets(CBApplication application, DBWServletContext servletContext) throws DBException {
+ if (!application.isMultiuser()) {
+ return;
+ }
servletContext.addServlet(
"dataTransfer",
new WebDataTransferServlet(application, getServiceImpl()),
diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferImportServlet.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferImportServlet.java
index 5aad6558cc..f645d07404 100644
--- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferImportServlet.java
+++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferImportServlet.java
@@ -42,7 +42,9 @@
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.*;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.UUID;
@MultipartConfig
public class WebDataTransferImportServlet extends WebServiceServletBase {
@@ -86,7 +88,7 @@ protected void processServiceRequest(
throw new IllegalArgumentException("Missing required parameters");
}
- WebConnectionInfo webConnectionInfo = session.getWebConnectionInfo(projectId, connectionId);
+ WebConnectionInfo webConnectionInfo = session.getAccessibleProjectById(projectId).getWebConnectionInfo(connectionId);
WebSQLProcessor processor = WebServiceBindingSQL.getSQLProcessor(webConnectionInfo);
WebSQLContextInfo webSQLContextInfo = processor.getContext(contextId);
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 aaef196873..0257ce1394 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.21.qualifier
-Bundle-Release-Date: 20240819
+Bundle-Version: 1.0.26.qualifier
+Bundle-Release-Date: 20241104
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 9a6c5ccc60..ad762c4ee9 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.21-SNAPSHOT
+ 1.0.26-SNAPSHOT
eclipse-plugin
diff --git a/server/bundles/io.cloudbeaver.service.fs/src/io/cloudbeaver/service/fs/WebServiceBindingFS.java b/server/bundles/io.cloudbeaver.service.fs/src/io/cloudbeaver/service/fs/WebServiceBindingFS.java
index d4d616dace..2d8b5515fe 100644
--- a/server/bundles/io.cloudbeaver.service.fs/src/io/cloudbeaver/service/fs/WebServiceBindingFS.java
+++ b/server/bundles/io.cloudbeaver.service.fs/src/io/cloudbeaver/service/fs/WebServiceBindingFS.java
@@ -118,6 +118,9 @@ public void bindWiring(DBWBindingContext model) throws DBWebException {
@Override
public void addServlets(CBApplication application, DBWServletContext servletContext) throws DBException {
+ if (!application.isMultiuser()) {
+ return;
+ }
servletContext.addServlet(
"fileSystems",
new WebFSServlet(application, getServiceImpl()),
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 53ac1a5dad..fa2514c98f 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
@@ -27,7 +27,7 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
-import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.ee10.servlet.ServletContextRequest;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.navigator.fs.DBNPathBase;
@@ -79,7 +79,7 @@ private void doGet(WebSession session, HttpServletRequest request, HttpServletRe
private void doPost(WebSession session, HttpServletRequest request, HttpServletResponse response) throws DBException, IOException {
// we need to set this attribute to get parts
- request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, new MultipartConfigElement(""));
+ request.setAttribute(ServletContextRequest.MULTIPART_CONFIG_ELEMENT, new MultipartConfigElement(""));
Map variables = getVariables(request);
String parentNodePath = JSONUtils.getString(variables, "toParentNodePath");
if (CommonUtils.isEmpty(parentNodePath)) {
diff --git a/server/bundles/io.cloudbeaver.service.ldap.auth/plugin.xml b/server/bundles/io.cloudbeaver.service.ldap.auth/plugin.xml
index 4abd568426..e908b7dc2a 100644
--- a/server/bundles/io.cloudbeaver.service.ldap.auth/plugin.xml
+++ b/server/bundles/io.cloudbeaver.service.ldap.auth/plugin.xml
@@ -12,16 +12,27 @@
-
+
+
+
+
-
-
+
diff --git a/server/bundles/io.cloudbeaver.service.ldap.auth/src/io/cloudbeaver/service/ldap/auth/LdapAuthProvider.java b/server/bundles/io.cloudbeaver.service.ldap.auth/src/io/cloudbeaver/service/ldap/auth/LdapAuthProvider.java
index 4a86e2fb4e..7bdee38129 100644
--- a/server/bundles/io.cloudbeaver.service.ldap.auth/src/io/cloudbeaver/service/ldap/auth/LdapAuthProvider.java
+++ b/server/bundles/io.cloudbeaver.service.ldap.auth/src/io/cloudbeaver/service/ldap/auth/LdapAuthProvider.java
@@ -25,6 +25,7 @@
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.DBPObject;
import org.jkiss.dbeaver.model.auth.SMSession;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
@@ -34,19 +35,22 @@
import org.jkiss.utils.CommonUtils;
import javax.naming.Context;
+import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.UUID;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
public class LdapAuthProvider implements SMAuthProviderExternal, SMBruteForceProtected {
+ private static final Log log = Log.getLog(LdapAuthProvider.class);
+
public LdapAuthProvider() {
}
+ @NotNull
@Override
public Map authExternalUser(
@NotNull DBRProgressMonitor monitor,
@@ -56,41 +60,113 @@ public Map authExternalUser(
if (providerConfig == null) {
throw new DBException("LDAP provider config is null");
}
- String userName = JSONUtils.getString(authParameters, LdapConstants.CRED_USERNAME);
+ String userName = JSONUtils.getString(authParameters, LdapConstants.CRED_USER_DN);
if (CommonUtils.isEmpty(userName)) {
- throw new DBException("LDAP user name is empty");
+ throw new DBException("LDAP user dn is empty");
}
String password = JSONUtils.getString(authParameters, LdapConstants.CRED_PASSWORD);
if (CommonUtils.isEmpty(password)) {
throw new DBException("LDAP password is empty");
}
- String unit = CommonUtils.nullIfEmpty(JSONUtils.getString(authParameters, LdapConstants.CRED_UNITS));
LdapSettings ldapSettings = new LdapSettings(providerConfig);
- Hashtable environment = new Hashtable<>();
- environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ Hashtable environment = creteAuthEnvironment(ldapSettings);
- var ldapProviderUrl = "ldap://" + ldapSettings.getHost() + ":" + ldapSettings.getPort();
- environment.put(Context.PROVIDER_URL, ldapProviderUrl);
- environment.put(Context.SECURITY_AUTHENTICATION, "simple");
+ String fullUserDN = userName;
+
+ if (!fullUserDN.startsWith(ldapSettings.getUserIdentifierAttr())) {
+ fullUserDN = String.join("=", ldapSettings.getUserIdentifierAttr(), userName);
+ }
+ if (CommonUtils.isNotEmpty(ldapSettings.getBaseDN()) && !fullUserDN.endsWith(ldapSettings.getBaseDN())) {
+ fullUserDN = String.join(",", fullUserDN, ldapSettings.getBaseDN());
+ }
- String cn = "cn=" + userName;
- var principal = Stream.of(cn, unit, ldapSettings.getBaseDN())
- .filter(CommonUtils::isNotEmpty)
- .collect(Collectors.joining(","));
+ validateUserAccess(fullUserDN, ldapSettings);
- environment.put(Context.SECURITY_PRINCIPAL, principal);
+ environment.put(Context.SECURITY_PRINCIPAL, fullUserDN);
environment.put(Context.SECURITY_CREDENTIALS, password);
+ DirContext context = null;
try {
- DirContext context = new InitialDirContext(environment);
- context.close();
+ context = new InitialDirContext(environment);
Map userData = new HashMap<>();
- userData.put(LdapConstants.CRED_USERNAME, userName);
+ userData.put(LdapConstants.CRED_USERNAME, findUserNameFromDN(fullUserDN, ldapSettings));
userData.put(LdapConstants.CRED_SESSION_ID, UUID.randomUUID());
return userData;
} catch (Exception e) {
throw new DBException("LDAP authentication failed: " + e.getMessage(), e);
+ } finally {
+ try {
+ if (context != null) {
+ context.close();
+ }
+ } catch (NamingException e) {
+ log.warn("Error closing LDAP user context", e);
+ }
+ }
+ }
+
+ private void validateUserAccess(@NotNull String fullUserDN, @NotNull LdapSettings ldapSettings) throws DBException {
+ if (
+ CommonUtils.isEmpty(ldapSettings.getFilter())
+ || CommonUtils.isEmpty(ldapSettings.getBindUserDN())
+ || CommonUtils.isEmpty(ldapSettings.getBindUserPassword())
+ ) {
+ return;
+ }
+
+ var environment = creteAuthEnvironment(ldapSettings);
+ environment.put(Context.SECURITY_PRINCIPAL, ldapSettings.getBindUserDN());
+ environment.put(Context.SECURITY_CREDENTIALS, ldapSettings.getBindUserPassword());
+ DirContext bindUserContext = null;
+ try {
+ bindUserContext = new InitialDirContext(environment);
+
+ SearchControls searchControls = new SearchControls();
+ searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+ searchControls.setTimeLimit(30_000);
+ var searchResult = bindUserContext.search(fullUserDN, ldapSettings.getFilter(), searchControls);
+ if (!searchResult.hasMore()) {
+ throw new DBException("Access denied");
+ }
+ } catch (DBException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new DBException("LDAP user access validation by filter failed: " + e.getMessage(), e);
+ } finally {
+ if (bindUserContext != null) {
+ try {
+ bindUserContext.close();
+ } catch (NamingException e) {
+ log.warn("Error closing LDAP bind user context", e);
+ }
+ }
+ }
+ }
+
+ @NotNull
+ private static Hashtable creteAuthEnvironment(LdapSettings ldapSettings) {
+ Hashtable environment = new Hashtable<>();
+ environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+
+ environment.put(Context.PROVIDER_URL, ldapSettings.getLdapProviderUrl());
+ environment.put(Context.SECURITY_AUTHENTICATION, "simple");
+ return environment;
+ }
+
+ @NotNull
+ private String findUserNameFromDN(@NotNull String fullUserDN, @NotNull LdapSettings ldapSettings)
+ throws DBException {
+ String userId = null;
+ for (String dn : fullUserDN.split(",")) {
+ if (dn.startsWith(ldapSettings.getUserIdentifierAttr() + "=")) {
+ userId = dn.split("=")[1];
+ break;
+ }
+ }
+ if (userId == null) {
+ throw new DBException("Failed to determinate userId from user DN: " + fullUserDN);
}
+ return userId;
}
@NotNull
diff --git a/server/bundles/io.cloudbeaver.service.ldap.auth/src/io/cloudbeaver/service/ldap/auth/LdapConstants.java b/server/bundles/io.cloudbeaver.service.ldap.auth/src/io/cloudbeaver/service/ldap/auth/LdapConstants.java
index 02fcfbea6d..dac3e989b1 100644
--- a/server/bundles/io.cloudbeaver.service.ldap.auth/src/io/cloudbeaver/service/ldap/auth/LdapConstants.java
+++ b/server/bundles/io.cloudbeaver.service.ldap.auth/src/io/cloudbeaver/service/ldap/auth/LdapConstants.java
@@ -20,10 +20,14 @@ public interface LdapConstants {
String PARAM_HOST = "ldap-host";
String PARAM_PORT = "ldap-port";
String PARAM_DN = "ldap-dn";
+ String PARAM_BIND_USER = "ldap-bind-user";
+ String PARAM_BIND_USER_PASSWORD = "ldap-bind-user-pwd";
+ String PARAM_FILTER = "ldap-filter";
+ String PARAM_USER_IDENTIFIER_ATTR = "ldap-identifier-attr";
String CRED_USERNAME = "user";
+ String CRED_USER_DN = "user-dn";
String CRED_PASSWORD = "password";
- String CRED_UNITS = "units";
String CRED_SESSION_ID = "session-id";
}
diff --git a/server/bundles/io.cloudbeaver.service.ldap.auth/src/io/cloudbeaver/service/ldap/auth/LdapSettings.java b/server/bundles/io.cloudbeaver.service.ldap.auth/src/io/cloudbeaver/service/ldap/auth/LdapSettings.java
index 9bce30ee14..538e2df1b3 100644
--- a/server/bundles/io.cloudbeaver.service.ldap.auth/src/io/cloudbeaver/service/ldap/auth/LdapSettings.java
+++ b/server/bundles/io.cloudbeaver.service.ldap.auth/src/io/cloudbeaver/service/ldap/auth/LdapSettings.java
@@ -28,13 +28,26 @@ public class LdapSettings {
@NotNull
private final String baseDN;
private final int port;
+ @NotNull
+ private final String userIdentifierAttr;
+ private final String bindUser;
+ private final String bindUserPassword;
+ private final String filter;
+
- protected LdapSettings(SMAuthProviderCustomConfiguration providerConfiguration) {
+ protected LdapSettings(
+ SMAuthProviderCustomConfiguration providerConfiguration
+ ) {
this.providerConfiguration = providerConfiguration;
this.host = providerConfiguration.getParameter(LdapConstants.PARAM_HOST);
this.port = CommonUtils.isNotEmpty(providerConfiguration.getParameter(LdapConstants.PARAM_PORT)) ? Integer.parseInt(
providerConfiguration.getParameter(LdapConstants.PARAM_PORT)) : 389;
this.baseDN = providerConfiguration.getParameterOrDefault(LdapConstants.PARAM_DN, "");
+ this.userIdentifierAttr = providerConfiguration.getParameterOrDefault(LdapConstants.PARAM_USER_IDENTIFIER_ATTR,
+ "cn");
+ this.bindUser = providerConfiguration.getParameterOrDefault(LdapConstants.PARAM_BIND_USER, "");
+ this.bindUserPassword = providerConfiguration.getParameterOrDefault(LdapConstants.PARAM_BIND_USER_PASSWORD, "");
+ this.filter = providerConfiguration.getParameterOrDefault(LdapConstants.PARAM_FILTER, "");
}
@@ -51,4 +64,25 @@ public String getHost() {
public int getPort() {
return port;
}
+
+ public String getLdapProviderUrl() {
+ return "ldap://" + getHost() + ":" + getPort();
+ }
+
+ @NotNull
+ public String getUserIdentifierAttr() {
+ return userIdentifierAttr;
+ }
+
+ public String getBindUserDN() {
+ return bindUser;
+ }
+
+ public String getBindUserPassword() {
+ return bindUserPassword;
+ }
+
+ public String getFilter() {
+ return filter;
+ }
}
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 867baf2c04..92add5dc98 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.107.qualifier
-Bundle-Release-Date: 20240819
+Bundle-Version: 1.0.112.qualifier
+Bundle-Release-Date: 20241104
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 d89f059707..2115c701fc 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.107-SNAPSHOT
+ 1.0.112-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 41e2d2849b..eb9b59eb3f 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.21.qualifier
-Bundle-Release-Date: 20240819
+Bundle-Version: 1.0.26.qualifier
+Bundle-Release-Date: 20241104
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 220c6d4113..fae03f750e 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.21-SNAPSHOT
+ 1.0.26-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 fa6dbedafb..4263eaeed0 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.56.qualifier
-Bundle-Release-Date: 20240819
+Bundle-Version: 1.0.61.qualifier
+Bundle-Release-Date: 20241104
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 564a6496fa..3b0f64b47b 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.56-SNAPSHOT
+ 1.0.61-SNAPSHOT
eclipse-plugin
diff --git a/server/bundles/io.cloudbeaver.service.rm/src/io/cloudbeaver/service/rm/impl/WebServiceRM.java b/server/bundles/io.cloudbeaver.service.rm/src/io/cloudbeaver/service/rm/impl/WebServiceRM.java
index 8af78a4819..4c412ecbd8 100644
--- a/server/bundles/io.cloudbeaver.service.rm/src/io/cloudbeaver/service/rm/impl/WebServiceRM.java
+++ b/server/bundles/io.cloudbeaver.service.rm/src/io/cloudbeaver/service/rm/impl/WebServiceRM.java
@@ -214,7 +214,7 @@ public boolean moveResource(@NotNull WebSession webSession,
WSResourceProperty.NAME);
return true;
} catch (Exception e) {
- throw new DBWebException("Error moving resource " + oldResourcePath, e);
+ throw new DBWebException(e.getMessage(), e);
}
}
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 a7d81b179c..f3de2d795c 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-Vendor: Cloudbeaver Web Service - Security
Bundle-Vendor: DBeaver Corp
Bundle-SymbolicName: io.cloudbeaver.service.security;singleton:=true
-Bundle-Version: 1.0.59.qualifier
-Bundle-Release-Date: 20240819
+Bundle-Version: 1.0.64.qualifier
+Bundle-Release-Date: 20241104
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
diff --git a/server/bundles/io.cloudbeaver.service.security/pom.xml b/server/bundles/io.cloudbeaver.service.security/pom.xml
index 08b8d0ca0c..9f682a8392 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.59-SNAPSHOT
+ 1.0.64-SNAPSHOT
eclipse-plugin
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 7b21ede7fb..f8ac85aa1c 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
@@ -21,9 +21,9 @@
import com.google.gson.reflect.TypeToken;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.auth.*;
-import io.cloudbeaver.model.app.WebAppConfiguration;
import io.cloudbeaver.model.app.WebAuthApplication;
import io.cloudbeaver.model.app.WebAuthConfiguration;
+import io.cloudbeaver.model.config.SMControllerConfiguration;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import io.cloudbeaver.registry.WebMetaParametersRegistry;
@@ -515,7 +515,7 @@ public SMUser[] findUsers(@NotNull SMUserFilter filter)
try (PreparedStatement dbStat = dbCon.prepareStatement(
database.normalizeTableNames("SELECT USER_ID,IS_ACTIVE,DEFAULT_AUTH_ROLE FROM {table_prefix}CB_USER"
+ buildUsersFilter(filter) + "\nORDER BY USER_ID " + getOffsetLimitPart(filter)))) {
- int parameterIndex = setUsersFilterValues(dbStat, filter, 1);
+ setUsersFilterValues(dbStat, filter, 1);
try (ResultSet dbResult = dbStat.executeQuery()) {
while (dbResult.next()) {
@@ -531,14 +531,11 @@ public SMUser[] findUsers(@NotNull SMUserFilter filter)
}
readSubjectsMetas(dbCon, SMSubjectType.user, filter.getUserIdMask(), result);
- StringBuilder teamsSql = new StringBuilder()
- .append("SELECT USER_ID,TEAM_ID FROM {table_prefix}CB_USER_TEAM")
- .append("\n")
- .append("WHERE USER_ID IN (")
- .append(SQLUtils.generateParamList(result.size()))
- .append(")");
+ String teamsSql =
+ "SELECT USER_ID,TEAM_ID FROM {table_prefix}CB_USER_TEAM\n" +
+ "WHERE USER_ID IN (" + SQLUtils.generateParamList(result.size()) + ")";
// Read teams
- try (PreparedStatement dbStat = dbCon.prepareStatement(database.normalizeTableNames(teamsSql.toString()))) {
+ try (PreparedStatement dbStat = dbCon.prepareStatement(database.normalizeTableNames(teamsSql))) {
int parameterIndex = 1;
for (String userId : result.keySet()) {
dbStat.setString(parameterIndex++, userId);
@@ -574,7 +571,7 @@ private String buildUsersFilter(SMUserFilter filter) {
if (filter.getEnabledState() != null) {
whereParts.add("IS_ACTIVE=?");
}
- if (whereParts.size() > 0) {
+ if (!whereParts.isEmpty()) {
where.append(whereParts.stream().collect(Collectors.joining(" AND ", " WHERE ", "")));
}
return where.toString();
@@ -858,7 +855,7 @@ public void setUserCredentials(
String encodedValue = CommonUtils.toString(cred.getValue());
encodedValue = property.getEncryption().encrypt(userId, encodedValue);
return new String[]{propertyName, encodedValue};
- }).collect(Collectors.toList());
+ }).toList();
} catch (Exception e) {
throw new DBCException(e.getMessage(), e);
}
@@ -1037,21 +1034,19 @@ public String[] getUserLinkedProviders(@NotNull String userId) throws DBExceptio
@NotNull
@Override
- public SMPropertyDescriptor[] getMetaParametersBySubjectType(SMSubjectType subjectType) throws DBException {
+ public SMPropertyDescriptor[] getMetaParametersBySubjectType(SMSubjectType subjectType) {
// First add global metas
List props = new ArrayList<>(
WebMetaParametersRegistry.getInstance().getMetaParameters(subjectType));
// Add metas from enabled auth providers
- WebAppConfiguration appConfiguration = application.getAppConfiguration();
- if (appConfiguration instanceof WebAuthConfiguration) {
- for (String apId : ((WebAuthConfiguration) appConfiguration).getEnabledAuthProviders()) {
- WebAuthProviderDescriptor ap = WebAuthProviderRegistry.getInstance().getAuthProvider(apId);
- if (ap != null) {
- List metaProps = ap.getMetaParameters(SMSubjectType.team);
- if (!CommonUtils.isEmpty(metaProps)) {
- props.addAll(metaProps);
- }
+ WebAuthConfiguration authConfiguration = application.getAuthConfiguration();
+ for (String apId : authConfiguration.getEnabledAuthProviders()) {
+ WebAuthProviderDescriptor ap = WebAuthProviderRegistry.getInstance().getAuthProvider(apId);
+ if (ap != null) {
+ List metaProps = ap.getMetaParameters(SMSubjectType.team);
+ if (!CommonUtils.isEmpty(metaProps)) {
+ props.addAll(metaProps);
}
}
}
@@ -1071,9 +1066,10 @@ public SMTeam[] readAllTeams() throws DBCException {
String defaultUserTeam = getDefaultUserTeam();
Map teams = new LinkedHashMap<>();
String query = database.normalizeTableNames(
- "SELECT T.*, S.IS_SECRET_STORAGE FROM {table_prefix}CB_TEAM T, " +
- "{table_prefix}CB_AUTH_SUBJECT S " +
- "WHERE T.TEAM_ID IN (S.SUBJECT_ID, ?) ORDER BY TEAM_ID");
+ """
+ SELECT T.*, S.IS_SECRET_STORAGE FROM {table_prefix}CB_TEAM T, \
+ {table_prefix}CB_AUTH_SUBJECT S \
+ WHERE T.TEAM_ID IN (S.SUBJECT_ID, ?) ORDER BY TEAM_ID""");
try (PreparedStatement dbPreparedStatement = dbCon.prepareStatement(query)) {
dbPreparedStatement.setString(1, defaultUserTeam);
try (ResultSet dbResult = dbPreparedStatement.executeQuery()) {
@@ -1083,9 +1079,11 @@ public SMTeam[] readAllTeams() throws DBCException {
}
}
}
- query = database.normalizeTableNames("SELECT SUBJECT_ID,PERMISSION_ID\n" +
- "FROM {table_prefix}CB_AUTH_PERMISSIONS AP, {table_prefix}CB_TEAM R\n" +
- "WHERE AP.SUBJECT_ID IN (R.TEAM_ID,?)\n");
+ query = database.normalizeTableNames("""
+ SELECT SUBJECT_ID,PERMISSION_ID
+ FROM {table_prefix}CB_AUTH_PERMISSIONS AP, {table_prefix}CB_TEAM R
+ WHERE AP.SUBJECT_ID IN (R.TEAM_ID,?)
+ """);
try (PreparedStatement dbPreparedStatement = dbCon.prepareStatement(query)) {
dbPreparedStatement.setString(1, defaultUserTeam);
try (ResultSet dbResult = dbPreparedStatement.executeQuery()) {
@@ -1422,7 +1420,7 @@ private String createSmSession(
@NotNull Map parameters,
@NotNull SMSessionType sessionType,
Connection dbCon
- ) throws SQLException, DBException {
+ ) throws SQLException {
var sessionId = UUID.randomUUID().toString();
try (PreparedStatement dbStat = dbCon.prepareStatement(
database.normalizeTableNames(
@@ -1754,11 +1752,10 @@ private void updateAuthStatus(
SMAuthConfigurationReference providerId = entry.getKey();
String authJson = gson.toJson(entry.getValue());
boolean configIdExist = providerId.getAuthProviderConfigurationId() != null;
- var sqlBuilder = new StringBuilder();
- sqlBuilder.append("UPDATE {table_prefix}CB_AUTH_ATTEMPT_INFO SET AUTH_STATE=? ")
- .append("WHERE AUTH_ID=? AND AUTH_PROVIDER_ID=? AND ")
- .append(configIdExist ? "AUTH_PROVIDER_CONFIGURATION_ID=?" : "AUTH_PROVIDER_CONFIGURATION_ID IS NULL");
- try (PreparedStatement dbStat = dbCon.prepareStatement(database.normalizeTableNames(sqlBuilder.toString()))) {
+ String sqlBuilder = "UPDATE {table_prefix}CB_AUTH_ATTEMPT_INFO SET AUTH_STATE=? " +
+ "WHERE AUTH_ID=? AND AUTH_PROVIDER_ID=? AND " +
+ (configIdExist ? "AUTH_PROVIDER_CONFIGURATION_ID=?" : "AUTH_PROVIDER_CONFIGURATION_ID IS NULL");
+ try (PreparedStatement dbStat = dbCon.prepareStatement(database.normalizeTableNames(sqlBuilder))) {
dbStat.setString(1, authJson);
dbStat.setString(2, authId);
dbStat.setString(3, providerId.getAuthProviderId());
@@ -1848,16 +1845,14 @@ private SMAuthInfo getAuthStatus(@NotNull String authId, boolean readExpiredData
if (authProviderConfiguration != null) {
WebAuthProviderDescriptor authProviderDescriptor = getAuthProvider(authProviderId);
var authProviderInstance = authProviderDescriptor.getInstance();
- if (SMAuthProviderFederated.class.isAssignableFrom(authProviderInstance.getClass())) {
- signInLink = buildRedirectLink(((SMAuthProviderFederated) authProviderInstance).getRedirectLink(
+ if (authProviderInstance instanceof SMAuthProviderFederated providerFederated) {
+ signInLink = buildRedirectLink(providerFederated.getRedirectLink(
authProviderConfiguration,
Map.of()), authId);
- var userCustomSignOutLink =
- ((SMAuthProviderFederated) authProviderInstance).getUserSignOutLink(
- application.getAuthConfiguration()
- .getAuthProviderConfiguration(authProviderConfiguration),
- authProviderData);
- signOutLink = userCustomSignOutLink;
+ signOutLink = providerFederated.getUserSignOutLink(
+ application.getAuthConfiguration()
+ .getAuthProviderConfiguration(authProviderConfiguration),
+ authProviderData);
}
}
@@ -1867,16 +1862,13 @@ private SMAuthInfo getAuthStatus(@NotNull String authId, boolean readExpiredData
}
if (smAuthStatus != SMAuthStatus.SUCCESS) {
- switch (smAuthStatus) {
- case IN_PROGRESS:
- return SMAuthInfo.inProgress(authId, signInLink, signOutLink, authData, isMainAuth, forceSessionsLogout);
- case ERROR:
- return SMAuthInfo.error(authId, authError, isMainAuth, errorCode);
- case EXPIRED:
- return SMAuthInfo.expired(authId, readExpiredData ? authData : Map.of(), isMainAuth);
- default:
- throw new SMException("Unknown auth status:" + smAuthStatus);
- }
+ return switch (smAuthStatus) {
+ case IN_PROGRESS ->
+ SMAuthInfo.inProgress(authId, signInLink, signOutLink, authData, isMainAuth, forceSessionsLogout);
+ case ERROR -> SMAuthInfo.error(authId, authError, isMainAuth, errorCode);
+ case EXPIRED -> SMAuthInfo.expired(authId, readExpiredData ? authData : Map.of(), isMainAuth);
+ default -> throw new SMException("Unknown auth status:" + smAuthStatus);
+ };
}
SMTokens smTokens = findTokenBySmSession(smSessionId);
@@ -2090,7 +2082,7 @@ public SMAuthInfo finishAuthentication(@NotNull String authId) throws DBExceptio
return finishAuthentication(authInfo, false, authInfo.isForceSessionsLogout());
}
- private SMAuthInfo finishAuthentication(
+ protected SMAuthInfo finishAuthentication(
@NotNull SMAuthInfo authInfo,
boolean isSyncAuth,
boolean forceSessionsLogout
@@ -2114,7 +2106,7 @@ private SMAuthInfo finishAuthentication(
String activeUserId = null;
if (!isMainAuthSession) {
var accessToken = findTokenBySmSession(authAttemptSessionInfo.getSmSessionId()).getSmAccessToken();
- //this is an additional authorization and we should to return the original permissions and userId
+ //this is an additional authorization, and we should to return the original permissions and userId
permissions = getTokenPermissions(accessToken);
activeUserId = permissions.getUserId();
}
@@ -2260,10 +2252,9 @@ private void autoUpdateUserTeams(
String userId,
SMTeam[] allTeams
) throws DBCException {
- if (!(authProvider.getInstance() instanceof SMAuthProviderAssigner)) {
+ if (!(authProvider.getInstance() instanceof SMAuthProviderAssigner authProviderAssigner)) {
return;
}
- SMAuthProviderAssigner authProviderAssigner = (SMAuthProviderAssigner) authProvider.getInstance();
String externalTeamIdMetadataFieldName = authProviderAssigner.getExternalTeamIdMetadataFieldName();
if (!CommonUtils.isEmpty(externalTeamIdMetadataFieldName)) {
@@ -2583,8 +2574,9 @@ private SMAuthPermissions getTokenPermissions(@NotNull String token) throws DBEx
String authRole;
try (Connection dbCon = database.openConnection();
PreparedStatement dbStat = dbCon.prepareStatement(
- database.normalizeTableNames("SELECT USER_ID, EXPIRATION_TIME, SESSION_ID, AUTH_ROLE FROM {table_prefix}CB_AUTH_TOKEN " +
- "WHERE TOKEN_ID=?"));
+ database.normalizeTableNames("""
+ SELECT USER_ID, EXPIRATION_TIME, SESSION_ID, AUTH_ROLE FROM {table_prefix}CB_AUTH_TOKEN \
+ WHERE TOKEN_ID=?"""))
) {
dbStat.setString(1, token);
try (var dbResult = dbStat.executeQuery()) {
@@ -2608,17 +2600,14 @@ private SMAuthPermissions getTokenPermissions(@NotNull String token) throws DBEx
@Override
public SMAuthProviderDescriptor[] getAvailableAuthProviders() throws DBException {
- if (!(application.getAppConfiguration() instanceof WebAuthConfiguration)) {
- throw new DBException("Web application doesn't support external authentication");
- }
- WebAuthConfiguration appConfiguration = (WebAuthConfiguration) application.getAppConfiguration();
+ WebAuthConfiguration appConfiguration = application.getAuthConfiguration();
Set customConfigurations = appConfiguration.getAuthCustomConfigurations();
List providers = WebAuthProviderRegistry.getInstance().getAuthProviders().stream()
.filter(ap ->
!ap.isTrusted() &&
appConfiguration.isAuthProviderEnabled(ap.getId()) &&
(!ap.isConfigurable() || hasProviderConfiguration(ap, customConfigurations)))
- .map(WebAuthProviderDescriptor::createDescriptorBean).collect(Collectors.toList());
+ .map(WebAuthProviderDescriptor::createDescriptorBean).toList();
if (!CommonUtils.isEmpty(customConfigurations)) {
// Attach custom configs to providers
@@ -3009,12 +2998,12 @@ public List getObjectPermissionGrants(
var grantedPermissionsBySubjectId = new HashMap();
try (Connection dbCon = database.openConnection()) {
try (PreparedStatement dbStat = dbCon.prepareStatement(database.normalizeTableNames(
- "SELECT OP.SUBJECT_ID,S.SUBJECT_TYPE, OP.PERMISSION\n" +
- "FROM {table_prefix}CB_OBJECT_PERMISSIONS OP, {table_prefix}CB_AUTH_SUBJECT S\n" +
- "WHERE S.SUBJECT_ID = OP.SUBJECT_ID AND OP.OBJECT_TYPE=? AND OP.OBJECT_ID=?"))) {
+ """
+ SELECT OP.SUBJECT_ID,S.SUBJECT_TYPE, OP.PERMISSION
+ FROM {table_prefix}CB_OBJECT_PERMISSIONS OP, {table_prefix}CB_AUTH_SUBJECT S
+ WHERE S.SUBJECT_ID = OP.SUBJECT_ID AND OP.OBJECT_TYPE=? AND OP.OBJECT_ID=?"""))) {
dbStat.setString(1, smObjectType.name());
dbStat.setString(2, objectId);
- List result = new ArrayList<>();
try (ResultSet dbResult = dbStat.executeQuery()) {
while (dbResult.next()) {
String subjectId = dbResult.getString(1);
@@ -3063,7 +3052,7 @@ public List getSubjectObjectPermissionGrants(@NotNull
}
return grantedPermissionsByObjectId.values().stream()
.map(SMObjectPermissionsGrant.Builder::build)
- .collect(Collectors.toList());
+ .toList();
}
} catch (SQLException e) {
@@ -3145,7 +3134,7 @@ private void deleteAuthSubject(Connection dbCon, String subjectId) throws SQLExc
}
}
- private WebAuthProviderDescriptor getAuthProvider(String authProviderId) throws DBCException {
+ protected WebAuthProviderDescriptor getAuthProvider(String authProviderId) throws DBCException {
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(authProviderId);
if (authProvider == null) {
throw new DBCException("Auth provider not found: " + authProviderId);
@@ -3211,11 +3200,10 @@ public void clearOldAuthAttemptInfo() throws DBException {
public Set getFilteredSubjects(Set allSubjects) {
try (Connection dbCon = database.openConnection()) {
Set result = new HashSet<>();
- var sqlBuilder = new StringBuilder("SELECT SUBJECT_ID FROM {table_prefix}CB_AUTH_SUBJECT U ")
- .append("WHERE SUBJECT_ID IN (")
- .append(SQLUtils.generateParamList(allSubjects.size()))
- .append(")");
- try (var dbStat = dbCon.prepareStatement(database.normalizeTableNames(sqlBuilder.toString()))) {
+ String sqlBuilder =
+ "SELECT SUBJECT_ID FROM {table_prefix}CB_AUTH_SUBJECT U " +
+ "WHERE SUBJECT_ID IN (" + SQLUtils.generateParamList(allSubjects.size()) + ")";
+ try (var dbStat = dbCon.prepareStatement(database.normalizeTableNames(sqlBuilder))) {
int parameterIndex = 1;
for (String subjectId : allSubjects) {
dbStat.setString(parameterIndex++, subjectId);
@@ -3235,7 +3223,6 @@ public Set getFilteredSubjects(Set allSubjects) {
private SMSubjectType getSubjectType(@NotNull String subjectId) {
try (Connection dbCon = database.openConnection()) {
- Set result = new HashSet<>();
String sqlBuilder = "SELECT SUBJECT_TYPE FROM {table_prefix}CB_AUTH_SUBJECT U WHERE SUBJECT_ID = ?";
try (var dbStat = dbCon.prepareStatement(database.normalizeTableNames(sqlBuilder))) {
dbStat.setString(1, subjectId);
diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/EmbeddedSecurityControllerFactory.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/EmbeddedSecurityControllerFactory.java
index 6c7b581b13..1b1a7405fa 100644
--- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/EmbeddedSecurityControllerFactory.java
+++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/EmbeddedSecurityControllerFactory.java
@@ -18,8 +18,9 @@
import io.cloudbeaver.auth.NoAuthCredentialsProvider;
import io.cloudbeaver.model.app.WebAuthApplication;
+import io.cloudbeaver.model.config.SMControllerConfiguration;
+import io.cloudbeaver.model.config.WebDatabaseConfig;
import io.cloudbeaver.service.security.db.CBDatabase;
-import io.cloudbeaver.service.security.db.WebDatabaseConfig;
import io.cloudbeaver.service.security.internal.ClearAuthAttemptInfoJob;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/bruteforce/BruteForceUtils.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/bruteforce/BruteForceUtils.java
index fecf508ec6..9a0bb08e27 100644
--- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/bruteforce/BruteForceUtils.java
+++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/bruteforce/BruteForceUtils.java
@@ -16,7 +16,7 @@
*/
package io.cloudbeaver.service.security.bruteforce;
-import io.cloudbeaver.service.security.SMControllerConfiguration;
+import io.cloudbeaver.model.config.SMControllerConfiguration;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.auth.SMAuthStatus;
diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java
index 0c373781e4..3484e47d52 100644
--- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java
+++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java
@@ -18,8 +18,10 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
+import com.google.gson.Strictness;
import io.cloudbeaver.auth.provider.local.LocalAuthProviderConstants;
import io.cloudbeaver.model.app.WebApplication;
+import io.cloudbeaver.model.config.WebDatabaseConfig;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import io.cloudbeaver.utils.WebAppUtils;
@@ -300,7 +302,9 @@ CBDatabaseInitialData getInitialData() throws DBException {
initialDataPath = WebAppUtils.getRelativePath(
databaseConfiguration.getInitialDataConfiguration(), application.getHomeDirectory());
try (Reader reader = new InputStreamReader(new FileInputStream(initialDataPath), StandardCharsets.UTF_8)) {
- Gson gson = new GsonBuilder().setLenient().create();
+ Gson gson = new GsonBuilder()
+ .setStrictness(Strictness.LENIENT)
+ .create();
return gson.fromJson(reader, CBDatabaseInitialData.class);
} catch (Exception e) {
throw new DBException("Error loading initial data configuration", e);
diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/internal/utils/DBConfigurationUtils.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/internal/utils/DBConfigurationUtils.java
index 8d1ac5d980..d025c18221 100644
--- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/internal/utils/DBConfigurationUtils.java
+++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/internal/utils/DBConfigurationUtils.java
@@ -16,7 +16,7 @@
*/
package io.cloudbeaver.service.security.internal.utils;
-import io.cloudbeaver.service.security.db.WebDatabaseConfig;
+import io.cloudbeaver.model.config.WebDatabaseConfig;
import org.jkiss.code.Nullable;
import org.jkiss.utils.CommonUtils;
diff --git a/server/bundles/io.cloudbeaver.slf4j/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.slf4j/META-INF/MANIFEST.MF
index 46a21cc5e9..3eb22bf581 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.19.qualifier
-Bundle-Release-Date: 20240819
+Bundle-Version: 1.0.24.qualifier
+Bundle-Release-Date: 20241104
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 6045e7e856..bcc71fa113 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.19-SNAPSHOT
+ 1.0.24-SNAPSHOT
eclipse-plugin
diff --git a/server/drivers/db2-jt400/pom.xml b/server/drivers/db2-jt400/pom.xml
index 5158c8c7f2..05cdc02a0a 100644
--- a/server/drivers/db2-jt400/pom.xml
+++ b/server/drivers/db2-jt400/pom.xml
@@ -18,7 +18,7 @@
net.sf.jt400
jt400
- 10.5
+ 20.0.7
diff --git a/server/drivers/mysql/pom.xml b/server/drivers/mysql/pom.xml
index 7f993d3be7..76959e3ab1 100644
--- a/server/drivers/mysql/pom.xml
+++ b/server/drivers/mysql/pom.xml
@@ -20,6 +20,10 @@
mysql-connector-j
8.2.0
+
+ com.google.protobuf
+ protobuf-java
+
diff --git a/server/drivers/pom.xml b/server/drivers/pom.xml
index 39fecc0302..06766cf06e 100644
--- a/server/drivers/pom.xml
+++ b/server/drivers/pom.xml
@@ -1,7 +1,12 @@
4.0.0
- io.cloudbeaver
+
+ io.cloudbeaver
+ cloudbeaver
+ 1.0.0-SNAPSHOT
+ ../
+
drivers
1.0.0
pom
diff --git a/server/drivers/sqlserver/pom.xml b/server/drivers/sqlserver/pom.xml
index 2ae93c9fe7..71138b49aa 100644
--- a/server/drivers/sqlserver/pom.xml
+++ b/server/drivers/sqlserver/pom.xml
@@ -18,7 +18,7 @@
com.microsoft.sqlserver
mssql-jdbc
- 8.2.0.jre8
+ 12.8.0.jre11
diff --git a/server/features/io.cloudbeaver.server.feature/pom.xml b/server/features/io.cloudbeaver.server.feature/pom.xml
index df176583f2..5cb3dc1d02 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.1.5-SNAPSHOT
+ 24.2.4-SNAPSHOT
eclipse-feature
diff --git a/server/features/io.cloudbeaver.ws.feature/feature.xml b/server/features/io.cloudbeaver.ws.feature/feature.xml
index e69666f3a5..d771aa8a70 100644
--- a/server/features/io.cloudbeaver.ws.feature/feature.xml
+++ b/server/features/io.cloudbeaver.ws.feature/feature.xml
@@ -2,7 +2,7 @@
@@ -15,7 +15,6 @@
-
diff --git a/server/features/io.cloudbeaver.ws.feature/pom.xml b/server/features/io.cloudbeaver.ws.feature/pom.xml
index e3b43cfd37..c2f998b10c 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.57-SNAPSHOT
+ 1.0.62-SNAPSHOT
eclipse-feature
diff --git a/server/pom.xml b/server/pom.xml
index fc94b4b30f..d8fb5d9272 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -19,19 +19,28 @@
CloudBeaver CE
- 24.1.5
+ 24.2.4
bundles
features
- drivers
- test
-
-
- product
+
+
+
+ full-build
+ !plain-api-server
+
+ drivers
+
+ product
+ test
+
+
+
+
diff --git a/server/product/aggregate/build-full.cmd b/server/product/aggregate/build-full.cmd
index 6aacb1a7a6..c660606cb6 100644
--- a/server/product/aggregate/build-full.cmd
+++ b/server/product/aggregate/build-full.cmd
@@ -1,6 +1,6 @@
@echo off
set MAVEN_OPTS=-Xmx2048m
-call mvn clean install -Dheadless-platform
+call mvn clean install -Dheadless-platform -T 1C
pause
diff --git a/server/product/web-server/CloudbeaverServer.product b/server/product/web-server/CloudbeaverServer.product
index 8e03bd0a5a..1cd6808668 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 c0faddf5e7..a0a46f5953 100644
--- a/server/product/web-server/pom.xml
+++ b/server/product/web-server/pom.xml
@@ -9,7 +9,7 @@
1.0.0-SNAPSHOT
../../
- 24.1.5-SNAPSHOT
+ 24.2.4-SNAPSHOT
web-server
eclipse-repository
Cloudbeaver Server Product
diff --git a/server/test/io.cloudbeaver.test.platform/META-INF/MANIFEST.MF b/server/test/io.cloudbeaver.test.platform/META-INF/MANIFEST.MF
index 12ada095bf..29e094999e 100644
--- a/server/test/io.cloudbeaver.test.platform/META-INF/MANIFEST.MF
+++ b/server/test/io.cloudbeaver.test.platform/META-INF/MANIFEST.MF
@@ -8,7 +8,6 @@ Bundle-Vendor: DBeaver Corp
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Require-Bundle: org.eclipse.core.runtime,
- org.eclipse.core.resources,
org.junit,
org.mockito.mockito-core,
org.apache.felix.scr,
diff --git a/server/test/io.cloudbeaver.test.platform/workspace/conf/cloudbeaver.conf b/server/test/io.cloudbeaver.test.platform/workspace/conf/cloudbeaver.conf
index cd8725bd8d..4511bd491c 100644
--- a/server/test/io.cloudbeaver.test.platform/workspace/conf/cloudbeaver.conf
+++ b/server/test/io.cloudbeaver.test.platform/workspace/conf/cloudbeaver.conf
@@ -1,48 +1,48 @@
{
server: {
serverPort: "${CLOUDBEAVER_TEST_PORT:18978}",
- serverName: "CloudBeaver CE Test Server",
+ serverName: "${CLOUDBEAVER_SERVER_NAME:CloudBeaver CE Test Server}",
- workspaceLocation: "workspace",
+ workspaceLocation: "${CLOUDBEAVER_WORKSPACE_LOCATION:workspace}",
contentRoot: "workspace/web",
driversLocation: "../../../deploy/",
- rootURI: "/",
+ rootURI: "${CLOUDBEAVER_ROOT_URI:/}",
serviceURI: "/api/",
productSettings: {},
- expireSessionAfterPeriod: 1800000,
+ expireSessionAfterPeriod: "${CLOUDBEAVER_EXPIRE_SESSION_AFTER_PERIOD:1800000}",
- develMode: false,
+ develMode: "${CLOUDBEAVER_DEVEL_MODE:false}",
sm: {
enableBruteForceProtection: "${CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED:false}"
},
database: {
- driver="h2_embedded_v2",
- url: "jdbc:h2:mem:testdb",
+ driver: "${CLOUDBEAVER_DB_DRIVER:h2_embedded_v2}",
+ url: "${CLOUDBEAVER_DB_URL:jdbc:h2:mem:testdb}",
- createDatabase: true,
+ createDatabase: "${CLOUDBEAVER_CREATE_DATABASE:true}",
- initialDataConfiguration: "workspace/conf/initial-data.conf",
+ initialDataConfiguration: "${CLOUDBEAVER_DB_INITIAL_DATA:workspace/conf/initial-data.conf}",
pool: {
- minIdleConnections: 4,
- maxIdleConnections: 10,
- maxConnections: 100,
- validationQuery: "SELECT 1"
+ minIdleConnections: "${CLOUDBEAVER_DB_MIN_IDLE_CONNECTIONS:4}",
+ maxIdleConnections: "${CLOUDBEAVER_DB_MAX_IDLE_CONNECTIONS:10}",
+ maxConnections: "${CLOUDBEAVER_DB_MAX_CONNECTIONS:100}",
+ validationQuery: "${CLOUDBEAVER_DB_VALIDATION_QUERY:SELECT 1}"
}
}
},
app: {
- anonymousAccessEnabled: true,
- anonymousUserRole: "user",
- defaultUserTeam: "user",
- supportsCustomConnections: true,
- enableReverseProxyAuth: true,
+ anonymousAccessEnabled: "${CLOUDBEAVER_APP_ANONYMOUS_ACCESS_ENABLED:true}",
+ anonymousUserRole: user,
+ defaultUserTeam: "${CLOUDBEAVER_APP_DEFAULT_USER_TEAM:user}",
+ supportsCustomConnections: "${CLOUDBEAVER_APP_SUPPORTS_CUSTOM_CONNECTIONS:true}",
+ enableReverseProxyAuth: "${CLOUDBEAVER_APP_ENABLE_REVERSE_PROXY_AUTH:true}",
enabledAuthProviders: [
"local",
"reverseProxy"
@@ -52,13 +52,12 @@
],
resourceQuotas: {
- dataExportFileSizeLimit: 10000000,
- sqlMaxRunningQueries: 100,
- sqlResultSetRowsLimit: 100000,
- sqlResultSetMemoryLimit: 2000000,
- sqlTextPreviewMaxLength: 4096,
- sqlBinaryPreviewMaxLength: 261120,
- sqlQueryTimeout: 5
+ dataExportFileSizeLimit: "${CLOUDBEAVER_RESOURCE_QUOTA_DATA_EXPORT_FILE_SIZE_LIMIT:10000000}",
+ sqlMaxRunningQueries: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_MAX_RUNNING_QUERIES:100}",
+ sqlResultSetRowsLimit: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_RESULT_SET_ROWS_LIMIT:100000}",
+ sqlTextPreviewMaxLength: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_TEXT_PREVIEW_MAX_LENGTH:4096}",
+ sqlBinaryPreviewMaxLength: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_BINARY_PREVIEW_MAX_LENGTH:261120}",
+ sqlQueryTimeout: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_QUERY_TIMEOUT:5}"
},
disabledDrivers: [
diff --git a/webapp/package.json b/webapp/package.json
index f16b36fbc3..8ff63b4aed 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -1,6 +1,7 @@
{
"name": "cloudbeaver-ce",
"version": "1.0.0",
+ "type": "module",
"private": true,
"workspaces": {
"packages": [
@@ -35,7 +36,7 @@
"@testing-library/user-event": "^14",
"@types/react": "^18",
"@types/react-dom": "^18",
- "concurrently": "^8",
+ "concurrently": "^9",
"husky": "^9",
"lerna": "^5",
"mobx": "^6",
diff --git a/webapp/packages/core-administration/package.json b/webapp/packages/core-administration/package.json
index 4ca22be149..fb46112aef 100644
--- a/webapp/packages/core-administration/package.json
+++ b/webapp/packages/core-administration/package.json
@@ -1,5 +1,6 @@
{
"name": "@cloudbeaver/core-administration",
+ "type": "module",
"sideEffects": [
"src/**/*.css",
"src/**/*.scss",
diff --git a/webapp/packages/core-administration/src/AdministrationItem/AdministrationItemService.ts b/webapp/packages/core-administration/src/AdministrationItem/AdministrationItemService.ts
index 422e719751..1cae5f2989 100644
--- a/webapp/packages/core-administration/src/AdministrationItem/AdministrationItemService.ts
+++ b/webapp/packages/core-administration/src/AdministrationItem/AdministrationItemService.ts
@@ -8,13 +8,18 @@
import { makeObservable, observable } from 'mobx';
import { injectable } from '@cloudbeaver/core-di';
-import { Executor, IExecutor, IExecutorHandler } from '@cloudbeaver/core-executor';
+import { Executor, type IExecutor, type IExecutorHandler } from '@cloudbeaver/core-executor';
import type { RouterState } from '@cloudbeaver/core-routing';
-import { filterConfigurationWizard } from './filterConfigurationWizard';
-import { AdministrationItemType, IAdministrationItem, IAdministrationItemOptions, IAdministrationItemSubItem } from './IAdministrationItem';
-import type { IAdministrationItemRoute } from './IAdministrationItemRoute';
-import { orderAdministrationItems } from './orderAdministrationItems';
+import { filterConfigurationWizard } from './filterConfigurationWizard.js';
+import {
+ AdministrationItemType,
+ type IAdministrationItem,
+ type IAdministrationItemOptions,
+ type IAdministrationItemSubItem,
+} from './IAdministrationItem.js';
+import type { IAdministrationItemRoute } from './IAdministrationItemRoute.js';
+import { orderAdministrationItems } from './orderAdministrationItems.js';
interface IActivationData {
screen: IAdministrationItemRoute;
@@ -112,14 +117,14 @@ export class AdministrationItemService {
return onlyActive.name;
}
- return items[0].name;
+ return items[0]?.name || null;
}
getAdministrationItemRoute(state: RouterState, configurationMode = false): IAdministrationItemRoute {
return {
- item: state.params.item || this.getDefaultItem(configurationMode),
- sub: state.params.sub || null,
- param: state.params.param || null,
+ item: state.params['item'] || this.getDefaultItem(configurationMode),
+ sub: state.params['sub'] || null,
+ param: state.params['param'] || null,
};
}
@@ -162,7 +167,7 @@ export class AdministrationItemService {
};
const index = this.items.push(item);
- return this.items[index - 1];
+ return this.items[index - 1]!;
}
async activate(screen: IAdministrationItemRoute, configurationWizard: boolean, outside: boolean, outsideAdminPage: boolean): Promise {
@@ -241,7 +246,7 @@ export class AdministrationItemService {
if (item === items.length) {
break;
}
- await items[item].configurationWizardOptions?.onLoad?.();
+ await items[item]?.configurationWizardOptions?.onLoad?.();
item++;
}
}
diff --git a/webapp/packages/core-administration/src/AdministrationItem/IAdministrationItem.ts b/webapp/packages/core-administration/src/AdministrationItem/IAdministrationItem.ts
index 8b1ec7a058..dce7254076 100644
--- a/webapp/packages/core-administration/src/AdministrationItem/IAdministrationItem.ts
+++ b/webapp/packages/core-administration/src/AdministrationItem/IAdministrationItem.ts
@@ -5,7 +5,7 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
-import type { IRouteParams } from './IRouteParams';
+import type { IRouteParams } from './IRouteParams.js';
export enum AdministrationItemType {
Default,
diff --git a/webapp/packages/core-administration/src/AdministrationItem/filterConfigurationWizard.ts b/webapp/packages/core-administration/src/AdministrationItem/filterConfigurationWizard.ts
index f070fb1745..816d7b3d1f 100644
--- a/webapp/packages/core-administration/src/AdministrationItem/filterConfigurationWizard.ts
+++ b/webapp/packages/core-administration/src/AdministrationItem/filterConfigurationWizard.ts
@@ -5,7 +5,7 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
-import { AdministrationItemType, IAdministrationItem } from './IAdministrationItem';
+import { AdministrationItemType, type IAdministrationItem } from './IAdministrationItem.js';
export function filterConfigurationWizard(configurationWizard: boolean) {
return (item: IAdministrationItem) =>
diff --git a/webapp/packages/core-administration/src/AdministrationItem/orderAdministrationItems.ts b/webapp/packages/core-administration/src/AdministrationItem/orderAdministrationItems.ts
index 6ebb678278..06599f76ab 100644
--- a/webapp/packages/core-administration/src/AdministrationItem/orderAdministrationItems.ts
+++ b/webapp/packages/core-administration/src/AdministrationItem/orderAdministrationItems.ts
@@ -5,7 +5,7 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
-import type { IAdministrationItem } from './IAdministrationItem';
+import type { IAdministrationItem } from './IAdministrationItem.js';
export function orderAdministrationItems(configuration: boolean) {
return (itemA: IAdministrationItem, itemB: IAdministrationItem): number => {
diff --git a/webapp/packages/core-administration/src/AdministrationLocaleService.ts b/webapp/packages/core-administration/src/AdministrationLocaleService.ts
index 8febc531c1..ba5c98f6a8 100644
--- a/webapp/packages/core-administration/src/AdministrationLocaleService.ts
+++ b/webapp/packages/core-administration/src/AdministrationLocaleService.ts
@@ -14,24 +14,22 @@ export class AdministrationLocaleService extends Bootstrap {
super();
}
- register(): void | Promise {
+ override register(): void {
this.localizationService.addProvider(this.provider.bind(this));
}
- load(): void | Promise {}
-
private async provider(locale: string) {
switch (locale) {
case 'ru':
- return (await import('./locales/ru')).default;
+ return (await import('./locales/ru.js')).default;
case 'it':
- return (await import('./locales/it')).default;
+ return (await import('./locales/it.js')).default;
case 'zh':
- return (await import('./locales/zh')).default;
+ return (await import('./locales/zh.js')).default;
case 'fr':
- return (await import('./locales/fr')).default;
+ return (await import('./locales/fr.js')).default;
default:
- return (await import('./locales/en')).default;
+ return (await import('./locales/en.js')).default;
}
}
}
diff --git a/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreenService.ts b/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreenService.ts
index 9c8ff331f1..30828e7841 100644
--- a/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreenService.ts
+++ b/webapp/packages/core-administration/src/AdministrationScreen/AdministrationScreenService.ts
@@ -9,16 +9,16 @@ import { computed, makeObservable, observable } from 'mobx';
import { injectable } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
-import { Executor, IExecutor } from '@cloudbeaver/core-executor';
+import { Executor, type IExecutor } from '@cloudbeaver/core-executor';
import { EAdminPermission, PermissionsService, ServerConfigResource, SessionPermissionsResource } from '@cloudbeaver/core-root';
-import { RouterState, ScreenService } from '@cloudbeaver/core-routing';
+import { type RouterState, ScreenService } from '@cloudbeaver/core-routing';
import { StorageService } from '@cloudbeaver/core-storage';
-import { DefaultValueGetter, GlobalConstants, MetadataMap, schema } from '@cloudbeaver/core-utils';
+import { type DefaultValueGetter, GlobalConstants, MetadataMap, schema } from '@cloudbeaver/core-utils';
-import { AdministrationItemService } from '../AdministrationItem/AdministrationItemService';
-import type { IAdministrationItemRoute } from '../AdministrationItem/IAdministrationItemRoute';
-import type { IRouteParams } from '../AdministrationItem/IRouteParams';
-import { ADMINISTRATION_SCREEN_STATE_SCHEMA, type IAdministrationScreenInfo } from './IAdministrationScreenState';
+import { AdministrationItemService } from '../AdministrationItem/AdministrationItemService.js';
+import type { IAdministrationItemRoute } from '../AdministrationItem/IAdministrationItemRoute.js';
+import type { IRouteParams } from '../AdministrationItem/IRouteParams.js';
+import { ADMINISTRATION_SCREEN_STATE_SCHEMA, type IAdministrationScreenInfo } from './IAdministrationScreenState.js';
const ADMINISTRATION_INFO = 'administration_info';
@@ -111,6 +111,21 @@ export class AdministrationScreenService {
this.permissionsResource.onDataUpdate.addPostHandler(() => {
this.checkPermissions(this.screenService.routerService.state);
});
+
+ this.screenService.routeChange.addHandler(this.onRouteChange.bind(this));
+ }
+
+ private async onRouteChange() {
+ // this is need for this.isConfigurationMode
+ await this.serverConfigResource.load();
+
+ if (!this.isAdministrationPageActive) {
+ return;
+ }
+
+ if (!this.activeScreen || !this.administrationItemService.getItem(this.activeScreen.item, this.isConfigurationMode)) {
+ this.navigateToRoot();
+ }
}
getRouteName(item?: string, sub?: string, param?: string) {
@@ -213,7 +228,7 @@ export class AdministrationScreenService {
}
async handleCanDeActivate(fromState: RouterState, toState: RouterState): Promise {
- if (!fromState.params.item) {
+ if (!fromState.params['item']) {
return true;
}
@@ -228,7 +243,7 @@ export class AdministrationScreenService {
}
async handleCanActivate(toState: RouterState, fromState: RouterState): Promise {
- if (!toState.params.item) {
+ if (!toState.params['item']) {
return false;
}
diff --git a/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/ConfigurationWizardScreenService.ts b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/ConfigurationWizardScreenService.ts
new file mode 100644
index 0000000000..fb7091f711
--- /dev/null
+++ b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/ConfigurationWizardScreenService.ts
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+import { Dependency, injectable } from '@cloudbeaver/core-di';
+import { ServerConfigResource } from '@cloudbeaver/core-root';
+import { ScreenService } from '@cloudbeaver/core-routing';
+
+import { AdministrationScreenService } from '../AdministrationScreenService.js';
+import { ConfigurationWizardService } from './ConfigurationWizardService.js';
+
+@injectable()
+export class ConfigurationWizardScreenService extends Dependency {
+ constructor(
+ private readonly administrationScreenService: AdministrationScreenService,
+ private readonly screenService: ScreenService,
+ private readonly serverConfigResource: ServerConfigResource,
+ private readonly configurationWizardService: ConfigurationWizardService,
+ ) {
+ super();
+ this.screenService.routeChange.addHandler(this.onRouteChange.bind(this));
+ }
+
+ private async onRouteChange() {
+ // this is need for this.isConfigurationMode
+ await this.serverConfigResource.load();
+
+ if (!this.administrationScreenService.isConfigurationMode) {
+ return;
+ }
+
+ const isCurrentStepAvailable =
+ this.configurationWizardService.currentStep &&
+ this.configurationWizardService.isStepAvailable(this.configurationWizardService.currentStep.name);
+
+ if (!isCurrentStepAvailable) {
+ this.administrationScreenService.navigateToRoot();
+ }
+ }
+}
diff --git a/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/ConfigurationWizardService.ts b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/ConfigurationWizardService.ts
index d40cc8a3e2..186a4c89d4 100644
--- a/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/ConfigurationWizardService.ts
+++ b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/ConfigurationWizardService.ts
@@ -10,11 +10,11 @@ import { computed, makeObservable } from 'mobx';
import { injectable } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
-import { AdministrationItemService, filterHiddenAdministrationItem } from '../../AdministrationItem/AdministrationItemService';
-import { filterConfigurationWizard } from '../../AdministrationItem/filterConfigurationWizard';
-import type { IAdministrationItem } from '../../AdministrationItem/IAdministrationItem';
-import { orderAdministrationItems } from '../../AdministrationItem/orderAdministrationItems';
-import { AdministrationScreenService } from '../AdministrationScreenService';
+import { AdministrationItemService, filterHiddenAdministrationItem } from '../../AdministrationItem/AdministrationItemService.js';
+import { filterConfigurationWizard } from '../../AdministrationItem/filterConfigurationWizard.js';
+import type { IAdministrationItem } from '../../AdministrationItem/IAdministrationItem.js';
+import { orderAdministrationItems } from '../../AdministrationItem/orderAdministrationItems.js';
+import { AdministrationScreenService } from '../AdministrationScreenService.js';
@injectable()
export class ConfigurationWizardService {
@@ -86,10 +86,6 @@ export class ConfigurationWizardService {
}
isStepAvailable(name: string): boolean {
- if (this.currentStep?.name === name) {
- return true;
- }
-
for (const step of this.steps) {
if (step.name === name) {
return true;
@@ -151,7 +147,7 @@ export class ConfigurationWizardService {
}
if (this.currentStepIndex - 1 >= 0) {
- const step = this.steps[this.currentStepIndex - 1];
+ const step = this.steps[this.currentStepIndex - 1]!;
this.administrationScreenService.navigateTo(step.name, step.configurationWizardOptions?.defaultRoute);
}
}
diff --git a/webapp/packages/core-administration/src/DataContext/DATA_CONTEXT_ADMINISTRATION_ITEM_ROUTE.ts b/webapp/packages/core-administration/src/DataContext/DATA_CONTEXT_ADMINISTRATION_ITEM_ROUTE.ts
index b0948244b9..d2e158b088 100644
--- a/webapp/packages/core-administration/src/DataContext/DATA_CONTEXT_ADMINISTRATION_ITEM_ROUTE.ts
+++ b/webapp/packages/core-administration/src/DataContext/DATA_CONTEXT_ADMINISTRATION_ITEM_ROUTE.ts
@@ -7,6 +7,6 @@
*/
import { createDataContext } from '@cloudbeaver/core-data-context';
-import type { IAdministrationItemRoute } from '../AdministrationItem/IAdministrationItemRoute';
+import type { IAdministrationItemRoute } from '../AdministrationItem/IAdministrationItemRoute.js';
export const DATA_CONTEXT_ADMINISTRATION_ITEM_ROUTE = createDataContext('AdministrationItemRoute');
diff --git a/webapp/packages/core-administration/src/PermissionsResource.ts b/webapp/packages/core-administration/src/PermissionsResource.ts
index f49f8020e9..3a3fd145e9 100644
--- a/webapp/packages/core-administration/src/PermissionsResource.ts
+++ b/webapp/packages/core-administration/src/PermissionsResource.ts
@@ -8,7 +8,7 @@
import { injectable } from '@cloudbeaver/core-di';
import { CachedMapAllKey, CachedMapResource, resourceKeyList } from '@cloudbeaver/core-resource';
import { SessionDataResource } from '@cloudbeaver/core-root';
-import { AdminObjectGrantInfoFragment, AdminPermissionInfoFragment, GraphQLService } from '@cloudbeaver/core-sdk';
+import { type AdminObjectGrantInfoFragment, type AdminPermissionInfoFragment, GraphQLService } from '@cloudbeaver/core-sdk';
export type PermissionInfo = AdminPermissionInfoFragment;
export type AdminObjectGrantInfo = AdminObjectGrantInfoFragment;
diff --git a/webapp/packages/core-administration/src/index.ts b/webapp/packages/core-administration/src/index.ts
index 672bf369c6..d1708c2bc8 100644
--- a/webapp/packages/core-administration/src/index.ts
+++ b/webapp/packages/core-administration/src/index.ts
@@ -1,12 +1,19 @@
-export * from './manifest';
-export * from './AdministrationItem/AdministrationItemService';
-export * from './AdministrationItem/filterConfigurationWizard';
-export * from './AdministrationItem/IAdministrationItem';
-export * from './AdministrationItem/IAdministrationItemRoute';
-export * from './AdministrationItem/IRouteParams';
-export * from './AdministrationItem/orderAdministrationItems';
-export * from './AdministrationScreen/AdministrationScreenService';
-export * from './AdministrationScreen/ConfigurationWizard/ConfigurationWizardService';
-export * from './DataContext/DATA_CONTEXT_ADMINISTRATION_ITEM_ROUTE';
-export * from './AdministrationLocaleService';
-export * from './PermissionsResource';
+/*
+ * 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 * from './manifest.js';
+export * from './AdministrationItem/AdministrationItemService.js';
+export * from './AdministrationItem/filterConfigurationWizard.js';
+export * from './AdministrationItem/IAdministrationItem.js';
+export * from './AdministrationItem/IAdministrationItemRoute.js';
+export * from './AdministrationItem/IRouteParams.js';
+export * from './AdministrationItem/orderAdministrationItems.js';
+export * from './AdministrationScreen/AdministrationScreenService.js';
+export * from './AdministrationScreen/ConfigurationWizard/ConfigurationWizardService.js';
+export * from './DataContext/DATA_CONTEXT_ADMINISTRATION_ITEM_ROUTE.js';
+export * from './AdministrationLocaleService.js';
+export * from './PermissionsResource.js';
diff --git a/webapp/packages/core-administration/src/locales/zh.ts b/webapp/packages/core-administration/src/locales/zh.ts
index a83ba9a2ee..7b8cc21cf1 100644
--- a/webapp/packages/core-administration/src/locales/zh.ts
+++ b/webapp/packages/core-administration/src/locales/zh.ts
@@ -1,7 +1,14 @@
+/*
+ * 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 default [
['administration_menu_enter', '管理'],
['administration_menu_back', '返回应用'],
['administration_configuration_wizard_title', '初始化服务器配置'],
- ['administration_configuration_wizard_finish_success_title', 'Server configured'],
- ['administration_configuration_wizard_finish_success_message', 'You can log-in as administrator in order to set up additional parameters.'],
+ ['administration_configuration_wizard_finish_success_title', '服务器已完成配置'],
+ ['administration_configuration_wizard_finish_success_message', '您可以使用管理员登录后进行更多参数配置。'],
];
diff --git a/webapp/packages/core-administration/src/manifest.ts b/webapp/packages/core-administration/src/manifest.ts
index e3c9c4bdd6..af2c95191e 100644
--- a/webapp/packages/core-administration/src/manifest.ts
+++ b/webapp/packages/core-administration/src/manifest.ts
@@ -13,10 +13,11 @@ export const coreAdministrationManifest: PluginManifest = {
},
providers: [
- () => import('./AdministrationItem/AdministrationItemService').then(m => m.AdministrationItemService),
- () => import('./PermissionsResource').then(m => m.PermissionsResource),
- () => import('./AdministrationScreen/AdministrationScreenService').then(m => m.AdministrationScreenService),
- () => import('./AdministrationScreen/ConfigurationWizard/ConfigurationWizardService').then(m => m.ConfigurationWizardService),
- () => import('./AdministrationLocaleService').then(m => m.AdministrationLocaleService),
+ () => import('./AdministrationItem/AdministrationItemService.js').then(m => m.AdministrationItemService),
+ () => import('./PermissionsResource.js').then(m => m.PermissionsResource),
+ () => import('./AdministrationScreen/AdministrationScreenService.js').then(m => m.AdministrationScreenService),
+ () => import('./AdministrationScreen/ConfigurationWizard/ConfigurationWizardService.js').then(m => m.ConfigurationWizardService),
+ () => import('./AdministrationScreen/ConfigurationWizard/ConfigurationWizardScreenService.js').then(m => m.ConfigurationWizardScreenService),
+ () => import('./AdministrationLocaleService.js').then(m => m.AdministrationLocaleService),
],
};
diff --git a/webapp/packages/core-app/package.json b/webapp/packages/core-app/package.json
index c2eda7cecb..62e0321797 100644
--- a/webapp/packages/core-app/package.json
+++ b/webapp/packages/core-app/package.json
@@ -1,5 +1,6 @@
{
"name": "@cloudbeaver/core-app",
+ "type": "module",
"sideEffects": [
"src/**/*.css",
"src/**/*.scss",
diff --git a/webapp/packages/core-app/src/AppLocaleService.ts b/webapp/packages/core-app/src/AppLocaleService.ts
index 6d835fb52a..8d5fb6df62 100644
--- a/webapp/packages/core-app/src/AppLocaleService.ts
+++ b/webapp/packages/core-app/src/AppLocaleService.ts
@@ -14,24 +14,22 @@ export class AppLocaleService extends Bootstrap {
super();
}
- register(): void | Promise {
+ override register(): void {
this.localizationService.addProvider(this.provider.bind(this));
}
- load(): void | Promise {}
-
private async provider(locale: string) {
switch (locale) {
case 'ru':
- return (await import('./locales/ru')).default;
+ return (await import('./locales/ru.js')).default;
case 'it':
- return (await import('./locales/it')).default;
+ return (await import('./locales/it.js')).default;
case 'zh':
- return (await import('./locales/zh')).default;
+ return (await import('./locales/zh.js')).default;
case 'fr':
- return (await import('./locales/fr')).default;
+ return (await import('./locales/fr.js')).default;
default:
- return (await import('./locales/en')).default;
+ return (await import('./locales/en.js')).default;
}
}
}
diff --git a/webapp/packages/core-app/src/AppScreen/AppScreen.tsx b/webapp/packages/core-app/src/AppScreen/AppScreen.tsx
index 8b364ee9e1..62bcb6ab1f 100644
--- a/webapp/packages/core-app/src/AppScreen/AppScreen.tsx
+++ b/webapp/packages/core-app/src/AppScreen/AppScreen.tsx
@@ -10,8 +10,8 @@ import { memo } from 'react';
import { Loader, Placeholder } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
-import { AppScreenService } from './AppScreenService';
-import { Main } from './Main';
+import { AppScreenService } from './AppScreenService.js';
+import { Main } from './Main.js';
export const AppScreen = memo(function AppScreen() {
const appScreenService = useService(AppScreenService);
diff --git a/webapp/packages/core-app/src/AppScreen/AppScreenBootstrap.ts b/webapp/packages/core-app/src/AppScreen/AppScreenBootstrap.ts
index 7e7a71395e..2875ea25b4 100644
--- a/webapp/packages/core-app/src/AppScreen/AppScreenBootstrap.ts
+++ b/webapp/packages/core-app/src/AppScreen/AppScreenBootstrap.ts
@@ -6,11 +6,11 @@
* you may not use this file except in compliance with the License.
*/
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
-import { Executor, IExecutor } from '@cloudbeaver/core-executor';
+import { Executor, type IExecutor } from '@cloudbeaver/core-executor';
import { ScreenService } from '@cloudbeaver/core-routing';
-import { AppScreen } from './AppScreen';
-import { AppScreenService } from './AppScreenService';
+import { AppScreen } from './AppScreen.js';
+import { AppScreenService } from './AppScreenService.js';
@injectable()
export class AppScreenBootstrap extends Bootstrap {
@@ -21,7 +21,7 @@ export class AppScreenBootstrap extends Bootstrap {
this.activation = new Executor();
}
- register(): void {
+ override register(): void {
this.screenService.create({
name: AppScreenService.screenName,
routes: [{ name: AppScreenService.screenName, path: '/' }],
@@ -32,6 +32,4 @@ export class AppScreenBootstrap extends Bootstrap {
},
});
}
-
- load(): void | Promise {}
}
diff --git a/webapp/packages/core-app/src/AppScreen/Main.tsx b/webapp/packages/core-app/src/AppScreen/Main.tsx
index dcc56e93db..514dc8a2e2 100644
--- a/webapp/packages/core-app/src/AppScreen/Main.tsx
+++ b/webapp/packages/core-app/src/AppScreen/Main.tsx
@@ -12,7 +12,7 @@ import { useService } from '@cloudbeaver/core-di';
import { LeftBarPanelService, SideBarPanel, SideBarPanelService } from '@cloudbeaver/core-ui';
import style from './Main.module.css';
-import { RightArea } from './RightArea';
+import { RightArea } from './RightArea.js';
export const Main = observer(function Main() {
const styles = useS(style);
diff --git a/webapp/packages/core-app/src/AppScreen/RightArea.tsx b/webapp/packages/core-app/src/AppScreen/RightArea.tsx
index 470a220660..34097d383f 100644
--- a/webapp/packages/core-app/src/AppScreen/RightArea.tsx
+++ b/webapp/packages/core-app/src/AppScreen/RightArea.tsx
@@ -23,7 +23,7 @@ import {
import { useService } from '@cloudbeaver/core-di';
import { OptionsPanelService } from '@cloudbeaver/core-ui';
-import { AppScreenService } from './AppScreenService';
+import { AppScreenService } from './AppScreenService.js';
import style from './RightArea.module.css';
interface Props {
@@ -40,8 +40,12 @@ export const RightArea = observer(function RightArea({ className }) {
const toolsDisabled = appScreenService.rightAreaBottom.getDisplayed({}).length === 0;
+ function close() {
+ optionsPanelService.close();
+ }
+
return (
-
+
@@ -61,7 +65,7 @@ export const RightArea = observer(function RightArea({ className }) {
- optionsPanelService.close()} />
+
);
diff --git a/webapp/packages/core-app/src/Body.tsx b/webapp/packages/core-app/src/Body.tsx
index 831aa61315..03fcc3698b 100644
--- a/webapp/packages/core-app/src/Body.tsx
+++ b/webapp/packages/core-app/src/Body.tsx
@@ -20,8 +20,8 @@ import { DNDProvider } from '@cloudbeaver/core-ui';
import { useAppVersion } from '@cloudbeaver/core-version';
import style from './Body.module.css';
-import { useAppHeight } from './useAppHeight';
-import { useClientActivity } from './useClientActivity';
+import { useAppHeight } from './useAppHeight.js';
+import { useClientActivity } from './useClientActivity.js';
export const Body = observer(function Body() {
// const serverConfigLoader = useResource(Body, ServerConfigResource, undefined);
@@ -41,7 +41,7 @@ export const Body = observer(function Body() {
if (ref.current) {
document.body.className = ref.current.className;
}
- document.documentElement.dataset.backendVersion = backendVersion;
+ document.documentElement.dataset['backendVersion'] = backendVersion;
});
useAppHeight();
@@ -50,7 +50,17 @@ export const Body = observer(function Body() {
return (
-
+
{Screen && }
diff --git a/webapp/packages/core-app/src/BodyLazy.ts b/webapp/packages/core-app/src/BodyLazy.ts
index 3d8043c34d..e717806089 100644
--- a/webapp/packages/core-app/src/BodyLazy.ts
+++ b/webapp/packages/core-app/src/BodyLazy.ts
@@ -7,4 +7,4 @@
*/
import { importLazyComponent } from '@cloudbeaver/core-blocks';
-export const BodyLazy = importLazyComponent(() => import('./Body').then(m => m.Body));
+export const BodyLazy = importLazyComponent(() => import('./Body.js').then(m => m.Body));
diff --git a/webapp/packages/core-app/src/index.ts b/webapp/packages/core-app/src/index.ts
index 51841f691c..e7c12637ba 100644
--- a/webapp/packages/core-app/src/index.ts
+++ b/webapp/packages/core-app/src/index.ts
@@ -1,11 +1,18 @@
+/*
+ * 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.
+ */
// Services
-export * from './AppScreen/AppScreenService';
-export * from './AppScreen/AppScreenBootstrap';
+export * from './AppScreen/AppScreenService.js';
+export * from './AppScreen/AppScreenBootstrap.js';
-export * from './AppLocaleService';
+export * from './AppLocaleService.js';
// components
-export * from './BodyLazy';
+export * from './BodyLazy.js';
// Interfaces
-export * from './manifest';
+export * from './manifest.js';
diff --git a/webapp/packages/core-app/src/manifest.ts b/webapp/packages/core-app/src/manifest.ts
index e857c64f6c..0c64908dd9 100644
--- a/webapp/packages/core-app/src/manifest.ts
+++ b/webapp/packages/core-app/src/manifest.ts
@@ -13,8 +13,8 @@ export const coreAppManifest: PluginManifest = {
},
providers: [
- () => import('./AppScreen/AppScreenService').then(m => m.AppScreenService),
- () => import('./AppScreen/AppScreenBootstrap').then(m => m.AppScreenBootstrap),
- () => import('./AppLocaleService').then(m => m.AppLocaleService),
+ () => import('./AppScreen/AppScreenService.js').then(m => m.AppScreenService),
+ () => import('./AppScreen/AppScreenBootstrap.js').then(m => m.AppScreenBootstrap),
+ () => import('./AppLocaleService.js').then(m => m.AppLocaleService),
],
};
diff --git a/webapp/packages/core-authentication/package.json b/webapp/packages/core-authentication/package.json
index bff33346db..2cfe0dce7e 100644
--- a/webapp/packages/core-authentication/package.json
+++ b/webapp/packages/core-authentication/package.json
@@ -1,5 +1,6 @@
{
"name": "@cloudbeaver/core-authentication",
+ "type": "module",
"sideEffects": [
"src/**/*.css",
"src/**/*.scss",
@@ -41,7 +42,7 @@
"@cloudbeaver/core-settings": "^0",
"@cloudbeaver/tests-runner": "^0",
"@jest/globals": "^29",
- "@testing-library/jest-dom": "^6",
+ "@types/jest": "^29",
"msw": "^2",
"typescript": "^5"
}
diff --git a/webapp/packages/core-authentication/src/ADMIN_USERNAME_MIN_LENGTH.ts b/webapp/packages/core-authentication/src/ADMIN_USERNAME_MIN_LENGTH.ts
new file mode 100644
index 0000000000..fff36b1d12
--- /dev/null
+++ b/webapp/packages/core-authentication/src/ADMIN_USERNAME_MIN_LENGTH.ts
@@ -0,0 +1,9 @@
+/*
+ * 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 const ADMIN_USERNAME_MIN_LENGTH = 6;
diff --git a/webapp/packages/core-authentication/src/AppAuthService.ts b/webapp/packages/core-authentication/src/AppAuthService.ts
index e76d207d2b..0e653faf10 100644
--- a/webapp/packages/core-authentication/src/AppAuthService.ts
+++ b/webapp/packages/core-authentication/src/AppAuthService.ts
@@ -6,19 +6,17 @@
* you may not use this file except in compliance with the License.
*/
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
-import { Executor, ExecutorInterrupter, IExecutor } from '@cloudbeaver/core-executor';
-import { CachedDataResourceKey, CachedResource, getCachedDataResourceLoaderState } from '@cloudbeaver/core-resource';
+import { Executor, ExecutorInterrupter, type IExecutor } from '@cloudbeaver/core-executor';
+import { type CachedDataResourceKey, CachedResource, getCachedDataResourceLoaderState } from '@cloudbeaver/core-resource';
import { ServerConfigResource } from '@cloudbeaver/core-root';
import type { ILoadableState } from '@cloudbeaver/core-utils';
-import { UserInfoResource } from './UserInfoResource';
+import { UserInfoResource } from './UserInfoResource.js';
@injectable()
export class AppAuthService extends Bootstrap {
get authenticated(): boolean {
- const user = this.userInfoResource.data;
-
- return this.serverConfigResource.anonymousAccessEnabled || this.serverConfigResource.configurationMode || user !== null;
+ return this.serverConfigResource.configurationMode || this.userInfoResource.hasAccess();
}
get loaders(): ILoadableState[] {
@@ -56,9 +54,9 @@ export class AppAuthService extends Bootstrap {
throw new Error("Can't configure Authentication");
}
- const user = await this.userInfoResource.load();
+ await this.userInfoResource.load();
- return !this.serverConfigResource.configurationMode && !this.serverConfigResource.anonymousAccessEnabled && user === null;
+ return !this.serverConfigResource.configurationMode && !this.userInfoResource.hasAccess();
}
async authUser(): Promise
{
@@ -68,8 +66,4 @@ export class AppAuthService extends Bootstrap {
await this.auth.execute(state);
return state;
}
-
- register(): void {}
-
- load(): void {}
}
diff --git a/webapp/packages/core-authentication/src/AuthConfigurationParametersResource.ts b/webapp/packages/core-authentication/src/AuthConfigurationParametersResource.ts
index 19f223b322..c5359f36f6 100644
--- a/webapp/packages/core-authentication/src/AuthConfigurationParametersResource.ts
+++ b/webapp/packages/core-authentication/src/AuthConfigurationParametersResource.ts
@@ -9,8 +9,8 @@ import { injectable } from '@cloudbeaver/core-di';
import { CachedMapResource, isResourceAlias, type ResourceKey, ResourceKeyUtils } from '@cloudbeaver/core-resource';
import { EAdminPermission, SessionDataResource, SessionPermissionsResource } from '@cloudbeaver/core-root';
import {
- AuthProviderConfigurationParametersFragment,
- GetAuthProviderConfigurationParametersQueryVariables,
+ type AuthProviderConfigurationParametersFragment,
+ type GetAuthProviderConfigurationParametersQueryVariables,
GraphQLService,
} from '@cloudbeaver/core-sdk';
diff --git a/webapp/packages/core-authentication/src/AuthConfigurationsResource.ts b/webapp/packages/core-authentication/src/AuthConfigurationsResource.ts
index 14960db42b..cec81b6319 100644
--- a/webapp/packages/core-authentication/src/AuthConfigurationsResource.ts
+++ b/webapp/packages/core-authentication/src/AuthConfigurationsResource.ts
@@ -18,9 +18,9 @@ import {
ResourceKeyUtils,
} from '@cloudbeaver/core-resource';
import { EAdminPermission, SessionPermissionsResource } from '@cloudbeaver/core-root';
-import { AdminAuthProviderConfiguration, GetAuthProviderConfigurationsQueryVariables, GraphQLService } from '@cloudbeaver/core-sdk';
+import { type AdminAuthProviderConfiguration, type GetAuthProviderConfigurationsQueryVariables, GraphQLService } from '@cloudbeaver/core-sdk';
-import type { AuthProviderConfiguration } from './AuthProvidersResource';
+import type { AuthProviderConfiguration } from './AuthProvidersResource.js';
const NEW_CONFIGURATION_SYMBOL = Symbol('new-configuration');
@@ -106,7 +106,7 @@ export class AuthConfigurationsResource extends CachedMapResource;
export type AuthProviderConfiguration = NonNullable;
@@ -31,8 +31,13 @@ export class AuthProvidersResource extends CachedMapResource provider.configurable);
}
+ get enabledConfigurableAuthProviders(): AuthProvider[] {
+ const enabledProviders = new Set(this.serverConfigResource.data?.enabledAuthProviders);
+
+ return this.configurable.filter(provider => enabledProviders.has(provider.id));
+ }
+
constructor(
- private readonly authSettingsService: AuthSettingsService,
private readonly graphQLService: GraphQLService,
private readonly serverConfigResource: ServerConfigResource,
private readonly authConfigurationsResource: AuthConfigurationsResource,
@@ -64,7 +69,7 @@ export class AuthProvidersResource extends CachedMapResource [
// {
- // key: 'disableAnonymousAccess',
+ // key: 'core.authentication.disableAnonymousAccess',
+ // access: {
+ // scope: ['server'],
+ // },
// type: ESettingsValueType.Checkbox,
// name: 'settings_authentication_disable_anonymous_access_name',
// description: 'settings_authentication_disable_anonymous_access_description',
diff --git a/webapp/packages/core-authentication/src/LocaleService.ts b/webapp/packages/core-authentication/src/LocaleService.ts
index f8618985f9..a55cfec20e 100644
--- a/webapp/packages/core-authentication/src/LocaleService.ts
+++ b/webapp/packages/core-authentication/src/LocaleService.ts
@@ -14,24 +14,22 @@ export class LocaleService extends Bootstrap {
super();
}
- register(): void | Promise {
+ override register(): void {
this.localizationService.addProvider(this.provider.bind(this));
}
- load(): void | Promise {}
-
private async provider(locale: string) {
switch (locale) {
case 'ru':
- return (await import('./locales/ru')).default;
+ return (await import('./locales/ru.js')).default;
case 'it':
- return (await import('./locales/it')).default;
+ return (await import('./locales/it.js')).default;
case 'zh':
- return (await import('./locales/zh')).default;
+ return (await import('./locales/zh.js')).default;
case 'fr':
- return (await import('./locales/fr')).default;
+ return (await import('./locales/fr.js')).default;
default:
- return (await import('./locales/en')).default;
+ return (await import('./locales/en.js')).default;
}
}
}
diff --git a/webapp/packages/core-authentication/src/TeamInfoMetaParametersResource.ts b/webapp/packages/core-authentication/src/TeamInfoMetaParametersResource.ts
new file mode 100644
index 0000000000..f1e213544d
--- /dev/null
+++ b/webapp/packages/core-authentication/src/TeamInfoMetaParametersResource.ts
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+import { injectable } from '@cloudbeaver/core-di';
+import { CachedMapAllKey, CachedMapResource, isResourceAlias, type ResourceKey, resourceKeyList, ResourceKeyUtils } from '@cloudbeaver/core-resource';
+import { GraphQLService } from '@cloudbeaver/core-sdk';
+
+import type { TeamMetaParameter } from './TeamMetaParametersResource.js';
+import { TeamsResource } from './TeamsResource.js';
+
+@injectable()
+export class TeamInfoMetaParametersResource extends CachedMapResource {
+ constructor(
+ private readonly graphQLService: GraphQLService,
+ private readonly teamsResource: TeamsResource,
+ ) {
+ super();
+
+ this.sync(this.teamsResource);
+ this.teamsResource.onItemDelete.addHandler(this.delete.bind(this));
+ }
+
+ protected async loader(param: ResourceKey): Promise