mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-28 03:24:29 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d529f9f751 | ||
|
|
43b33088a9 | ||
|
|
54fb2c81cd | ||
|
|
d4e65343c9 | ||
|
|
0e68e09ca0 | ||
|
|
df9b86e290 | ||
|
|
837c6fe52c | ||
|
|
32f56c2d82 | ||
|
|
1c11c47dc4 | ||
|
|
c7233f4c09 | ||
|
|
37f0520669 | ||
|
|
21aee4c11d | ||
|
|
28809be5f4 |
Vendored
+2
-2
@@ -15,11 +15,11 @@
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"streetsidesoftware.code-spell-checker-russian",
|
||||
"syler.sass-indented",
|
||||
"github.copilot-chat",
|
||||
"VisualStudioExptTeam.vscodeintellicode",
|
||||
"yzhang.markdown-all-in-one",
|
||||
"GraphQL.vscode-graphql-syntax",
|
||||
"mrmlnc.vscode-json5",
|
||||
"esbenp.prettier-vscode",
|
||||
"devnaumov.licensify"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,16 +25,6 @@ You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
|
||||
|
||||
## Changelog
|
||||
|
||||
### 25.2.5 2025-11-17
|
||||
- A "Show SQL" button has been added to the Data Editor and the grouping panel, enabling users to inspect the exact SQL query behind displayed results—helping validate data transformations, debug query logic, and ensure analytical accuracy;
|
||||
- The search option has been added to the default catalog/schema selector in the SQL Editor's main toolbar;
|
||||
- The SQL Server driver has been updated to version 13.2.1.
|
||||
|
||||
### 25.2.4 2025-11-03
|
||||
- A confirmation prompt has been added for potentially dangerous queries in the SQL Editor to help prevent unintended execution. You can customize this behavior in Preferences > SQL Editor;
|
||||
- The value panel in the Data Editor has been redesigned to provide users with more space for viewing and editing data. Tab buttons for different data type visualizations are now aligned on a single level, creating a cleaner layout;
|
||||
- Permission configuration for connections was simplified, providing a clearer appearance and improved performance.
|
||||
|
||||
### 25.2.3 2025-10-20
|
||||
- Added visualization for column reordering in the Data Editor;
|
||||
- Fixed the critical issue with duplicating the execution of queries executed by a shortcut in the SQL Editor;
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Model
|
||||
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
|
||||
Bundle-Version: 1.0.90.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 1.0.88.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model</artifactId>
|
||||
<version>1.0.90-SNAPSHOT</version>
|
||||
<version>1.0.88-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+33
-5
@@ -17,25 +17,32 @@
|
||||
package io.cloudbeaver;
|
||||
|
||||
import io.cloudbeaver.model.WebConnectionConfig;
|
||||
import io.cloudbeaver.model.app.WebAppConfiguration;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.registry.DataSourceNavigatorSettings;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C extends DataSourceDescriptor> {
|
||||
private static final Log log = Log.getLog(WebConnectionConfigInputHandler.class);
|
||||
protected final T input;
|
||||
protected final DBPDataSourceRegistry registry;
|
||||
protected final WebSession webSession;
|
||||
|
||||
public WebConnectionConfigInputHandler(@NotNull DBPDataSourceRegistry registry, T configInput) {
|
||||
public WebConnectionConfigInputHandler(@NotNull DBPDataSourceRegistry registry, T configInput, WebSession webSession) {
|
||||
this.registry = registry;
|
||||
this.input = configInput;
|
||||
this.webSession = webSession;
|
||||
}
|
||||
|
||||
public C createDataSourceContainer() throws DBWebException {
|
||||
@@ -47,9 +54,7 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
|
||||
|
||||
C newDataSource = createDataSourceContainerFromInput(driver);
|
||||
|
||||
if (ServletAppUtils.getServletApplication().getAppConfiguration() instanceof WebAppConfiguration webAppConfiguration) {
|
||||
newDataSource.setNavigatorSettings(webAppConfiguration.getDefaultNavigatorSettings());
|
||||
}
|
||||
presetNavigatorSettings(newDataSource);
|
||||
|
||||
WebDataSourceUtils.saveAuthProperties(
|
||||
newDataSource,
|
||||
@@ -61,6 +66,29 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
|
||||
return newDataSource;
|
||||
}
|
||||
|
||||
private <C extends DataSourceDescriptor> void presetNavigatorSettings(C newDataSource) {
|
||||
// if (ServletAppUtils.getServletApplication().getAppConfiguration() instanceof WebAppConfiguration webAppConfiguration) {
|
||||
// newDataSource.setNavigatorSettings(webAppConfiguration.getDefaultNavigatorSettings());
|
||||
// }
|
||||
try {
|
||||
DBPProject project = newDataSource.getProject();
|
||||
String projectId = project.getId();
|
||||
Map<String, Object> projectNavigatorSettings = webSession.getUserContext().getSecurityController().getObjectSettings(
|
||||
projectId, SMObjectType.project,
|
||||
ServletAppUtils.getServletApplication().getAppConfiguration().getDefaultUserTeam(),
|
||||
DataSourceNavigatorSettings.getSettingsKeySet()
|
||||
);
|
||||
if (!CommonUtils.isEmpty(projectNavigatorSettings)) {
|
||||
DataSourceNavigatorSettings navigatorSettings = new DataSourceNavigatorSettings();
|
||||
navigatorSettings.loadSettings(projectNavigatorSettings);
|
||||
newDataSource.setNavigatorSettings(navigatorSettings);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Error loading navigator settings", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void updateDataSource(@NotNull C dataSource) throws DBWebException {
|
||||
dataSource.setId(dataSource.getId());
|
||||
if (!CommonUtils.isEmpty(input.getName())) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package io.cloudbeaver;
|
||||
import io.cloudbeaver.model.WebConnectionConfig;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
@@ -252,6 +253,7 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
Map<String, Object> loadedSettings = webSession.getSecurityController().getObjectSettings(
|
||||
getId(),
|
||||
SMObjectType.project,
|
||||
ServletAppUtils.getServletApplication().getAppConfiguration().getDefaultUserTeam(),
|
||||
null
|
||||
);
|
||||
projectSettings.clear();
|
||||
@@ -268,7 +270,7 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
if (CommonUtils.isEmpty(configMap)) {
|
||||
throw new DBWebException("Connection configuration parameters are missing");
|
||||
}
|
||||
DBPDataSourceContainer newDataSource = getDataSourceContainerFromInput(getConnectionConfigInput(configMap));
|
||||
DBPDataSourceContainer newDataSource = getDataSourceContainerFromInput(configMap);
|
||||
return addDataSourceToProject(newDataSource);
|
||||
}
|
||||
|
||||
@@ -302,7 +304,7 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
webSession.addInfoMessage("Update connection - " + WebDataSourceUtils.getConnectionContainerInfo(dataSource));
|
||||
|
||||
DBPDataSourceRegistry registry = getDataSourceRegistry();
|
||||
getInputConfigHandler(config).updateDataSource(dataSource);
|
||||
getInputConfigHandler(configMap).updateDataSource(dataSource);
|
||||
connectionInfo.setCredentialsSavedInSession(null);
|
||||
try {
|
||||
registry.updateDataSource(dataSource);
|
||||
@@ -348,8 +350,8 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DataSourceDescriptor getDataSourceContainerFromInput(@NotNull WebConnectionConfig configInput) throws DBWebException {
|
||||
return getInputConfigHandler(configInput).createDataSourceContainer();
|
||||
public DataSourceDescriptor getDataSourceContainerFromInput(@NotNull Map<String, Object> configMap) throws DBWebException {
|
||||
return getInputConfigHandler(configMap).createDataSourceContainer();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -358,8 +360,8 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected WebConnectionConfigInputHandler getInputConfigHandler(@NotNull WebConnectionConfig configInput) {
|
||||
return new WebConnectionConfigInputHandler<>(getDataSourceRegistry(), configInput);
|
||||
protected WebConnectionConfigInputHandler getInputConfigHandler(@NotNull Map<String, Object> configMap) {
|
||||
return new WebConnectionConfigInputHandler<>(getDataSourceRegistry(), getConnectionConfigInput(configMap), webSession);
|
||||
}
|
||||
|
||||
|
||||
|
||||
-4
@@ -74,8 +74,4 @@ public interface SMAuthProviderExternal<AUTH_SESSION extends SMSession> extends
|
||||
*/
|
||||
default void postAuthentication() {}
|
||||
|
||||
@Override
|
||||
default boolean supportsOpeningSessionAsChild() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,16 +341,6 @@ public class WebConnectionInfo {
|
||||
if (CommonUtils.isEmpty(authModelId)) {
|
||||
authModelId = AuthModelDatabaseNative.ID;
|
||||
}
|
||||
|
||||
// Patch auth model
|
||||
DBPAuthModelDescriptor authModel = DBWorkbench.getPlatform().getDataSourceProviderRegistry().getAuthModel(authModelId);
|
||||
if (authModel != null) {
|
||||
DBPAuthModelDescriptor amReplace = authModel.getReplacedBy(dataSourceContainer.getDriver());
|
||||
if (amReplace != null && amReplace != authModel) {
|
||||
return amReplace.getId();
|
||||
}
|
||||
}
|
||||
|
||||
return authModelId;
|
||||
}
|
||||
|
||||
|
||||
+2
-14
@@ -23,23 +23,13 @@ import org.jkiss.dbeaver.model.cli.model.CommandLineAuthenticator;
|
||||
import picocli.CommandLine;
|
||||
|
||||
public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationInstanceController> {
|
||||
|
||||
@Nullable
|
||||
private final CommandLineAuthenticator authenticator;
|
||||
private final CloudBeaverMixin mixin;
|
||||
|
||||
public CloudBeaverCommandLine(@Nullable CommandLineAuthenticator authenticator) {
|
||||
super();
|
||||
this.authenticator = authenticator;
|
||||
this.mixin = new CloudBeaverMixin();
|
||||
}
|
||||
|
||||
public CloudBeaverCommandLine(
|
||||
@Nullable CommandLineAuthenticator authenticator,
|
||||
@NotNull CloudBeaverMixin mixin
|
||||
) {
|
||||
super();
|
||||
this.authenticator = authenticator;
|
||||
this.mixin = mixin;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,8 +51,6 @@ public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationIn
|
||||
@NotNull CommandLineContext context,
|
||||
@NotNull CLIRunMeta runMeta
|
||||
) {
|
||||
var cmd = super.initCommandLine(applicationInstanceController, context, runMeta);
|
||||
cmd.addMixin("cloudbeaver", mixin);
|
||||
return cmd;
|
||||
return super.initCommandLine(applicationInstanceController, context, runMeta);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ public class CloudBeaverInstanceServer extends ApplicationInstanceServer<Applica
|
||||
this.commandLine = new CloudBeaverCommandLine(null);
|
||||
}
|
||||
|
||||
public CloudBeaverInstanceServer(@NotNull CloudBeaverCommandLine commandLine) throws IOException {
|
||||
protected CloudBeaverInstanceServer(@NotNull CloudBeaverCommandLine commandLine) throws IOException {
|
||||
super(ApplicationInstanceController.class);
|
||||
this.commandLine = commandLine;
|
||||
}
|
||||
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.cli;
|
||||
|
||||
import picocli.CommandLine;
|
||||
|
||||
/**
|
||||
* CloudBeaver specific mixin options
|
||||
*/
|
||||
public class CloudBeaverMixin {
|
||||
@CommandLine.Option(
|
||||
names = {"-web-config"},
|
||||
arity = "1",
|
||||
hidden = true,
|
||||
scope = CommandLine.ScopeType.INHERIT
|
||||
)
|
||||
private String webConfig;
|
||||
}
|
||||
+1
-1
@@ -24,7 +24,7 @@ import org.jkiss.dbeaver.model.cli.CommandLineContext;
|
||||
import org.jkiss.dbeaver.model.cli.command.AbstractTopLevelCommand;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@CommandLine.Command(name = "cbvr", description = "CloudBeaver commands")
|
||||
@CommandLine.Command(name = "cbvr", description = "CloudBeaver commands", mixinStandardHelpOptions = true)
|
||||
public class CloudBeaverTopLevelCommand extends AbstractTopLevelCommand {
|
||||
protected CloudBeaverTopLevelCommand(
|
||||
@Nullable ApplicationInstanceController controller,
|
||||
|
||||
-2
@@ -1277,6 +1277,4 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
return project;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
-10
@@ -20,7 +20,6 @@ import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.impl.preferences.AbstractUserPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
@@ -86,13 +85,4 @@ public class WebSessionPreferenceStore extends AbstractUserPreferenceStore {
|
||||
public void save() throws IOException {
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
public boolean getUserPreferenceBoolean(@NotNull String name, boolean fallbackValue) {
|
||||
String value = CommonUtils.toString(userPreferences.get(name));
|
||||
if (value.isEmpty()) {
|
||||
return fallbackValue;
|
||||
} else {
|
||||
return toBoolean(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-33
@@ -17,18 +17,12 @@
|
||||
package io.cloudbeaver.registry;
|
||||
|
||||
import org.eclipse.core.runtime.IConfigurationElement;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.DBPConditionalProperty;
|
||||
import org.jkiss.dbeaver.model.impl.LocalizedPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.utils.RuntimeUtils;
|
||||
import org.osgi.framework.Bundle;
|
||||
|
||||
public class WebAuthProviderProperty extends LocalizedPropertyDescriptor implements DBPConditionalProperty {
|
||||
|
||||
private static final String WEB_AUTH_PROPERTY_PREFIX = "prop.auth.model.";
|
||||
|
||||
private final String[] requiredFeatures;
|
||||
@Nullable
|
||||
private final String type;
|
||||
@@ -36,7 +30,6 @@ public class WebAuthProviderProperty extends LocalizedPropertyDescriptor impleme
|
||||
private final String authProviderId;
|
||||
private final String hideExpr;
|
||||
private final String readOnlyExpr;
|
||||
private final Bundle bundle;
|
||||
|
||||
public WebAuthProviderProperty(String category, IConfigurationElement config, String authProviderId) {
|
||||
super(category, config);
|
||||
@@ -46,7 +39,6 @@ public class WebAuthProviderProperty extends LocalizedPropertyDescriptor impleme
|
||||
this.type = config.getAttribute("type");
|
||||
this.hideExpr = config.getAttribute("hideExpr");
|
||||
this.readOnlyExpr = config.getAttribute("readOnlyExpr");
|
||||
this.bundle = extractBundle(config);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -71,34 +63,12 @@ public class WebAuthProviderProperty extends LocalizedPropertyDescriptor impleme
|
||||
return readOnlyExpr;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getLocalizedName(@NotNull String locale) {
|
||||
return RuntimeUtils.getBundleLocalization(bundle, locale).getString(getPropertyId());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getLocalizedDescription(@NotNull String locale) {
|
||||
return RuntimeUtils.getBundleLocalization(bundle, locale).getString(getPropertyId() + "." + ATTR_DESCRIPTION);
|
||||
}
|
||||
|
||||
private String getPropertyId() {
|
||||
public String getPropertyId() {
|
||||
if (authProviderId != null) {
|
||||
return WEB_AUTH_PROPERTY_PREFIX + authProviderId + "." + this.getId();
|
||||
return "prop.auth.model." + authProviderId + "." + this.getId();
|
||||
} else {
|
||||
return WEB_AUTH_PROPERTY_PREFIX + this.getId();
|
||||
return "prop.auth.model." + this.getId();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Bundle extractBundle(@NotNull IConfigurationElement config) {
|
||||
final Bundle bundle;
|
||||
String bundleName = config.getContributor().getName();
|
||||
bundle = Platform.getBundle(bundleName);
|
||||
if (bundle == null) {
|
||||
throw new IllegalStateException("Bundle '" + bundleName + "' not found");
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -47,5 +47,5 @@ public class WebSQLConstants {
|
||||
|
||||
public static final String TASK_CONFIRMATION_ATTR_PREFIX = "session_task_confirmation_";
|
||||
public static final String SKIP_TASK_CONFIRMATIONS_ATTR = "skip_session_task_confirmations";
|
||||
public static final int TASK_CONFIRMATION_TIMEOUT_SECONDS = 60;
|
||||
public static final int TASK_CONFIRMATION_TIMEOUT_SECONDS = 30;
|
||||
}
|
||||
|
||||
-12
@@ -21,7 +21,6 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.DBPDataSource;
|
||||
import org.jkiss.dbeaver.model.DBUtils;
|
||||
import org.jkiss.dbeaver.model.data.DBDAttributeBinding;
|
||||
import org.jkiss.dbeaver.model.data.DBDDataFilter;
|
||||
import org.jkiss.dbeaver.model.data.DBDRowIdentifier;
|
||||
import org.jkiss.dbeaver.model.exec.trace.DBCTrace;
|
||||
import org.jkiss.dbeaver.model.struct.*;
|
||||
@@ -43,8 +42,6 @@ public class WebSQLResultsInfo {
|
||||
// TODO: find a way to remove isSingleRow and use virtual keys for reading BLOB and string cell values.
|
||||
private boolean isSingleRow;
|
||||
private DBCTrace trace;
|
||||
@Nullable
|
||||
private DBDDataFilter dataFilter;
|
||||
private String queryText;
|
||||
|
||||
public WebSQLResultsInfo(@NotNull DBSDataContainer dataContainer, @NotNull String id) {
|
||||
@@ -154,13 +151,4 @@ public class WebSQLResultsInfo {
|
||||
public void setSingleRow(boolean singleRow) {
|
||||
isSingleRow = singleRow;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DBDDataFilter getDataFilter() {
|
||||
return dataFilter;
|
||||
}
|
||||
|
||||
public void setDataFilter(@Nullable DBDDataFilter dataFilter) {
|
||||
this.dataFilter = dataFilter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: 25.3.0.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 25.2.4.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce</artifactId>
|
||||
<version>25.3.0-SNAPSHOT</version>
|
||||
<version>25.2.4-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Cloudbeaver Base JDBC drivers
|
||||
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
|
||||
Bundle-Version: 1.0.135.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 1.0.133.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
|
||||
<version>1.0.135-SNAPSHOT</version>
|
||||
<version>1.0.133-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver CE Server
|
||||
Bundle-SymbolicName: io.cloudbeaver.server.ce;singleton:=true
|
||||
Bundle-Version: 25.3.0.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 25.2.4.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server.ce</artifactId>
|
||||
<version>25.3.0-SNAPSHOT</version>
|
||||
<version>25.2.4-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
import io.cloudbeaver.auth.NoAuthCredentialsProvider;
|
||||
import io.cloudbeaver.model.CBWebServerConfig;
|
||||
import io.cloudbeaver.model.WebServerConfig;
|
||||
import io.cloudbeaver.model.config.CBServerConfig;
|
||||
import io.cloudbeaver.model.rm.local.LocalResourceController;
|
||||
import io.cloudbeaver.service.security.CBEmbeddedSecurityController;
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Server
|
||||
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
|
||||
Bundle-Version: 25.3.0.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 25.2.4.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server</artifactId>
|
||||
<version>25.3.0-SNAPSHOT</version>
|
||||
<version>25.2.4-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -158,6 +158,8 @@ type SQLExecuteInfo {
|
||||
duration: Int!
|
||||
# Actual conditions applied to query
|
||||
filterText: String
|
||||
# # original sql query without SQLDataFilter
|
||||
# originalQuery: String
|
||||
# Full query that was executed, contains all used filters
|
||||
fullQuery: String
|
||||
# Results
|
||||
|
||||
@@ -1,31 +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;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a field or method as sensitive, so its value won't be logged.
|
||||
*/
|
||||
@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface WebParameterSecure {
|
||||
}
|
||||
+7
@@ -246,7 +246,14 @@ public class GraphQLEndpoint extends HttpServlet {
|
||||
if (operationName != null) {
|
||||
contextBuilder.operationName(operationName);
|
||||
}
|
||||
String sessionId = GraphQLLoggerUtil.getSmSessionId(request);
|
||||
String userId = GraphQLLoggerUtil.getUserId(request);
|
||||
String loggerMessage = GraphQLLoggerUtil.buildLoggerMessage(sessionId, userId, variables);
|
||||
if (operationName != null) {
|
||||
log.debug("API > " + operationName + loggerMessage);
|
||||
} else if (DEBUG) {
|
||||
log.debug("API > " + query + loggerMessage);
|
||||
}
|
||||
LocalDateTime startTime = LocalDateTime.now();
|
||||
ExecutionInput executionInput = contextBuilder.build();
|
||||
ExecutionResult executionResult = null;
|
||||
|
||||
+29
-59
@@ -16,27 +16,21 @@
|
||||
*/
|
||||
package io.cloudbeaver.server.graphql;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.cloudbeaver.WebParameterSecure;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.server.WebApplication;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class GraphQLLoggerUtil {
|
||||
|
||||
private static final Log log = Log.getLog(GraphQLLoggerUtil.class);
|
||||
public static final String LOG_API_GRAPHQL_DEBUG_PARAMETER = "log.api.graphql.debug";
|
||||
public static final Gson GSON = new GsonBuilder().create();
|
||||
public static final String MASK_STRING = "****";
|
||||
private static final Set<String> PROHIBITED_VARIABLES =
|
||||
Set.of("password", "config", "parameters", "settings", "licenseText", "credentials", "username");
|
||||
|
||||
public static String getUserId(HttpServletRequest request) {
|
||||
WebSession session = getWebSession(request);
|
||||
@@ -69,67 +63,43 @@ public class GraphQLLoggerUtil {
|
||||
.findWebSession(request);
|
||||
}
|
||||
|
||||
public static String buildLoggerMessage(String sessionId, String userId, Method method, Object[] args) {
|
||||
StringBuilder sb = new StringBuilder(64)
|
||||
.append(" [user: ").append(userId)
|
||||
.append(", sessionId: ").append(sessionId)
|
||||
.append("]");
|
||||
public static String buildLoggerMessage(String sessionId, String userId, Map<String, Object> variables) {
|
||||
StringBuilder loggerMessage = new StringBuilder(" [user: ").append(userId)
|
||||
.append(", sessionId: ").append(sessionId).append("]");
|
||||
|
||||
if (WebAppUtils.getWebPlatform().getPreferenceStore().getBoolean(LOG_API_GRAPHQL_DEBUG_PARAMETER)) {
|
||||
sb.append('(');
|
||||
String text = maskArgsToString(method, args);
|
||||
if (CommonUtils.isNotEmpty(text)) {
|
||||
sb.append(text);
|
||||
if (WebAppUtils.getWebPlatform().getPreferenceStore().getBoolean(LOG_API_GRAPHQL_DEBUG_PARAMETER)
|
||||
&& variables != null
|
||||
) {
|
||||
loggerMessage.append(" [variables] ");
|
||||
String parsedVariables = parseVarialbes(variables);
|
||||
if (CommonUtils.isNotEmpty(parsedVariables)) {
|
||||
loggerMessage.append(parseVarialbes(variables));
|
||||
}
|
||||
sb.append(')');
|
||||
}
|
||||
return sb.toString();
|
||||
return loggerMessage.toString();
|
||||
}
|
||||
|
||||
public static String maskArgsToString(Method method, Object[] args) {
|
||||
Parameter[] params = method.getParameters();
|
||||
if (params.length == 0 || args == null || args.length == 0) {
|
||||
return "";
|
||||
}
|
||||
private static String parseVarialbes(Map<String, Object> map) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
int limit = Math.min(args.length, params.length);
|
||||
StringJoiner joiner = new StringJoiner(", ");
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
||||
for (int i = 0; i < limit; i++) {
|
||||
Object value = args[i];
|
||||
if (value instanceof WebSession) {
|
||||
//we already log sessionId
|
||||
continue;
|
||||
}
|
||||
if (params[i].isAnnotationPresent(WebParameterSecure.class)) {
|
||||
joiner.add(MASK_STRING);
|
||||
continue;
|
||||
}
|
||||
if (value instanceof String sv && CommonUtils.isEmpty(sv)) {
|
||||
boolean isProhibited = PROHIBITED_VARIABLES.stream()
|
||||
.anyMatch(prohibitedKey -> key.toLowerCase().contains(prohibitedKey.toLowerCase()));
|
||||
|
||||
if (isProhibited) {
|
||||
result.append(key).append(": ").append("******** ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value != null && !isSimple(value.getClass())) {
|
||||
String stringValue;
|
||||
try {
|
||||
stringValue = GSON.toJson(value);
|
||||
} catch (Exception e) {
|
||||
stringValue = value.toString();
|
||||
}
|
||||
joiner.add(stringValue);
|
||||
if (value instanceof Map) {
|
||||
result.append(parseVarialbes((Map<String, Object>) value));
|
||||
} else {
|
||||
joiner.add(String.valueOf(value));
|
||||
result.append(key).append(": ").append(value).append(" ");
|
||||
}
|
||||
}
|
||||
return joiner.toString();
|
||||
return result.toString().trim();
|
||||
}
|
||||
|
||||
private static boolean isSimple(Class<?> cls) {
|
||||
return cls.isPrimitive()
|
||||
|| Number.class.isAssignableFrom(cls)
|
||||
|| CharSequence.class.isAssignableFrom(cls)
|
||||
|| Boolean.class.equals(cls)
|
||||
|| Enum.class.isAssignableFrom(cls);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-22
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
package io.cloudbeaver.service;
|
||||
|
||||
import graphql.GraphQLContext;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import graphql.schema.idl.SchemaParser;
|
||||
import graphql.schema.idl.TypeDefinitionRegistry;
|
||||
@@ -28,11 +27,9 @@ import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.session.WebSessionProvider;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
|
||||
import io.cloudbeaver.server.graphql.GraphQLLoggerUtil;
|
||||
import io.cloudbeaver.service.security.SMUtils;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
@@ -164,7 +161,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static WebConnectionInfo getWebConnection(@NotNull WebSession session, @Nullable String projectId, @NotNull String connectionId) throws DBWebException {
|
||||
public static WebConnectionInfo getWebConnection(@NotNull WebSession session, @NotNull String projectId, @NotNull String connectionId) throws DBWebException {
|
||||
return WebDataSourceUtils.getWebConnectionInfo(session, projectId, connectionId);
|
||||
}
|
||||
|
||||
@@ -343,27 +340,16 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
}
|
||||
}
|
||||
}
|
||||
// Perform any checks before action call
|
||||
protected void beforeWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
|
||||
|
||||
GraphQLContext graphQlContext = this.env.getGraphQlContext();
|
||||
HttpServletRequest request = graphQlContext.get("request");
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
String sessionId = GraphQLLoggerUtil.getSmSessionId(request);
|
||||
String userId = GraphQLLoggerUtil.getUserId(request);
|
||||
String loggerMessage = GraphQLLoggerUtil.buildLoggerMessage(sessionId, userId, method, args);
|
||||
}
|
||||
|
||||
log.debug("API > " + method.getName() + loggerMessage);
|
||||
|
||||
setLogContext(method, args);
|
||||
}
|
||||
|
||||
protected void afterWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
|
||||
Log.setContext(null);
|
||||
}
|
||||
// Perform any checks before action call
|
||||
protected void beforeWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
|
||||
setLogContext(method, args);
|
||||
}
|
||||
|
||||
protected void afterWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
|
||||
Log.setContext(null);
|
||||
}
|
||||
|
||||
protected void setLogContext(Method method, @Nullable Object[] args) {
|
||||
|
||||
+6
-3
@@ -16,7 +16,10 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.core;
|
||||
|
||||
import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebAction;
|
||||
import io.cloudbeaver.WebObjectId;
|
||||
import io.cloudbeaver.WebProjectAction;
|
||||
import io.cloudbeaver.model.*;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.service.DBWService;
|
||||
@@ -116,7 +119,7 @@ public interface DBWServiceCore extends DBWService {
|
||||
@NotNull WebSession webSession,
|
||||
@Nullable String projectId,
|
||||
@NotNull String connectionId,
|
||||
@WebParameterSecure @Nullable Map<String, Object> authProperties,
|
||||
@Nullable Map<String, Object> authProperties,
|
||||
@Nullable List<WebNetworkHandlerConfigInput> networkCredentials,
|
||||
boolean saveCredentials,
|
||||
boolean sharedCredentials,
|
||||
@@ -127,7 +130,7 @@ public interface DBWServiceCore extends DBWService {
|
||||
WebConnectionInfo createConnection(
|
||||
@NotNull WebSession webSession,
|
||||
@Nullable @WebObjectId String projectId,
|
||||
@WebParameterSecure @NotNull Map<String, Object> connectionConfig
|
||||
@NotNull Map<String, Object> connectionConfig
|
||||
) throws DBWebException;
|
||||
|
||||
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATASOURCES_EDIT})
|
||||
|
||||
+1
-3
@@ -496,8 +496,6 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
WebSessionProjectImpl project = getProjectById(webSession, projectId);
|
||||
WebConnectionConfig configInput = project.getConnectionConfigInput(connectionConfig);
|
||||
|
||||
configInput.setSaveCredentials(true); // It is used in createConnectionFromConfig
|
||||
|
||||
DataSourceDescriptor dataSource = (DataSourceDescriptor) WebDataSourceUtils.getLocalOrGlobalDataSource(
|
||||
webSession, projectId, configInput.getConnectionId());
|
||||
|
||||
@@ -537,7 +535,7 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
true
|
||||
);
|
||||
} else {
|
||||
testDataSource = project.getDataSourceContainerFromInput(configInput);
|
||||
testDataSource = project.getDataSourceContainerFromInput(connectionConfig);
|
||||
}
|
||||
validateDriverLibrariesPresence(testDataSource);
|
||||
webSession.provideAuthParameters(
|
||||
|
||||
+4
-9
@@ -17,7 +17,10 @@
|
||||
package io.cloudbeaver.service.navigator.impl;
|
||||
|
||||
|
||||
import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.BaseWebProjectImpl;
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.model.WebCommandContext;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.rm.DBNAbstractResourceManagerNode;
|
||||
@@ -108,14 +111,6 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
Set<String> nodeIds = new HashSet<>(); // filter duplicate node ids
|
||||
|
||||
for (DBNNode node : nodeChildren) {
|
||||
// TODO: find a better way to filter out empty folder nodes for non-admin users
|
||||
if (node instanceof DBNLocalFolder localFolderNode &&
|
||||
localFolderNode.getDataSourceRegistry() instanceof WebGlobalProjectRegistryProxy globalProjectRegistryProxy
|
||||
) {
|
||||
if (!globalProjectRegistryProxy.getAllFolders().contains(localFolderNode.getFolder())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (node instanceof DBNDatabaseFolder folderNode && CommonUtils.isEmpty(folderNode.getMeta().getChildren(null))) {
|
||||
// Skip empty folders. Folder may become empty if their nested elements are provided by UI plugins.
|
||||
continue;
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ public class WebSQLContextInfo implements WebSessionProvider {
|
||||
private final transient WebSQLProcessor processor;
|
||||
private final String id;
|
||||
private final String projectId;
|
||||
private final transient Map<String, WebSQLResultsInfo> resultInfoMap = new HashMap<>();
|
||||
private final Map<String, WebSQLResultsInfo> resultInfoMap = new HashMap<>();
|
||||
|
||||
private final AtomicInteger resultId = new AtomicInteger();
|
||||
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ public class WebSQLExecuteInfo {
|
||||
@Property
|
||||
public String getOriginalQuery() {
|
||||
//TODO implement
|
||||
return null; // should return the original query without data filters
|
||||
return null;
|
||||
}
|
||||
|
||||
@Property
|
||||
|
||||
+40
-49
@@ -20,7 +20,6 @@ import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.session.WebSessionPreferenceStore;
|
||||
import io.cloudbeaver.model.session.WebSessionProvider;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.server.jobs.SqlOutputLogReaderJob;
|
||||
@@ -63,6 +62,7 @@ import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -231,7 +231,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
if (useEvents) {
|
||||
boolean isConfirmed = confirmQueryIfNeeded(mainQuery.getScriptElements(), asyncTask, isGenerated);
|
||||
if (!isConfirmed) {
|
||||
throw new DBWebException("Query execution was cancelled by user");
|
||||
throw new DBWebException("Query execution cancelled by user");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,6 +292,8 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
sqlOutputLogReaderJob.join();
|
||||
}
|
||||
fillQueryResults(contextInfo, dataContainer, dbStat, hasResultSet, executeInfo, webDataFilter, dataFilter, dataFormat);
|
||||
} catch (DBException e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,9 +335,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
DBDDataFilter dataFilter = filter.makeDataFilter((resultId == null ? null : contextInfo.getResults(resultId)));
|
||||
DBExecUtils.tryExecuteRecover(monitor, connection.getDataSource(), param -> {
|
||||
try (DBCSession session = executionContext.openSession(monitor, resolveQueryPurpose(dataFilter), "Read data from container")) {
|
||||
try (
|
||||
WebSQLQueryDataReceiver dataReceiver = new WebSQLQueryDataReceiver(contextInfo, dataContainer, dataFormat, dataFilter)
|
||||
) {
|
||||
try (WebSQLQueryDataReceiver dataReceiver = new WebSQLQueryDataReceiver(contextInfo, dataContainer, dataFormat)) {
|
||||
DBCStatistics statistics = dataContainer.readData(
|
||||
new WebExecutionSource(dataContainer, executionContext, this),
|
||||
session,
|
||||
@@ -352,12 +352,14 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
results.setResultSet(resultSet);
|
||||
|
||||
executeInfo.setResults(new WebSQLQueryResults[]{results});
|
||||
setResultFilterText(session.getDataSource(), executeInfo, dataFilter);
|
||||
setResultFilterText(dataContainer, session.getDataSource(), executeInfo, dataFilter);
|
||||
executeInfo.setFullQuery(statistics.getQueryText());
|
||||
if (resultSet != null && resultSet.getRowsWithMetaData() != null && resultSet.getResultsInfo() != null) {
|
||||
resultSet.getResultsInfo().setQueryText(statistics.getQueryText());
|
||||
executeInfo.setStatusMessage(resultSet.getRowsWithMetaData().size() + " row(s) fetched");
|
||||
}
|
||||
} catch (DBException e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -397,7 +399,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
for (var rowIdentifier : rowIdentifierList) {
|
||||
Map<DBSDataManipulator.ExecuteBatch, Object[]> resultBatches = new LinkedHashMap<>();
|
||||
DBSDataManipulator dataManipulator = generateUpdateResultsDataBatch(
|
||||
monitor, resultsInfo, rowIdentifier, updatedRows, deletedRows, addedRows, resultBatches, keyReceiver);
|
||||
monitor, resultsInfo, rowIdentifier, updatedRows, deletedRows, addedRows, dataFormat, resultBatches, keyReceiver);
|
||||
|
||||
DBCExecutionContext executionContext = getExecutionContext(dataManipulator);
|
||||
try (DBCSession session = executionContext.openSession(monitor, DBCExecutionPurpose.USER, "Update data in container")) {
|
||||
@@ -498,7 +500,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
@NotNull Set<WebSQLQueryResultSetRow> newResultSetRows,
|
||||
@Nullable WebDataFormat dataFormat,
|
||||
@NotNull DBRProgressMonitor monitor)
|
||||
throws DBException {
|
||||
throws DBCException {
|
||||
try (DBCSession session = getExecutionContext().openSession(
|
||||
monitor,
|
||||
DBCExecutionPurpose.UTIL,
|
||||
@@ -574,8 +576,9 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
@NotNull String resultsId,
|
||||
@Nullable List<WebSQLResultsRow> updatedRows,
|
||||
@Nullable List<WebSQLResultsRow> deletedRows,
|
||||
@Nullable List<WebSQLResultsRow> addedRows
|
||||
) throws DBException {
|
||||
@Nullable List<WebSQLResultsRow> addedRows,
|
||||
@Nullable WebDataFormat dataFormat) throws DBException
|
||||
{
|
||||
Map<DBSDataManipulator.ExecuteBatch, Object[]> resultBatches = new LinkedHashMap<>();
|
||||
|
||||
|
||||
@@ -591,7 +594,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
StringBuilder sqlBuilder = new StringBuilder();
|
||||
for (var rowIdentifier : rowIdentifierList) {
|
||||
DBSDataManipulator dataManipulator = generateUpdateResultsDataBatch(
|
||||
monitor, resultsInfo, rowIdentifier, updatedRows, deletedRows, addedRows, resultBatches, null);
|
||||
monitor, resultsInfo, rowIdentifier, updatedRows, deletedRows, addedRows, dataFormat, resultBatches, null);
|
||||
|
||||
List<DBEPersistAction> actions = new ArrayList<>();
|
||||
|
||||
@@ -617,6 +620,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
@Nullable List<WebSQLResultsRow> updatedRows,
|
||||
@Nullable List<WebSQLResultsRow> deletedRows,
|
||||
@Nullable List<WebSQLResultsRow> addedRows,
|
||||
@Nullable WebDataFormat dataFormat,
|
||||
@NotNull Map<DBSDataManipulator.ExecuteBatch, Object[]> resultBatches,
|
||||
@Nullable DBDDataReceiver keyReceiver)
|
||||
throws DBException
|
||||
@@ -876,6 +880,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
@NotNull
|
||||
public WebSQLExecutionPlan explainExecutionPlan(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull WebSQLContextInfo contextInfo,
|
||||
@NotNull String sql,
|
||||
@NotNull Map<String, Object> configuration) throws DBWebException {
|
||||
|
||||
@@ -900,6 +905,8 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
DBCQueryPlannerConfiguration planConfig = new DBCQueryPlannerConfiguration();
|
||||
planConfig.getParameters().putAll(configuration);
|
||||
dbcPlan[0] = planner.planQueryExecution(session, sql, planConfig);
|
||||
} catch (DBException e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
});
|
||||
} catch (DBException e) {
|
||||
@@ -939,14 +946,16 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
private void readCellDataValue(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull WebSQLResultsInfo resultsInfo,
|
||||
@NotNull WebSQLResultsRow row,
|
||||
@Nullable WebSQLResultsRow row,
|
||||
@NotNull WebSQLCellValueReceiver dataReceiver
|
||||
) throws DBException {
|
||||
DBSDataContainer dataContainer = resultsInfo.getDataContainer();
|
||||
DBCExecutionContext executionContext = getExecutionContext(dataContainer);
|
||||
try (DBCSession session = executionContext.openSession(monitor, DBCExecutionPurpose.USER, "Generate data update batches")) {
|
||||
DBDDataFilter dataFilter = new DBDDataFilter();
|
||||
addKeyAttributes(resultsInfo, row, dataContainer, session, dataFilter);
|
||||
if (!resultsInfo.isSingleRow()) {
|
||||
addKeyAttributes(resultsInfo, row, dataContainer, session, dataFilter);
|
||||
}
|
||||
WebExecutionSource executionSource = new WebExecutionSource(dataContainer, executionContext, this);
|
||||
dataContainer.readData(
|
||||
executionSource, session, dataReceiver, dataFilter,
|
||||
@@ -956,16 +965,12 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
|
||||
private void addKeyAttributes(
|
||||
@NotNull WebSQLResultsInfo resultsInfo,
|
||||
@NotNull WebSQLResultsRow row,
|
||||
@Nullable WebSQLResultsRow row,
|
||||
@NotNull DBSDataContainer dataContainer,
|
||||
@NotNull DBCSession session,
|
||||
@NotNull DBDDataFilter dataFilter
|
||||
) throws DBException {
|
||||
DBDRowIdentifier rowIdentifier = resultsInfo.getDefaultRowIdentifier();
|
||||
if (rowIdentifier == null || rowIdentifier.isIncomplete()) {
|
||||
return;
|
||||
}
|
||||
DBDAttributeBinding[] keyAttributes = rowIdentifier.getAttributes().toArray(new DBDAttributeBinding[0]);
|
||||
DBDAttributeBinding[] keyAttributes = resultsInfo.getDefaultRowIdentifier().getAttributes().toArray(new DBDAttributeBinding[0]);
|
||||
Object[] rowValues = new Object[keyAttributes.length];
|
||||
List<DBDAttributeConstraint> constraints = new ArrayList<>();
|
||||
for (int i = 0; i < keyAttributes.length; i++) {
|
||||
@@ -1069,16 +1074,10 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
if (resultSet == null) {
|
||||
break;
|
||||
}
|
||||
try (
|
||||
WebSQLQueryDataReceiver dataReceiver = new WebSQLQueryDataReceiver(
|
||||
contextInfo,
|
||||
dataContainer,
|
||||
dataFormat,
|
||||
dataFilter
|
||||
)
|
||||
) {
|
||||
try (WebSQLQueryDataReceiver dataReceiver = new WebSQLQueryDataReceiver(contextInfo, dataContainer, dataFormat)) {
|
||||
readResultSet(dbStat.getSession(), resultSet, webDataFilter, dataReceiver);
|
||||
results.setResultSet(dataReceiver.getResultSet());
|
||||
dataReceiver.getResultSet().getResultsInfo().setQueryText(resultSet.getSourceStatement().getQueryString());
|
||||
}
|
||||
}
|
||||
resultList.add(results);
|
||||
@@ -1098,15 +1097,11 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
}
|
||||
executeInfo.setResults(resultList.toArray(new WebSQLQueryResults[0]));
|
||||
|
||||
setResultFilterText(dbStat.getSession().getDataSource(), executeInfo, dataFilter);
|
||||
setResultFilterText(dataContainer, dbStat.getSession().getDataSource(), executeInfo, dataFilter);
|
||||
executeInfo.setFullQuery(dbStat.getQueryString());
|
||||
}
|
||||
|
||||
private void setResultFilterText(
|
||||
@NotNull DBPDataSource dataSource,
|
||||
@NotNull WebSQLExecuteInfo executeInfo,
|
||||
@NotNull DBDDataFilter filter
|
||||
) throws DBException {
|
||||
private void setResultFilterText(@NotNull DBSDataContainer dataContainer, @NotNull DBPDataSource dataSource, @NotNull WebSQLExecuteInfo executeInfo, @NotNull DBDDataFilter filter) throws DBException {
|
||||
if (!filter.getConstraints().isEmpty() || !CommonUtils.isEmpty(filter.getWhere())) {
|
||||
StringBuilder where = new StringBuilder();
|
||||
SQLUtils.appendConditionString(
|
||||
@@ -1119,13 +1114,8 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private void readResultSet(
|
||||
@NotNull DBCSession session,
|
||||
@NotNull DBCResultSet dbResult,
|
||||
@NotNull WebSQLDataFilter filter,
|
||||
@NotNull WebSQLQueryDataReceiver dataReceiver
|
||||
) throws DBException {
|
||||
DBDDataReceiver.startFetchWorkflow(dataReceiver, session, dbResult, filter.getOffset(), filter.getLimit());
|
||||
private void readResultSet(@NotNull DBCSession session, @NotNull DBCResultSet dbResult, @NotNull WebSQLDataFilter filter, @NotNull WebSQLQueryDataReceiver dataReceiver) throws DBCException {
|
||||
dataReceiver.fetchStart(session, dbResult, filter.getOffset(), filter.getLimit());
|
||||
int rowCount = 0;
|
||||
while (dbResult.nextRow()) {
|
||||
if (rowCount > filter.getLimit()) {
|
||||
@@ -1135,6 +1125,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
dataReceiver.fetchRow(session, dbResult);
|
||||
rowCount++;
|
||||
}
|
||||
dataReceiver.fetchEnd(session, dbResult);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1172,6 +1163,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
if (keyValue == null) {
|
||||
continue;
|
||||
}
|
||||
boolean updated = false;
|
||||
if (!CommonUtils.isEmpty(keyAttribute.getName())) {
|
||||
DBDAttributeBinding binding = DBUtils.findObject(resultsAttributes, keyAttribute.getName());
|
||||
if (binding != null) {
|
||||
@@ -1242,7 +1234,8 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
|
||||
private Object setCellRowValue(Object cellRow, WebSession webSession, DBCSession dbcSession, DBDAttributeBinding allAttributes, boolean withoutExecution)
|
||||
throws DBException {
|
||||
if (cellRow instanceof Map<?, ?> variables) {
|
||||
if (cellRow instanceof Map<?, ?>) {
|
||||
Map<String, Object> variables = (Map<String, Object>) cellRow;
|
||||
if (variables.get(FILE_ID) != null) {
|
||||
Path path = WebAppUtils.getWebPlatform()
|
||||
.getTempFolder(webSession.getProgressMonitor(), TEMP_FILE_FOLDER)
|
||||
@@ -1260,6 +1253,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
return convertInputCellValue(dbcSession, allAttributes, cellRow, withoutExecution);
|
||||
}
|
||||
|
||||
// TODO: Refactor to unify with desktop when confirmation settings will be added
|
||||
private boolean confirmQueryIfNeeded(
|
||||
@NotNull List<SQLScriptElement> scriptElements,
|
||||
@NotNull WebAsyncTaskInfo asyncTask,
|
||||
@@ -1287,28 +1281,25 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
.map(SQLScriptElement::getText)
|
||||
.collect(Collectors.joining("\n\n"));
|
||||
} else {
|
||||
WebSessionPreferenceStore store = webSession.getUserPreferenceStore();
|
||||
boolean confirmDangerousQueries = store.getUserPreferenceBoolean(ConfirmationConstants.CONFIRM_DANGER_SQL_KEY, true);
|
||||
boolean confirmDropQueries = store.getUserPreferenceBoolean(ConfirmationConstants.CONFIRM_DROP_SQL_KEY, true);
|
||||
for (SQLScriptElement scriptElement : scriptElements) {
|
||||
if (scriptElement instanceof SQLQuery sqlQuery) {
|
||||
if (confirmDangerousQueries && sqlQuery.isDeleteUpdateDangerous()) {
|
||||
if (sqlQuery.isDeleteUpdateDangerous()) {
|
||||
hasDangerousUpdates = true;
|
||||
ConfirmationDescriptor descriptor = ConfirmationRegistry.getInstance()
|
||||
.getConfirmation(ConfirmationConstants.CONFIRM_DANGER_SQL_ID);
|
||||
.getConfirmation(ConfirmationConstants.CONFIRM_DANGER_SQL);
|
||||
title = descriptor.getLocalizedTitle(webSession.getLocale());
|
||||
var entityMetadata = sqlQuery.getEntityMetadata(false);
|
||||
message = MessageFormat.format(
|
||||
descriptor.getLocalizedMessage(webSession.getLocale()),
|
||||
sqlQuery.getType().name(),
|
||||
entityMetadata != null ? entityMetadata.getEntityName() : "multiple tables"
|
||||
entityMetadata != null ? entityMetadata.getEntityName() : null
|
||||
);
|
||||
break;
|
||||
}
|
||||
if (confirmDropQueries && sqlQuery.isDropTableDangerous()) {
|
||||
if (sqlQuery.isDropTableDangerous()) {
|
||||
hasDropStatement = true;
|
||||
ConfirmationDescriptor descriptor = ConfirmationRegistry.getInstance()
|
||||
.getConfirmation(ConfirmationConstants.CONFIRM_DROP_SQL_ID);
|
||||
.getConfirmation(ConfirmationConstants.CONFIRM_DROP_SQL);
|
||||
title = descriptor.getLocalizedTitle(webSession.getLocale());
|
||||
message = descriptor.getLocalizedMessage(webSession.getLocale());
|
||||
break;
|
||||
|
||||
+2
-13
@@ -104,25 +104,14 @@ public class WebSQLQueryDataContainer implements DBSDataContainer, DBPContextPro
|
||||
long maxRows,
|
||||
long flags,
|
||||
int fetchSize
|
||||
) throws DBException {
|
||||
) throws DBCException {
|
||||
return queryDataContainer.readData(source, session, dataReceiver, dataFilter, firstRow, maxRows, flags, fetchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countData(@NotNull DBCExecutionSource source, @NotNull DBCSession session, @Nullable DBDDataFilter dataFilter, long flags) throws DBCException {
|
||||
try {
|
||||
String filteredQuery = dataFilter == null ? query : getDataSource().getSQLDialect().addFiltersToQuery(
|
||||
session.getProgressMonitor(),
|
||||
getDataSource(),
|
||||
query,
|
||||
dataFilter
|
||||
);
|
||||
|
||||
SQLQuery countQuery = new SQLQueryTransformerCount().transformQuery(
|
||||
dataSource,
|
||||
syntaxManager,
|
||||
new SQLQuery(dataSource, filteredQuery)
|
||||
);
|
||||
SQLQuery countQuery = new SQLQueryTransformerCount().transformQuery(dataSource, syntaxManager, new SQLQuery(dataSource, query));
|
||||
return DBUtils.countDataFromQuery(source, session, countQuery);
|
||||
} catch (DBException e) {
|
||||
throw new DBCException("Error executing row count", e);
|
||||
|
||||
+2
-16
@@ -19,7 +19,6 @@ package io.cloudbeaver.service.sql;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
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.DBPDataKind;
|
||||
@@ -42,14 +41,9 @@ import java.util.stream.Collectors;
|
||||
class WebSQLQueryDataReceiver implements DBDDataReceiver {
|
||||
private static final Log log = Log.getLog(WebSQLQueryDataReceiver.class);
|
||||
|
||||
@NotNull
|
||||
private final WebSQLContextInfo contextInfo;
|
||||
@NotNull
|
||||
private final DBSDataContainer dataContainer;
|
||||
@Nullable
|
||||
private final WebDataFormat dataFormat;
|
||||
@Nullable
|
||||
private final DBDDataFilter dataFilter;
|
||||
private final WebSQLQueryResultSet webResultSet = new WebSQLQueryResultSet();
|
||||
|
||||
private DBDAttributeBinding[] bindings;
|
||||
@@ -57,16 +51,10 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
|
||||
private List<WebSQLQueryResultSetRow> rows = new ArrayList<>();
|
||||
private final Number rowLimit;
|
||||
|
||||
WebSQLQueryDataReceiver(
|
||||
@NotNull WebSQLContextInfo contextInfo,
|
||||
@NotNull DBSDataContainer dataContainer,
|
||||
@Nullable WebDataFormat dataFormat,
|
||||
@Nullable DBDDataFilter dataFilter
|
||||
) {
|
||||
WebSQLQueryDataReceiver(WebSQLContextInfo contextInfo, DBSDataContainer dataContainer, WebDataFormat dataFormat) {
|
||||
this.contextInfo = contextInfo;
|
||||
this.dataContainer = dataContainer;
|
||||
this.dataFormat = dataFormat;
|
||||
this.dataFilter = dataFilter;
|
||||
rowLimit = ServletAppUtils.getServletApplication()
|
||||
.getAppConfiguration()
|
||||
.getResourceQuota(WebSQLConstants.QUOTA_PROP_ROW_LIMIT);
|
||||
@@ -174,8 +162,6 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
|
||||
webResultSet.setReadOnlyInfo(contextInfo.getProcessor().getExecutionContext());
|
||||
|
||||
WebSQLResultsInfo resultsInfo = contextInfo.saveResult(dataContainer, trace, bindings, rows.size() == 1);
|
||||
resultsInfo.setDataFilter(dataFilter);
|
||||
resultsInfo.setQueryText(resultSet.getSourceStatement().getQueryString());
|
||||
webResultSet.setResultsInfo(resultsInfo);
|
||||
|
||||
boolean isSingleEntity = DBExecUtils.detectSingleSourceTable(bindings) != null;
|
||||
@@ -246,6 +232,6 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// no-op
|
||||
rows.clear();
|
||||
}
|
||||
}
|
||||
|
||||
+49
-39
@@ -110,7 +110,7 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public WebSQLDialectInfo getDialectInfo(@NotNull WebConnectionInfo connectionInfo) {
|
||||
public WebSQLDialectInfo getDialectInfo(@NotNull WebConnectionInfo connectionInfo) throws DBWebException {
|
||||
DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer();
|
||||
SQLDialect dialect = getSqlDialectFromConnection(dataSourceContainer);
|
||||
return new WebSQLDialectInfo(dataSourceContainer.getDataSource(), dialect);
|
||||
@@ -400,16 +400,21 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
DBExecUtils.tryExecuteRecover(
|
||||
monitor,
|
||||
contextInfo.getProcessor().getConnection().getDataSource(),
|
||||
monitor1 ->
|
||||
result[0] = contextInfo.getProcessor().updateResultsDataBatch(
|
||||
monitor1, contextInfo, resultsId, updatedRows, deletedRows, addedRows, dataFormat)
|
||||
monitor1 -> {
|
||||
try {
|
||||
result[0] = contextInfo.getProcessor().updateResultsDataBatch(
|
||||
monitor1, contextInfo, resultsId, updatedRows, deletedRows, addedRows, dataFormat);
|
||||
} catch (Exception e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result[0];
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ThrowableFunction<T, R> {
|
||||
R apply(T obj) throws DBException;
|
||||
R apply(T obj) throws Exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -432,6 +437,9 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
@NotNull Integer lobColumnIndex,
|
||||
@NotNull WebSQLResultsRow row
|
||||
) throws DBWebException {
|
||||
if (row == null) {
|
||||
throw new DBWebException("Results row is not found");
|
||||
}
|
||||
WebSQLProcessor processor = contextInfo.getProcessor();
|
||||
ThrowableFunction<DBRProgressMonitor, String> function = monitor -> processor.readStringValue(
|
||||
monitor, contextInfo, resultsId, lobColumnIndex, row);
|
||||
@@ -449,7 +457,13 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
DBExecUtils.tryExecuteRecover(
|
||||
processor.getWebSession().getProgressMonitor(),
|
||||
processor.getConnection().getDataSource(),
|
||||
monitor -> result.append(function.apply(monitor))
|
||||
monitor -> {
|
||||
try {
|
||||
result.append(function.apply(monitor));
|
||||
} catch (Exception e) {
|
||||
throw new InvocationTargetException(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result.toString();
|
||||
} catch (DBException e) {
|
||||
@@ -462,8 +476,7 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
try {
|
||||
return contextInfo.getProcessor().generateResultsDataUpdateScript(
|
||||
contextInfo.getProcessor().getWebSession().getProgressMonitor(),
|
||||
contextInfo, resultsId, updatedRows, deletedRows, addedRows
|
||||
);
|
||||
contextInfo, resultsId, updatedRows, deletedRows, addedRows, dataFormat);
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error genering update script", e);
|
||||
}
|
||||
@@ -503,11 +516,10 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
@NotNull String nodePath,
|
||||
@Nullable String resultId,
|
||||
@Nullable WebSQLDataFilter filter,
|
||||
@Nullable WebDataFormat dataFormat
|
||||
) {
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
|
||||
@Nullable WebDataFormat dataFormat) throws DBWebException {
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<String>() {
|
||||
@Override
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
|
||||
try {
|
||||
monitor.beginTask("Read data", 1);
|
||||
monitor.subTask("Extra data from " + nodePath);
|
||||
@@ -521,8 +533,7 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
dataContainer,
|
||||
resultId,
|
||||
filter != null ? filter : new WebSQLDataFilter(),
|
||||
dataFormat
|
||||
);
|
||||
dataFormat);
|
||||
this.result = executeResults.getStatusMessage();
|
||||
this.extendedResults = executeResults;
|
||||
} catch (Throwable e) {
|
||||
@@ -553,25 +564,24 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
@Override
|
||||
public WebSQLExecuteInfo asyncGetQueryResults(@NotNull WebSession webSession, @NotNull String taskId) throws DBWebException {
|
||||
WebAsyncTaskInfo taskStatus = webSession.asyncTaskStatus(taskId, false);
|
||||
return (WebSQLExecuteInfo) taskStatus.getExtendedResult();
|
||||
if (taskStatus != null) {
|
||||
return (WebSQLExecuteInfo) taskStatus.getExtendedResult();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// Explain plan
|
||||
|
||||
@Override
|
||||
public WebAsyncTaskInfo asyncSqlExplainExecutionPlan(
|
||||
@NotNull WebSQLContextInfo contextInfo,
|
||||
@NotNull String sql,
|
||||
@NotNull Map<String, Object> configuration
|
||||
) {
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
|
||||
public WebAsyncTaskInfo asyncSqlExplainExecutionPlan(@NotNull WebSQLContextInfo contextInfo, @NotNull String sql, @NotNull Map<String, Object> configuration) throws DBException {
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<String>() {
|
||||
@Override
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
|
||||
try {
|
||||
monitor.beginTask("Explain execution plan", 1);
|
||||
monitor.subTask("Explain query [" + sql + "] execution plan");
|
||||
WebSQLExecutionPlan executeResults = contextInfo.getProcessor().explainExecutionPlan(monitor, sql, configuration);
|
||||
WebSQLExecutionPlan executeResults = contextInfo.getProcessor().explainExecutionPlan(monitor, contextInfo, sql, configuration);
|
||||
this.result = "Execution plan explain has been scheduled";
|
||||
this.extendedResults = executeResults;
|
||||
} catch (Throwable e) {
|
||||
@@ -587,11 +597,14 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
@Override
|
||||
public WebSQLExecutionPlan asyncSqlExplainExecutionPlanResult(@NotNull WebSession webSession, @NotNull String taskId) throws DBWebException {
|
||||
WebAsyncTaskInfo taskStatus = webSession.asyncTaskStatus(taskId, false);
|
||||
return (WebSQLExecutionPlan) taskStatus.getExtendedResult();
|
||||
if (taskStatus != null) {
|
||||
return (WebSQLExecutionPlan) taskStatus.getExtendedResult();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebSQLScriptInfo parseSqlScript(@NotNull WebConnectionInfo connectionInfo, @NotNull String sqlScript) {
|
||||
public WebSQLScriptInfo parseSqlScript(@NotNull WebConnectionInfo connectionInfo, @NotNull String sqlScript) throws DBWebException {
|
||||
SQLDialect dialect = getSqlDialectFromConnection(connectionInfo.getDataSourceContainer());
|
||||
List<SQLScriptElement> queries = SQLScriptParser.parseScript(
|
||||
connectionInfo.getDataSource(),
|
||||
@@ -605,7 +618,7 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebSQLQueryInfo parseSqlQuery(@NotNull WebConnectionInfo connectionInfo, @NotNull String sqlScript, int cursorPosition) {
|
||||
public WebSQLQueryInfo parseSqlQuery(@NotNull WebConnectionInfo connectionInfo, @NotNull String sqlScript, int cursorPosition) throws DBWebException {
|
||||
SQLDialect dialect = getSqlDialectFromConnection(connectionInfo.getDataSourceContainer());
|
||||
SQLScriptElement query = SQLScriptParser.parseQuery(
|
||||
connectionInfo.getDataSource(),
|
||||
@@ -648,20 +661,14 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebAsyncTaskInfo getRowDataCount(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo, @NotNull String resultsId) {
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
|
||||
public WebAsyncTaskInfo getRowDataCount(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo, @NotNull String resultsId) throws DBWebException {
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<String>() {
|
||||
@Override
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
|
||||
try {
|
||||
monitor.beginTask("Get row data count", 1);
|
||||
WebSQLResultsInfo results = contextInfo.getResults(resultsId);
|
||||
long rowCount = DBUtils.readRowCount(
|
||||
monitor,
|
||||
contextInfo.getProcessor().getExecutionContext(),
|
||||
results.getDataContainer(),
|
||||
results.getDataFilter(),
|
||||
this
|
||||
);
|
||||
long rowCount = DBUtils.readRowCount(monitor, contextInfo.getProcessor().getExecutionContext(), results.getDataContainer(), null, this);
|
||||
this.result = "Row data count completed";
|
||||
this.extendedResults = rowCount;
|
||||
} catch (Throwable e) {
|
||||
@@ -678,16 +685,19 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
@Nullable
|
||||
public Long getRowDataCountResult(@NotNull WebSession webSession, @NotNull String taskId) throws DBWebException {
|
||||
WebAsyncTaskInfo taskStatus = webSession.asyncTaskStatus(taskId, false);
|
||||
return (Long) taskStatus.getExtendedResult();
|
||||
if (taskStatus != null) {
|
||||
return (Long) taskStatus.getExtendedResult();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebAsyncTaskInfo asyncSqlSetAutoCommit(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo, boolean autoCommit) {
|
||||
public WebAsyncTaskInfo asyncSqlSetAutoCommit(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo, boolean autoCommit) throws DBWebException {
|
||||
return contextInfo.setAutoCommit(autoCommit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebAsyncTaskInfo asyncSqlRollbackTransaction(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo) {
|
||||
public WebAsyncTaskInfo asyncSqlRollbackTransaction(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo) throws DBWebException {
|
||||
return contextInfo.rollbackTransaction();
|
||||
}
|
||||
|
||||
|
||||
@@ -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.134.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 1.0.132.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.admin</artifactId>
|
||||
<version>1.0.134-SNAPSHOT</version>
|
||||
<version>1.0.132-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+6
-3
@@ -16,7 +16,10 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.admin;
|
||||
|
||||
import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.DBWFeatureSet;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebAction;
|
||||
import io.cloudbeaver.model.WebPropertyInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.registry.WebAuthProviderConfiguration;
|
||||
@@ -113,7 +116,7 @@ public interface DBWServiceAdmin extends DBWService {
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String userID,
|
||||
@NotNull String providerId,
|
||||
@WebParameterSecure @NotNull Map<String, Object> credentials
|
||||
@NotNull Map<String, Object> credentials
|
||||
) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
|
||||
@@ -153,7 +156,7 @@ public interface DBWServiceAdmin extends DBWService {
|
||||
boolean disabled,
|
||||
@Nullable String iconURL,
|
||||
@Nullable String description,
|
||||
@WebParameterSecure @Nullable Map<String, Object> parameters) throws DBWebException;
|
||||
@Nullable Map<String, Object> parameters) throws DBWebException;
|
||||
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
|
||||
boolean deleteAuthProviderConfiguration(@NotNull WebSession webSession, @NotNull String id) throws DBWebException;
|
||||
|
||||
|
||||
+8
-6
@@ -16,7 +16,10 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.admin.impl;
|
||||
|
||||
import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.DBWFeatureSet;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.auth.provider.local.LocalAuthProvider;
|
||||
import io.cloudbeaver.model.WebPropertyInfo;
|
||||
import io.cloudbeaver.model.config.CBAppConfig;
|
||||
@@ -360,11 +363,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setUserCredentials(@NotNull WebSession webSession,
|
||||
@NotNull String userID,
|
||||
@NotNull String providerId,
|
||||
@WebParameterSecure @NotNull Map<String, Object> credentials
|
||||
) throws DBWebException {
|
||||
public boolean setUserCredentials(@NotNull WebSession webSession, @NotNull String userID, @NotNull String providerId, @NotNull Map<String, Object> credentials) throws DBWebException {
|
||||
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(providerId);
|
||||
if (authProvider == null) {
|
||||
throw new DBWebException("Invalid auth provider '" + providerId + "'");
|
||||
@@ -666,6 +665,9 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
if (configurationMode) {
|
||||
// In config mode we always refresh because admin user doesn't exist yet
|
||||
webSession.resetUserState();
|
||||
} else {
|
||||
// Just reload session state
|
||||
webSession.refreshUserData();
|
||||
}
|
||||
WebAppUtils.getWebApplication().getDriverRegistry().refreshApplicableDrivers();
|
||||
} catch (Throwable e) {
|
||||
|
||||
@@ -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.134.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 1.0.132.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.auth</artifactId>
|
||||
<version>1.0.134-SNAPSHOT</version>
|
||||
<version>1.0.132-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+1
-6
@@ -18,7 +18,6 @@ package io.cloudbeaver.service.auth;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebAction;
|
||||
import io.cloudbeaver.WebParameterSecure;
|
||||
import io.cloudbeaver.model.WebPropertyInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.service.DBWService;
|
||||
@@ -78,11 +77,7 @@ public interface DBWServiceAuth extends DBWService {
|
||||
WebAuthProviderInfo[] getAuthProviders(@NotNull HttpServletRequest request) throws DBWebException;
|
||||
|
||||
@WebAction()
|
||||
boolean changeLocalPassword(
|
||||
@NotNull WebSession webSession,
|
||||
@WebParameterSecure @NotNull String oldPassword,
|
||||
@WebParameterSecure @NotNull String newPassword
|
||||
) throws DBWebException;
|
||||
boolean changeLocalPassword(@NotNull WebSession webSession, @NotNull String oldPassword, @NotNull String newPassword) throws DBWebException;
|
||||
|
||||
@WebAction(authRequired = false)
|
||||
WebPropertyInfo[] listUserProfileProperties(@NotNull WebSession webSession);
|
||||
|
||||
+1
-6
@@ -71,13 +71,8 @@ public class WebAsyncAuthJob extends AbstractJob implements CustomCancelableJob
|
||||
|
||||
@Override
|
||||
public void cancelJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo taskInfo) {
|
||||
cancelJob(webSession, taskInfo, "Canceled by the user");
|
||||
}
|
||||
|
||||
public void cancelJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo taskInfo, @NotNull String errorMessage) {
|
||||
taskInfo.setRunning(false);
|
||||
taskInfo.setJobError(new DBException(errorMessage));
|
||||
taskInfo.setJobError(new DBException("Canceled by the user"));
|
||||
WebEventUtils.sendAsyncTaskEvent(webSession, taskInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.auth;
|
||||
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
public class WebAsyncAuthTimeoutJob extends Job {
|
||||
@NotNull
|
||||
private final WebSession webSession;
|
||||
@NotNull
|
||||
private final WebAsyncTaskInfo asyncInfo;
|
||||
@NotNull
|
||||
private final WebAsyncAuthJob authJob;
|
||||
|
||||
public WebAsyncAuthTimeoutJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo asyncInfo, @NotNull WebAsyncAuthJob authJob) {
|
||||
super("Auth timeout job for " + asyncInfo.getName());
|
||||
this.webSession = webSession;
|
||||
this.asyncInfo = asyncInfo;
|
||||
this.authJob = authJob;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected IStatus run(@NotNull IProgressMonitor monitor) {
|
||||
if (asyncInfo.isRunning()) {
|
||||
authJob.cancelJob(webSession, asyncInfo, "Authentication timeout exceeded");
|
||||
}
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}
|
||||
+4
-8
@@ -62,7 +62,7 @@ import java.util.Map;
|
||||
public class WebServiceAuthImpl implements DBWServiceAuth {
|
||||
|
||||
private static final Log log = Log.getLog(WebServiceAuthImpl.class);
|
||||
private static final long DEFAULT_TIMEOUT_MILLISECONDS = 5 * 60 * 1000;
|
||||
public static final String CONFIG_TEMP_ADMIN_USER_ID = "temp_config_admin";
|
||||
|
||||
@Override
|
||||
public WebAuthStatus authLogin(
|
||||
@@ -120,15 +120,11 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
|
||||
if (CommonUtils.isEmpty(smAuthInfo.getRedirectUrl())) {
|
||||
throw new DBWebException("Missing redirect URL");
|
||||
}
|
||||
WebAsyncAuthJob job = new WebAsyncAuthJob(
|
||||
providerId + " authentication job",
|
||||
smAuthInfo.getAuthAttemptId(),
|
||||
linkWithActiveUser
|
||||
);
|
||||
WebAsyncTaskInfo authTask = webSession.createAsyncTask(providerId + " authentication");
|
||||
authTask.setRunning(true);
|
||||
authTask.setJob(job);
|
||||
new WebAsyncAuthTimeoutJob(webSession, authTask, job).schedule(DEFAULT_TIMEOUT_MILLISECONDS);
|
||||
authTask.setJob(
|
||||
new WebAsyncAuthJob(providerId + " authentication job", smAuthInfo.getAuthAttemptId(), linkWithActiveUser)
|
||||
);
|
||||
return new WebAsyncAuthStatus(smAuthInfo.getRedirectUrl(), authTask);
|
||||
} catch (SMTooManySessionsException e) {
|
||||
throw new DBWebException("User authentication failed", e.getErrorType(), e);
|
||||
|
||||
@@ -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.135.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 1.0.133.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.data.transfer</artifactId>
|
||||
<version>1.0.135-SNAPSHOT</version>
|
||||
<version>1.0.133-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ public interface DBWServiceDataTransfer extends DBWService {
|
||||
@NotNull WebSQLResultsInfo webSQLResultsInfo,
|
||||
@NotNull WebSession webSession) throws DBWebException;
|
||||
|
||||
@WebAction(requireGlobalPermissions = RMConstants.PERMISSION_DATA_EDITOR_EXPORT)
|
||||
@WebAction(requireGlobalPermissions = RMConstants.GLOBAL_PERMISSION_DATA_EDITOR_EXPORT)
|
||||
WebAsyncTaskInfo dataTransferExportDataFromResults(
|
||||
WebSQLContextInfo sqlContextInfo,
|
||||
String resultsId,
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ public class WebDataTransferImportServlet extends WebServiceServletBase {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Permission denied");
|
||||
return;
|
||||
}
|
||||
if (!session.hasGlobalPermission(DBWConstants.GLOBAL_PERMISSION_DATA_EDITOR_IMPORT)) {
|
||||
if (!session.hasPermission(DBWConstants.GLOBAL_PERMISSION_DATA_EDITOR_IMPORT)) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Import is not allowed for this user");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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.52.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 1.0.50.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.fs</artifactId>
|
||||
<version>1.0.52-SNAPSHOT</version>
|
||||
<version>1.0.50-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Cloudbeaver Web Service - LDAP
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.ldap.auth;singleton:=true
|
||||
Bundle-Version: 1.0.17.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 1.0.15.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.model;visibility:=reexport,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<configuration>
|
||||
<propertyGroup label="LDAP" description="LDAP authentication">
|
||||
<property id="ldap-host" label="%ldap-host" type="string" description="%ldap-host.description" required="true"/>
|
||||
<property id="ldap-port" label="%ldap-port" type="string" defaultValue="389" required="true" description="%ldap-port.description"/>
|
||||
<property id="ldap-port" label="%ldap-port" type="integer" defaultValue="389" required="true" description="%ldap-port.description"/>
|
||||
<property id="ldap-identifier-attr" label="%ldap-identifier-attr"
|
||||
defaultValue="cn"
|
||||
required="true"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.ldap.auth</artifactId>
|
||||
<version>1.0.17-SNAPSHOT</version>
|
||||
<version>1.0.15-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+31
-86
@@ -39,7 +39,6 @@ import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyStore;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.Certificate;
|
||||
@@ -49,8 +48,6 @@ import javax.naming.Context;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.*;
|
||||
import javax.naming.ldap.LdapName;
|
||||
import javax.naming.ldap.Rdn;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
@@ -309,22 +306,18 @@ public class LdapAuthProvider implements SMAuthProviderExternal<SMSession>, SMBr
|
||||
|
||||
@NotNull
|
||||
private String findUserNameFromDN(@NotNull String fullUserDN, @NotNull LdapSettings ldapSettings)
|
||||
throws DBException {
|
||||
try {
|
||||
LdapName ldapDN = new LdapName(fullUserDN);
|
||||
for (Rdn rdn : ldapDN.getRdns()) {
|
||||
if (rdn.getType().equalsIgnoreCase(ldapSettings.getUserIdentifierAttr())) {
|
||||
Object v = rdn.getValue();
|
||||
if (v instanceof byte[]) {
|
||||
return new String((byte[]) v, StandardCharsets.UTF_8);
|
||||
}
|
||||
return String.valueOf(v);
|
||||
}
|
||||
throws DBException {
|
||||
String userId = null;
|
||||
for (String dn : fullUserDN.split(",")) {
|
||||
if (dn.startsWith(ldapSettings.getUserIdentifierAttr() + "=")) {
|
||||
userId = dn.split("=")[1];
|
||||
break;
|
||||
}
|
||||
throw new DBException("Failed to determine userId from user DN: " + fullUserDN);
|
||||
} catch (Exception e) {
|
||||
throw new DBException("Invalid user DN: " + fullUserDN, e);
|
||||
}
|
||||
if (userId == null) {
|
||||
throw new DBException("Failed to determine userId from user DN: " + fullUserDN);
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -418,10 +411,10 @@ public class LdapAuthProvider implements SMAuthProviderExternal<SMSession>, SMBr
|
||||
private String buildFullUserDN(String userName, LdapSettings ldapSettings) {
|
||||
String fullUserDN = userName;
|
||||
|
||||
if (!CommonUtils.startsWithIgnoreCase(fullUserDN, ldapSettings.getUserIdentifierAttr())) {
|
||||
if (!fullUserDN.startsWith(ldapSettings.getUserIdentifierAttr())) {
|
||||
fullUserDN = String.join("=", ldapSettings.getUserIdentifierAttr(), userName);
|
||||
}
|
||||
if (CommonUtils.isNotEmpty(ldapSettings.getBaseDN()) && !CommonUtils.endsWithIgnoreCase(fullUserDN, ldapSettings.getBaseDN())) {
|
||||
if (CommonUtils.isNotEmpty(ldapSettings.getBaseDN()) && !fullUserDN.endsWith(ldapSettings.getBaseDN())) {
|
||||
fullUserDN = String.join(",", fullUserDN, ldapSettings.getBaseDN());
|
||||
}
|
||||
|
||||
@@ -533,7 +526,8 @@ public class LdapAuthProvider implements SMAuthProviderExternal<SMSession>, SMBr
|
||||
@NotNull
|
||||
private List<String> getGroupForMember(String fullDN, LdapSettings ldapSettings, Map<String, Object> authParameters) {
|
||||
DirContext context = null;
|
||||
Set<String> result = new LinkedHashSet<>();
|
||||
NamingEnumeration<SearchResult> searchResults = null;
|
||||
List<String> result = new ArrayList<>();
|
||||
try {
|
||||
Map<String, String> environment = creteAuthEnvironment(ldapSettings);
|
||||
if (CommonUtils.isEmpty(ldapSettings.getBindUserDN())) {
|
||||
@@ -553,12 +547,20 @@ public class LdapAuthProvider implements SMAuthProviderExternal<SMSession>, SMBr
|
||||
}
|
||||
|
||||
context = initConnection(environment);
|
||||
List<String> groupsByMemberOfAttribute = findGroupsByMemberOfAttribute(fullDN, context);
|
||||
log.debug("Found " + groupsByMemberOfAttribute.size() + " groups by memberOf attribute");
|
||||
result.addAll(groupsByMemberOfAttribute);
|
||||
List<String> groupsByMemberAttribute = findGroupsByMemberAttribute(fullDN, ldapSettings, context);
|
||||
log.debug("Found " + groupsByMemberAttribute.size() + " groups by member attribute");
|
||||
result.addAll(groupsByMemberAttribute);
|
||||
|
||||
String searchFilter = "(member=" + fullDN + ")";
|
||||
SearchControls searchControls = new SearchControls();
|
||||
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
|
||||
searchResults = context.search(ldapSettings.getBaseDN(), searchFilter, searchControls);
|
||||
while (searchResults.hasMore()) {
|
||||
try {
|
||||
SearchResult next = searchResults.next();
|
||||
result.add(next.getName());
|
||||
} catch (Exception e) {
|
||||
log.error("Failed fetch user group. Skipping...", e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Group not found", e);
|
||||
} finally {
|
||||
@@ -566,70 +568,13 @@ public class LdapAuthProvider implements SMAuthProviderExternal<SMSession>, SMBr
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
if (searchResults != null) {
|
||||
searchResults.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Close resource of ldap group search failed", e);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(result);
|
||||
}
|
||||
|
||||
private List<String> findGroupsByMemberOfAttribute(String fullDN, DirContext context) throws NamingException {
|
||||
List<String> result = new ArrayList<>();
|
||||
SearchControls memberOfSearch = new SearchControls();
|
||||
memberOfSearch.setSearchScope(SearchControls.OBJECT_SCOPE);
|
||||
memberOfSearch.setTimeLimit(30_000);
|
||||
memberOfSearch.setReturningAttributes(new String[] {"*", "+"});
|
||||
NamingEnumeration<SearchResult> userRecord = context.search(fullDN, "(objectClass=*)", memberOfSearch);
|
||||
try {
|
||||
if (userRecord.hasMore()) {
|
||||
SearchResult userResult = userRecord.next();
|
||||
Attributes userAttributes = userResult.getAttributes();
|
||||
Attribute memberOfAttr = userAttributes.get("memberOf");
|
||||
if (memberOfAttr != null) {
|
||||
NamingEnumeration<?> groups = memberOfAttr.getAll();
|
||||
while (groups.hasMore()) {
|
||||
String groupDN = String.valueOf(groups.next());
|
||||
result.add(groupDN);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (userRecord != null) {
|
||||
userRecord.close();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<String> findGroupsByMemberAttribute(String fullDN, LdapSettings ldapSettings, DirContext context) throws NamingException {
|
||||
List<String> result = new ArrayList<>();
|
||||
String searchFilter = "(member={0})";
|
||||
SearchControls searchControls = new SearchControls();
|
||||
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
NamingEnumeration<SearchResult> searchResults = context.search(
|
||||
ldapSettings.getBaseDN(),
|
||||
searchFilter,
|
||||
new Object[] {fullDN},
|
||||
searchControls
|
||||
);
|
||||
try {
|
||||
while (searchResults.hasMore()) {
|
||||
try {
|
||||
SearchResult next = searchResults.next();
|
||||
//add full dn
|
||||
result.add(next.getNameInNamespace());
|
||||
//add relative dn to base dn
|
||||
result.add(next.getName());
|
||||
} catch (Exception e) {
|
||||
log.error("Failed fetch user group. Skipping...", e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (searchResults != null) {
|
||||
searchResults.close();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.138.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 1.0.136.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.metadata</artifactId>
|
||||
<version>1.0.138-SNAPSHOT</version>
|
||||
<version>1.0.136-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -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.52.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 1.0.50.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.model,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.rm.nio</artifactId>
|
||||
<version>1.0.52-SNAPSHOT</version>
|
||||
<version>1.0.50-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -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.87.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 1.0.85.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.rm</artifactId>
|
||||
<version>1.0.87-SNAPSHOT</version>
|
||||
<version>1.0.85-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -69,6 +69,9 @@ extend type Query {
|
||||
|
||||
"Returns project settings that are specified for a current user"
|
||||
rmUserProjectSettings(projectId: ID!): Object @since(version: "25.2.1")
|
||||
|
||||
"Returns project settings that are specified for a subject"
|
||||
rmAdminUserProjectSettings(projectId: ID!, subjectId: String!): Object @since(version: "25.2.3")
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
@@ -137,4 +140,9 @@ extend type Mutation {
|
||||
rmAddUserProjectSettings(projectId: ID!, settings: Object!): Boolean! @since(version: "25.2.1")
|
||||
"Deletes project settings that are specified for a current user"
|
||||
rmDeleteUserProjectSettings(projectId: ID!, settingIds: [String!]!): Boolean! @since(version: "25.2.1")
|
||||
|
||||
"Adds project settings (e.g. enables simple view for a project) for a subject"
|
||||
rmAdminAddUserProjectSettings(projectId: ID!, subjectId:String!, settings: Object!): Boolean! @since(version: "25.2.3")
|
||||
"Deletes project settings that are specified for a subject"
|
||||
rmAdminDeleteUserProjectSettings(projectId: ID!, subjectId:String!, settingIds: [String!]!): Boolean! @since(version: "25.2.3")
|
||||
}
|
||||
+25
@@ -214,4 +214,29 @@ public interface DBWServiceRM extends DBWService {
|
||||
@NotNull @WebObjectId String projectId,
|
||||
@Nullable List<String> settings
|
||||
) throws DBWebException;
|
||||
|
||||
@NotNull
|
||||
@WebProjectAction(requireProjectPermissions = DBWConstants.PERMISSION_ADMIN)
|
||||
Map<String, Object> getProjectSettings(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull @WebObjectId String projectId,
|
||||
@NotNull String subjectId,
|
||||
@Nullable String settingId
|
||||
) throws DBWebException;
|
||||
|
||||
@WebProjectAction(requireProjectPermissions = DBWConstants.PERMISSION_ADMIN)
|
||||
boolean addProjectSettings(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull @WebObjectId String projectId,
|
||||
@NotNull String subjectId,
|
||||
@NotNull Map<String, Object> settings
|
||||
) throws DBWebException;
|
||||
|
||||
@WebProjectAction(requireProjectPermissions = DBWConstants.PERMISSION_ADMIN)
|
||||
boolean deleteProjectSettings(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull @WebObjectId String projectId,
|
||||
@NotNull String subjectId,
|
||||
@Nullable List<String> settings
|
||||
) throws DBWebException;
|
||||
}
|
||||
|
||||
+24
@@ -70,6 +70,14 @@ public class WebServiceBindingRM extends WebServiceBindingBase<DBWServiceRM> {
|
||||
getArgument(env, "settingId")
|
||||
)
|
||||
)
|
||||
.dataFetcher(
|
||||
"rmAdminUserProjectSettings", env -> getService(env).getProjectSettings(
|
||||
getWebSession(env),
|
||||
getArgumentVal(env, "projectId"),
|
||||
getArgumentVal(env, "subjectId"),
|
||||
getArgument(env, "settingId")
|
||||
)
|
||||
)
|
||||
;
|
||||
model.getMutationType()
|
||||
.dataFetcher("rmCreateResource",
|
||||
@@ -152,6 +160,22 @@ public class WebServiceBindingRM extends WebServiceBindingBase<DBWServiceRM> {
|
||||
getArgument(env, "settingIds")
|
||||
)
|
||||
)
|
||||
.dataFetcher(
|
||||
"rmAdminAddUserProjectSettings", env -> getService(env).addProjectSettings(
|
||||
getWebSession(env),
|
||||
getArgumentVal(env, "projectId"),
|
||||
getArgumentVal(env, "subjectId"),
|
||||
getArgumentVal(env, "settings")
|
||||
)
|
||||
)
|
||||
.dataFetcher(
|
||||
"rmAdminDeleteUserProjectSettings", env -> getService(env).deleteProjectSettings(
|
||||
getWebSession(env),
|
||||
getArgumentVal(env, "projectId"),
|
||||
getArgumentVal(env, "subjectId"),
|
||||
getArgument(env, "settingIds")
|
||||
)
|
||||
)
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
+85
-15
@@ -469,22 +469,79 @@ public class WebServiceRM implements DBWServiceRM {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addProjectSettings(@NotNull WebSession webSession, @NotNull String projectId, @NotNull Map<String, Object> settings)
|
||||
throws DBWebException {
|
||||
|
||||
try {
|
||||
validateProject(webSession, projectId);
|
||||
//fixme transactional
|
||||
SMController securityController = webSession.getSecurityController();
|
||||
securityController.deleteObjectSettings(
|
||||
projectId,
|
||||
SMObjectType.project,
|
||||
settings.keySet()
|
||||
);
|
||||
securityController.setObjectSettings(
|
||||
projectId,
|
||||
SMObjectType.project,
|
||||
settings
|
||||
);
|
||||
return true;
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error adding object settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> getProjectSettings(@NotNull WebSession webSession, @NotNull String projectId, @Nullable String settingId)
|
||||
throws DBWebException {
|
||||
try {
|
||||
validateProject(webSession, projectId);
|
||||
//todo accumulate settings from user and team. For now just return team settings
|
||||
return webSession.getSecurityController().getObjectSettings(
|
||||
projectId,
|
||||
SMObjectType.project,
|
||||
ServletAppUtils.getServletApplication().getAppConfiguration().getDefaultUserTeam(),
|
||||
settingId == null ? null : Set.of(settingId)
|
||||
);
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error getting project settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteProjectSettings(@NotNull WebSession webSession, @NotNull String projectId, @Nullable List<String> settings)
|
||||
throws DBWebException {
|
||||
try {
|
||||
validateProject(webSession, projectId);
|
||||
webSession.getSecurityController().deleteObjectSettings(
|
||||
projectId,
|
||||
SMObjectType.project,
|
||||
settings == null ? null : new HashSet<>(settings)
|
||||
);
|
||||
return true;
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error deleting object settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> getProjectSettings(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String projectId,
|
||||
@Nullable String subjectId,
|
||||
@Nullable String settingId
|
||||
) throws DBWebException {
|
||||
try {
|
||||
var project = webSession.getProjectById(projectId);
|
||||
if (project == null) {
|
||||
throw new DBWebException("Project '" + projectId + "' not found");
|
||||
}
|
||||
validateProject(webSession, projectId);
|
||||
return webSession.getSecurityController().getObjectSettings(
|
||||
projectId,
|
||||
SMObjectType.project,
|
||||
settingId
|
||||
subjectId,
|
||||
settingId == null ? null : Set.of(settingId)
|
||||
);
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error getting project settings", e);
|
||||
@@ -495,16 +552,23 @@ public class WebServiceRM implements DBWServiceRM {
|
||||
public boolean addProjectSettings(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String projectId,
|
||||
@NotNull String subjectId,
|
||||
@NotNull Map<String, Object> settings
|
||||
) throws DBWebException {
|
||||
try {
|
||||
var project = webSession.getProjectById(projectId);
|
||||
if (project == null) {
|
||||
throw new DBWebException("Project '" + projectId + "' not found");
|
||||
}
|
||||
webSession.getSecurityController().setObjectSettings(
|
||||
validateProject(webSession, projectId);
|
||||
//fixme transactional
|
||||
SMAdminController adminSecurityController = webSession.getAdminSecurityController();
|
||||
adminSecurityController.deleteObjectSettings(
|
||||
projectId,
|
||||
SMObjectType.project,
|
||||
subjectId,
|
||||
settings.keySet()
|
||||
);
|
||||
adminSecurityController.setObjectSettings(
|
||||
projectId,
|
||||
SMObjectType.project,
|
||||
subjectId,
|
||||
settings
|
||||
);
|
||||
return true;
|
||||
@@ -517,16 +581,15 @@ public class WebServiceRM implements DBWServiceRM {
|
||||
public boolean deleteProjectSettings(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String projectId,
|
||||
@NotNull String subjectId,
|
||||
@Nullable List<String> settings
|
||||
) throws DBWebException {
|
||||
try {
|
||||
var project = webSession.getProjectById(projectId);
|
||||
if (project == null) {
|
||||
throw new DBWebException("Project '" + projectId + "' not found");
|
||||
}
|
||||
webSession.getSecurityController().deleteObjectSettings(
|
||||
validateProject(webSession, projectId);
|
||||
webSession.getAdminSecurityController().deleteObjectSettings(
|
||||
projectId,
|
||||
SMObjectType.project,
|
||||
subjectId,
|
||||
settings == null ? null : new HashSet<>(settings)
|
||||
);
|
||||
return true;
|
||||
@@ -535,6 +598,13 @@ public class WebServiceRM implements DBWServiceRM {
|
||||
}
|
||||
}
|
||||
|
||||
private void validateProject(@NotNull WebSession webSession, @NotNull String projectId) throws DBWebException {
|
||||
var project = webSession.getProjectById(projectId);
|
||||
if (project == null) {
|
||||
throw new DBWebException("Project '" + projectId + "' not found");
|
||||
}
|
||||
}
|
||||
|
||||
private RMController getResourceController(WebSession webSession) {
|
||||
return webSession.getRmController();
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Cloudbeaver Web Service - Security
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.security;singleton:=true
|
||||
Bundle-Version: 1.0.90.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 1.0.88.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.model;visibility:=reexport,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.security</artifactId>
|
||||
<version>1.0.90-SNAPSHOT</version>
|
||||
<version>1.0.88-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
-5
@@ -101,11 +101,6 @@ public class LocalAuthProvider implements SMAuthProvider<LocalAuthSession>, SMBr
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsOpeningSessionAsChild() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean changeUserPassword(@NotNull WebSession webSession, @NotNull String oldPassword, @NotNull String newPassword) throws DBException {
|
||||
String userName = webSession.getUser().getUserId();
|
||||
|
||||
|
||||
+111
-106
@@ -111,69 +111,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
|
||||
@NotNull Map<String, Object> settings
|
||||
) throws DBException {
|
||||
String userId = getUserIdOrThrow();
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
|
||||
deleteObjectSettings(objectId, objectType, settings.keySet());
|
||||
try (
|
||||
PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
"INSERT INTO {table_prefix}CB_OBJECT_SETTINGS" +
|
||||
"(OBJECT_ID,OBJECT_TYPE,SUBJECT_ID,SETTING_ID,SETTING_VALUE,UPDATED_BY,UPDATE_TIME) " +
|
||||
"VALUES(?,?,?,?,?,?,CURRENT_TIMESTAMP)")
|
||||
) {
|
||||
for (Map.Entry<String, Object> entry : settings.entrySet()) {
|
||||
dbStat.setString(1, objectId);
|
||||
dbStat.setString(2, objectType.name());
|
||||
dbStat.setString(3, userId);
|
||||
dbStat.setString(4, entry.getKey());
|
||||
dbStat.setString(5, CommonUtils.toString(entry.getValue()));
|
||||
dbStat.setString(6, userId);
|
||||
dbStat.addBatch();
|
||||
}
|
||||
dbStat.executeBatch();
|
||||
}
|
||||
txn.commit();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBCException("Error while adding object settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> getObjectSettings(
|
||||
@NotNull String objectId,
|
||||
@NotNull SMObjectType objectType,
|
||||
@Nullable String settingId
|
||||
) throws DBException {
|
||||
String userId = getUserIdOrThrow();
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (
|
||||
PreparedStatement dbStat = dbCon.prepareStatement("SELECT SETTING_ID,SETTING_VALUE " +
|
||||
"FROM {table_prefix}CB_OBJECT_SETTINGS " +
|
||||
"WHERE OBJECT_ID=? AND OBJECT_TYPE=? AND SUBJECT_ID=?" +
|
||||
(settingId == null ? "" : " AND SETTING_ID=?"))
|
||||
) {
|
||||
int index = 1;
|
||||
dbStat.setString(index++, objectId);
|
||||
dbStat.setString(index++, objectType.name());
|
||||
dbStat.setString(index++, userId);
|
||||
if (settingId != null) {
|
||||
dbStat.setString(index++, settingId);
|
||||
}
|
||||
try (ResultSet dbResult = dbStat.executeQuery()) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
while (dbResult.next()) {
|
||||
result.put(
|
||||
dbResult.getString(1),
|
||||
dbResult.getString(2)
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBCException("Error while getting object settings", e);
|
||||
}
|
||||
setObjectSettings(objectId, objectType, userId, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -183,26 +121,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
|
||||
@Nullable Set<String> settingIds
|
||||
) throws DBException {
|
||||
String userId = getUserIdOrThrow();
|
||||
String sql = "DELETE FROM {table_prefix}CB_OBJECT_SETTINGS WHERE OBJECT_ID=? AND OBJECT_TYPE=? AND SUBJECT_ID=?";
|
||||
if (settingIds != null && !settingIds.isEmpty()) {
|
||||
sql += " AND SETTING_ID IN (" + SQLUtils.generateParamList(settingIds.size()) + ")";
|
||||
}
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(sql)) {
|
||||
int index = 1;
|
||||
dbStat.setString(index++, objectId);
|
||||
dbStat.setString(index++, objectType.name());
|
||||
dbStat.setString(index++, userId);
|
||||
if (settingIds != null) {
|
||||
for (String settingId : settingIds) {
|
||||
dbStat.setString(index++, settingId);
|
||||
}
|
||||
}
|
||||
dbStat.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBCException("Error while deleting object settings", e);
|
||||
}
|
||||
deleteObjectSettings(objectId, objectType, userId, settingIds);
|
||||
}
|
||||
|
||||
protected boolean isSubjectExists(String subjectId) throws DBCException {
|
||||
@@ -1363,6 +1282,112 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> getObjectSettings(
|
||||
@NotNull String objectId,
|
||||
@NotNull SMObjectType objectType,
|
||||
@Nullable String subjectId,
|
||||
@Nullable Set<String> settingIds
|
||||
) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (
|
||||
PreparedStatement dbStat = dbCon.prepareStatement("SELECT SETTING_ID,SETTING_VALUE " +
|
||||
"FROM {table_prefix}CB_OBJECT_SETTINGS " +
|
||||
"WHERE OBJECT_ID=? AND OBJECT_TYPE=? "
|
||||
+ (subjectId == null ? "" : " AND SUBJECT_ID=?") +
|
||||
(CommonUtils.isEmpty(settingIds) ? "" : " AND SETTING_ID IN (" + SQLUtils.generateParamList(settingIds.size()) + ")"))
|
||||
) {
|
||||
int index = 1;
|
||||
dbStat.setString(index++, objectId);
|
||||
dbStat.setString(index++, objectType.name());
|
||||
if (subjectId != null) {
|
||||
dbStat.setString(index++, subjectId);
|
||||
}
|
||||
if (!CommonUtils.isEmpty(settingIds)) {
|
||||
for (String settingId : settingIds) {
|
||||
dbStat.setString(index++, settingId);
|
||||
}
|
||||
}
|
||||
try (ResultSet dbResult = dbStat.executeQuery()) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
while (dbResult.next()) {
|
||||
result.put(
|
||||
dbResult.getString(1),
|
||||
dbResult.getString(2)
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBCException("Error while getting object settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObjectSettings(
|
||||
@NotNull String objectId,
|
||||
@NotNull SMObjectType objectType,
|
||||
@NotNull String subjectId,
|
||||
@NotNull Map<String, Object> settings
|
||||
) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
|
||||
deleteObjectSettings(objectId, objectType, settings.keySet());
|
||||
try (
|
||||
PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
"INSERT INTO {table_prefix}CB_OBJECT_SETTINGS" +
|
||||
"(OBJECT_ID,OBJECT_TYPE,SUBJECT_ID,SETTING_ID,SETTING_VALUE,UPDATED_BY,UPDATE_TIME) " +
|
||||
"VALUES(?,?,?,?,?,?,CURRENT_TIMESTAMP)")
|
||||
) {
|
||||
for (Map.Entry<String, Object> entry : settings.entrySet()) {
|
||||
dbStat.setString(1, objectId);
|
||||
dbStat.setString(2, objectType.name());
|
||||
dbStat.setString(3, subjectId);
|
||||
dbStat.setString(4, entry.getKey());
|
||||
dbStat.setString(5, CommonUtils.toString(entry.getValue()));
|
||||
dbStat.setString(6, subjectId);
|
||||
dbStat.addBatch();
|
||||
}
|
||||
dbStat.executeBatch();
|
||||
}
|
||||
txn.commit();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBCException("Error while adding object settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteObjectSettings(
|
||||
@NotNull String objectId,
|
||||
@NotNull SMObjectType objectType,
|
||||
@NotNull String subjectId,
|
||||
@Nullable Set<String> settingIds
|
||||
) throws DBException {
|
||||
String sql = "DELETE FROM {table_prefix}CB_OBJECT_SETTINGS WHERE OBJECT_ID=? AND OBJECT_TYPE=? AND SUBJECT_ID=?";
|
||||
if (settingIds != null && !settingIds.isEmpty()) {
|
||||
sql += " AND SETTING_ID IN (" + SQLUtils.generateParamList(settingIds.size()) + ")";
|
||||
}
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(sql)) {
|
||||
int index = 1;
|
||||
dbStat.setString(index++, objectId);
|
||||
dbStat.setString(index++, objectType.name());
|
||||
dbStat.setString(index++, subjectId);
|
||||
if (settingIds != null) {
|
||||
for (String settingId : settingIds) {
|
||||
dbStat.setString(index++, settingId);
|
||||
}
|
||||
}
|
||||
dbStat.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBCException("Error while deleting object settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private SMTeam fetchTeam(ResultSet dbResult) throws SQLException {
|
||||
return new SMTeam(
|
||||
@@ -2472,13 +2497,6 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
|
||||
if (autoAssign != null && CommonUtils.isNotEmpty(autoAssign.getAuthRoleAssignReason())) {
|
||||
log.info(activeUserId + " authenticated with role " + autoAssign.getAuthRole() + ", reason: " + autoAssign.getAuthRoleAssignReason());
|
||||
}
|
||||
} else {
|
||||
SMAuthProvider<?> authProviderInstance = authProvider.getInstance();
|
||||
if (!authProviderInstance.supportsOpeningSessionAsChild()) {
|
||||
var error = "Auth provider '" + authProviderId + "' doesn't support opening as a child session";
|
||||
updateAuthStatus(authId, SMAuthStatus.ERROR, dbStoredUserData, error, null);
|
||||
return SMAuthInfo.error(authId, error, isMainAuthSession, null, authInfo.getAppSessionId());
|
||||
}
|
||||
}
|
||||
dbStoredUserData.put(
|
||||
authConfiguration,
|
||||
@@ -2577,8 +2595,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
|
||||
.flatMap(externalTeamId -> findTeamByExternalTeamId(
|
||||
allTeams,
|
||||
externalTeamIdMetadataFieldName,
|
||||
externalTeamId,
|
||||
authProvider.isCaseInsensitive()
|
||||
externalTeamId
|
||||
).stream())
|
||||
.map(SMTeam::getTeamId)
|
||||
.toArray(String[]::new);
|
||||
@@ -2615,29 +2632,17 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<SMTeam> findTeamByExternalTeamId(
|
||||
SMTeam[] allTeams,
|
||||
String externalGroupParameterName,
|
||||
String groupId,
|
||||
boolean isCaseInsensitive
|
||||
) {
|
||||
private List<SMTeam> findTeamByExternalTeamId(SMTeam[] allTeams, String externalGroupParameterName, String groupId) {
|
||||
List<SMTeam> result = new ArrayList<>();
|
||||
for (SMTeam team : allTeams) {
|
||||
String teamGroupId = team.getMetaParameters().get(externalGroupParameterName);
|
||||
if (matchesGroupId(teamGroupId, groupId, isCaseInsensitive)) {
|
||||
if (CommonUtils.equalObjects(teamGroupId, groupId)) {
|
||||
result.add(team);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean matchesGroupId(String teamGroupId, String groupId, boolean isCaseInsensitive) {
|
||||
if (isCaseInsensitive) {
|
||||
return teamGroupId != null && teamGroupId.equalsIgnoreCase(groupId);
|
||||
}
|
||||
return CommonUtils.equalObjects(teamGroupId, groupId);
|
||||
}
|
||||
|
||||
|
||||
private String readProviderConfigId(String authAttemptId, String authProviderId) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
|
||||
@@ -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.50.qualifier
|
||||
Bundle-Release-Date: 20251201
|
||||
Bundle-Version: 1.0.48.qualifier
|
||||
Bundle-Release-Date: 20251103
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Fragment-Host: slf4j.api
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.slf4j</artifactId>
|
||||
<version>1.0.50-SNAPSHOT</version>
|
||||
<version>1.0.48-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -38,11 +38,6 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.lz4</groupId>
|
||||
<artifactId>lz4-java</artifactId>
|
||||
<version>1.8.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>3.25.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
<version>13.2.1.jre11</version>
|
||||
<version>12.8.0.jre11</version>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="io.cloudbeaver.ce.drivers.feature"
|
||||
label="Cloudbeaver CE 3rd Party Drivers"
|
||||
version="1.0.158.qualifier"
|
||||
version="1.0.156.qualifier"
|
||||
provider-name="DBeaver Corp">
|
||||
|
||||
<plugin id="io.cloudbeaver.resources.drivers.base" download-size="0" install-size="0" version="0.0.0"/>
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.ce.drivers.feature</artifactId>
|
||||
<version>1.0.158-SNAPSHOT</version>
|
||||
<version>1.0.156-SNAPSHOT</version>
|
||||
<packaging>eclipse-feature</packaging>
|
||||
</project>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="io.cloudbeaver.product.ce.feature"
|
||||
label="%featureName"
|
||||
version="25.3.0.qualifier"
|
||||
version="25.2.4.qualifier"
|
||||
provider-name="%providerName"
|
||||
plugin="io.cloudbeaver.product.ce">
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce.feature</artifactId>
|
||||
<version>25.3.0-SNAPSHOT</version>
|
||||
<version>25.2.4-SNAPSHOT</version>
|
||||
<packaging>eclipse-feature</packaging>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="io.cloudbeaver.server.feature"
|
||||
label="%featureName"
|
||||
version="25.3.0.qualifier"
|
||||
version="25.2.4.qualifier"
|
||||
provider-name="%providerName"
|
||||
plugin="io.cloudbeaver.server">
|
||||
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server.feature</artifactId>
|
||||
<version>25.3.0-SNAPSHOT</version>
|
||||
<version>25.2.4-SNAPSHOT</version>
|
||||
<packaging>eclipse-feature</packaging>
|
||||
</project>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="io.cloudbeaver.ws.feature"
|
||||
label="%featureName"
|
||||
version="1.0.88.qualifier"
|
||||
version="1.0.86.qualifier"
|
||||
provider-name="%providerName"
|
||||
plugin="io.cloudbeaver.model">
|
||||
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.ws.feature</artifactId>
|
||||
<version>1.0.88-SNAPSHOT</version>
|
||||
<version>1.0.86-SNAPSHOT</version>
|
||||
<packaging>eclipse-feature</packaging>
|
||||
</project>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<?pde version="3.5"?>
|
||||
|
||||
<product name="CloudBeaver ce Server" uid="io.cloudbeaver.product" id="io.cloudbeaver.product.ce.product"
|
||||
application="io.cloudbeaver.product.ce.application" version="25.3.0.qualifier" useFeatures="true"
|
||||
application="io.cloudbeaver.product.ce.application" version="25.2.4.qualifier" useFeatures="true"
|
||||
includeLaunchers="false">
|
||||
|
||||
<configIni use="default">
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../../</relativePath>
|
||||
</parent>
|
||||
<version>25.3.0-SNAPSHOT</version>
|
||||
<version>25.2.4-SNAPSHOT</version>
|
||||
<artifactId>web-server</artifactId>
|
||||
<packaging>eclipse-repository</packaging>
|
||||
<name>Cloudbeaver Server Product</name>
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dbeaver/js-helpers": "workspace:*",
|
||||
"@dbeaver/react-dnd": "workspace:^",
|
||||
"@dbeaver/ui-kit": "workspace:^",
|
||||
"react": "^19",
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2025 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 { memo, use, useMemo } from 'react';
|
||||
import { Cell, type CellRendererProps } from 'react-data-grid';
|
||||
import { DataGridCellContext, type IDataGridCellRenderer } from './DataGridCellContext.js';
|
||||
@@ -85,17 +77,19 @@ export const BaseCell = memo(function BaseCell<TRow, TSummaryRow>(props: CellRen
|
||||
|
||||
const renderDefaultCell = useMemo<IDataGridCellRenderer>(
|
||||
() =>
|
||||
({ onClick, onDoubleClick, onContextMenu, ...rest }) => (
|
||||
<Cell
|
||||
{...props}
|
||||
title={tooltip}
|
||||
onCellClick={(_, event) => onClick?.(event)}
|
||||
onCellDoubleClick={(_, event) => onDoubleClick?.(event)}
|
||||
onCellContextMenu={(_, event) => onContextMenu?.(event)}
|
||||
{...rest}
|
||||
isCellSelected={props.isCellSelected || rest.isFocused || false}
|
||||
/>
|
||||
),
|
||||
({ onClick, onDoubleClick, onContextMenu, ...rest }) => {
|
||||
return (
|
||||
<Cell
|
||||
{...props}
|
||||
title={tooltip}
|
||||
onCellClick={(_, event) => onClick?.(event)}
|
||||
onCellDoubleClick={(_, event) => onDoubleClick?.(event)}
|
||||
onCellContextMenu={(_, event) => onContextMenu?.(event)}
|
||||
{...rest}
|
||||
isCellSelected={props.isCellSelected || rest.isFocused || false}
|
||||
/>
|
||||
);
|
||||
},
|
||||
[...Object.values(props), tooltip],
|
||||
);
|
||||
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2025 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 { Row, type RenderRowProps } from 'react-data-grid';
|
||||
import { DataGridRowContext, type IDataGridRowRenderer } from './DataGridRowContext.js';
|
||||
import { memo, use, useEffect, useMemo } from 'react';
|
||||
import { DataGridRowContext } from './DataGridRowContext.js';
|
||||
import { memo, use, useEffect } from 'react';
|
||||
import { useGridReactiveValue } from './useGridReactiveValue.js';
|
||||
|
||||
export const BaseRow = memo(function BaseRow<TRow, TSummaryRow>(props: RenderRowProps<TRow, TSummaryRow>) {
|
||||
@@ -20,18 +12,5 @@ export const BaseRow = memo(function BaseRow<TRow, TSummaryRow>(props: RenderRow
|
||||
rowContext?.onScrollToBottom?.();
|
||||
}
|
||||
}, [rowsCount]);
|
||||
|
||||
const renderDefaultRow = useMemo<IDataGridRowRenderer>(
|
||||
() => override => (
|
||||
<Row
|
||||
{...props}
|
||||
{...override}
|
||||
/>
|
||||
),
|
||||
[...Object.values(props)],
|
||||
);
|
||||
|
||||
const rowElement = useGridReactiveValue(rowContext?.rowElement, props.rowIdx, props, renderDefaultRow);
|
||||
|
||||
return rowElement ?? <Row {...props} />;
|
||||
return <Row {...props} />;
|
||||
}) as <TRow, TSummaryRow>(props: RenderRowProps<TRow, TSummaryRow>) => React.ReactNode;
|
||||
|
||||
@@ -42,7 +42,6 @@ export interface DataGridCellKeyboardEvent extends React.KeyboardEvent<HTMLDivEl
|
||||
export interface DataGridProps extends IDataGridCellContext, IDataGridRowContext, IDataGridHeaderCellContext, React.PropsWithChildren {
|
||||
getRowHeight?: (rowIdx: number) => number;
|
||||
getRowId?: (rowIdx: number) => React.Key;
|
||||
getRowClass?: (rowIdx: number) => string | null;
|
||||
columnCount: IGridReactiveValue<number, []>;
|
||||
getColumnKey?: (colIdx: number) => string;
|
||||
onScroll?: (event: React.UIEvent<HTMLDivElement>) => void;
|
||||
@@ -79,11 +78,9 @@ export const DataGrid = forwardRef<DataGridRef, DataGridProps>(function DataGrid
|
||||
getCellEditable,
|
||||
columnCount,
|
||||
getColumnKey,
|
||||
rowElement,
|
||||
rowCount,
|
||||
getRowId,
|
||||
getRowHeight,
|
||||
getRowClass,
|
||||
onHeaderReorder,
|
||||
onScroll,
|
||||
onScrollToBottom,
|
||||
@@ -184,7 +181,7 @@ export const DataGrid = forwardRef<DataGridRef, DataGridProps>(function DataGrid
|
||||
|
||||
return (
|
||||
<HeaderDnDContext value={dndHeaderContext}>
|
||||
<DataGridRowContext value={{ rowElement, rowCount, onScrollToBottom }}>
|
||||
<DataGridRowContext value={{ rowCount, onScrollToBottom }}>
|
||||
<DataGridCellContext value={{ cell, cellText, cellElement, cellTooltip, onCellChange }}>
|
||||
<DataGridCellHeaderContext
|
||||
value={{ headerElement, headerText, getHeaderDnD, columnSortable, onColumnSort, columnSortingState, onHeaderKeyDown }}
|
||||
@@ -197,7 +194,6 @@ export const DataGrid = forwardRef<DataGridRef, DataGridProps>(function DataGrid
|
||||
headerRowHeight={getHeaderHeight?.()}
|
||||
rowHeight={getRowHeight ? row => getRowHeight(row.idx) : undefined}
|
||||
rowKeyGetter={getRowId ? row => getRowId(row.idx) : undefined}
|
||||
rowClass={getRowClass ? row => getRowClass(row.idx) : undefined}
|
||||
columnWidths={columnWidths}
|
||||
renderers={{
|
||||
renderRow: rowRenderer,
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2025 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 { createContext } from 'react';
|
||||
import type { IGridReactiveValue } from './IGridReactiveValue.js';
|
||||
|
||||
|
||||
@@ -1,22 +1,7 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2025 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 { createContext } from 'react';
|
||||
import type { IGridReactiveValue } from './IGridReactiveValue.js';
|
||||
|
||||
export interface IDataGridRowProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {}
|
||||
|
||||
export interface IDataGridRowRenderer {
|
||||
(propsOverride: Partial<IDataGridRowProps>): React.ReactNode;
|
||||
}
|
||||
|
||||
export interface IDataGridRowContext {
|
||||
rowElement?: IGridReactiveValue<React.ReactNode, [rowIdx: number, props: IDataGridRowProps, renderDefaultRow: IDataGridRowRenderer]>;
|
||||
rowCount: IGridReactiveValue<number, []>;
|
||||
onScrollToBottom?: () => void;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2025 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 './DataGrid.js';
|
||||
export * from './DataGridRowContext.js';
|
||||
export * from './DataGridCellContext.js';
|
||||
export * from './DataGridCellInnerContext.js';
|
||||
export * from './IGridReactiveValue.js';
|
||||
export * from './useCreateGridReactiveValue.js';
|
||||
export * from './useGridReactiveValue.js';
|
||||
export * from './reorderArray.js';
|
||||
|
||||
export * from './formatters/NullFormatter.js';
|
||||
export * from './formatters/BooleanFormatter.js';
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
export function reorderArray<T>(array: T[], from: number, to: number): T[] {
|
||||
const newArray = [...array];
|
||||
if (from < to) {
|
||||
newArray.splice(to, 0, array[from]!);
|
||||
newArray.splice(from, 1);
|
||||
} else {
|
||||
newArray.splice(from, 1);
|
||||
newArray.splice(to, 0, array[from]!);
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
@@ -1,15 +1,7 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2025 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 type { ColumnOrColumnGroup } from 'react-data-grid';
|
||||
import type { IInnerRow } from './IInnerRow.js';
|
||||
import { createContext, useId, useMemo, useState } from 'react';
|
||||
import { reorderArray } from '@dbeaver/js-helpers';
|
||||
import { reorderArray } from './reorderArray.js';
|
||||
|
||||
interface IHeaderDnDOptions {
|
||||
columns: Array<ColumnOrColumnGroup<IInnerRow, unknown>>;
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
"src"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../common-typescript/@dbeaver/js-helpers"
|
||||
},
|
||||
{
|
||||
"path": "../react-dnd"
|
||||
},
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
# @dbeaver/react-translate
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user