Compare commits

..
Author SHA1 Message Date
kseniaguzeeva c814017566 Merge branch 'devel' into dbeaver/pro#5476-task-scheduler-refactor 2025-10-27 16:02:04 +03:00
AinurandDaria Marutkina 4ab47583ad dbeaver/pro#7017 limit max idle time for anonymous user (#3846)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2025-10-27 11:38:34 +01:00
sergeyteleshevandEvgenia 37cfecbf91 [CB-5925] Charts - add export option (image) (#3819)
* dbeaver/pro#5078 adds chart export

* adds ExportImageDialog and reuses it across the app

* uses canvas2html for downloading images

* Revert "uses canvas2html for downloading images"

This reverts commit b59374cf85.

* moves transformClassesToStyles to helpers

* transforms styling attributes to computed styles

* adds file name

* fixes translations

* fixes styles for png image so it do not exports in black&white mode with incorrect sizing

* fixes styles for svg export

* uses domToImage library to save chart with legend

* cleanup

* moves helpers back to d3js diagrams

* cleanup

* uses html-to-image instead of dom-to-image

* cleanup

* pr fixes

* replaces the lib

* fixes svg so it is not blur

* validation for image urls

* cleanup

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2025-10-27 17:09:23 +08:00
046659672c dbeaver/pro#6956 redesign cli tools (#3830)
* dbeaver/pro#6956 redesign cli tools

* dbeaver/pro#6956 help per command

* dbeaver/pro#6956 refactor some commands to options

* dbeaver/pro#6982 Top level commands title fix

* dbeaver/pro#6956 fixes and more hidden options

* dbeaver/pro#6956 fix auth context

---------

Co-authored-by: serge-rider <serge@dbeaver.com>
Co-authored-by: MashaKorax <84867187+MashaKorax@users.noreply.github.com>
2025-10-24 18:30:41 +02:00
kseniaguzeeva 2a3a94608c Merge branch 'devel' into dbeaver/pro#5476-task-scheduler-refactor 2025-10-24 17:32:15 +03:00
Ainur bf52e23793 dbeaver/pro#7022 do not throw error if team with the same name exists (#3806) 2025-10-24 13:16:03 +00:00
Ainur 4a2ce73a9c dbeaver/pro#7181 collect internal db properties on graphql call (#3845) 2025-10-24 13:09:27 +00:00
serge-rider b73b707b7f Merge remote-tracking branch 'origin/devel' into devel 2025-10-24 08:59:26 +02:00
serge-rider 57461bd8be dbeaver/pro#7259 Nullability annotations 2025-10-24 08:56:48 +02:00
Sychev AndreyandEvgenia 71bebb32dd dbeaver/pro#7218 fix: value panel don't change the value of a cell (#3848)
The problem was that In ReactCodemirror, the updateListener calls onChange whenever the document changes and it's not marked as a remote transaction, but in value panel, when we switch changes, the new value coming to the panel was considered as the user change.
So, update listener were calling onChange from TextValuePresentation which calls editAction.set() and changes the real cell value.

In order to fix it, this commit mark programmatic updates by dispatching transaction annotations together with a change.

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2025-10-24 14:48:56 +08:00
ShadelessFox d810edf89d Merge branch 'devel' of https://github.com/dbeaver/cloudbeaver into dbeaver/pro#5476-task-scheduler-refactor 2025-10-23 14:11:54 +02:00
Sychev Andrey 28cade86aa dbeaver/pro#6026 feat: merge value panel tabs in one group (#3829)
* dbeaver/pro#6026 feat: merge value panel tabs in one group

move all text representations to the first tab level
specific representation is used if present (boolean, image)
auto type detection is used only for text

* dbeaver/pro#6026 refactor: remove unused CSS files for TextValuePresentation

* dbeaver/pro#6026 refactor: remove automatic state update in ValuePanel component

* dbeaver/pro#6026 refactor: update content type handling in ValuePanel

Move all the content type determination logic into the hook
Simplify the component

* dbeaver/pro#6026 refactor: consolidate content type handling in useAutoContentType hook

* dbeaver/pro#6026 fix: show scroll on tabs in value panel when overflow
2025-10-23 17:04:41 +08:00
MashaKorax e64d5ff92c Merge branch 'devel' into dbeaver/pro#5476-task-scheduler-refactor 2025-10-21 19:01:59 +02:00
Daria Marutkina 92186dd332 dbeaver/pro#7168 Update README file (#3842) 2025-10-20 12:16:08 +02:00
ShadelessFox 36be366c64 dbeaver/pro#5476 Move ZONE_ID_UTC to StandardConstants 2025-10-16 18:46:58 +02:00
sergeyteleshev 860a9f55f2 dbeaver/pro#7183 updates happy-dom to 20.0.2 (#3835) 2025-10-17 00:42:19 +08:00
dbeaver-devops 754c8d562d dbeaver/pro#7168 25.2.4 version bump (#3827) 2025-10-14 19:26:58 +02:00
Alexey PotsetsuevandEvgenia ed0e92cdd6 dbeaver/pro#6294 fix: dnd styles (#3824)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2025-10-14 23:45:33 +08:00
Serge Rider 9267e65300 dbeaver/pro#7142 Check node children for null (#3825) 2025-10-14 13:41:53 +02:00
Sychev AndreyandDaria Marutkina 4e3ec7cbf4 dbeaver/pro#7001 feat: add combobox cancel feature (#3801)
* dbeaver/pro#7001 feat: add combobox cancel feature

* dbeaver/pro#7001 refactor: reuse handleSelect for clear action

also changed cancel button styles

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2025-10-14 18:38:55 +08:00
AinurandDaria Marutkina 6978ae5355 dbeaver/pro#7058 fix error log for fs request (#3818)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2025-10-14 09:52:56 +02:00
Ruslan MusaevandEvgenia 3986debe8f dbeaver/cloudbeaver#3484 add ldap referral handling (#3817)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2025-10-14 09:40:55 +02:00
sergeyteleshev 38cfb0a690 [CB] Fix critical vulnerability with happy-dom (#3821)
* dbeaver/pro#7132 updates deps

* Revert "dbeaver/pro#7132 updates deps"

This reverts commit 522c56660c.

* updates only happy-dom
2025-10-13 22:48:36 +08:00
682a387564 dbeaver/pro#7080 refactor: grid API (#3799)
* dbeaver/pro#7080 refactor: grid API

* chore: cleanup after review

* chore: fix grid view action dependencies

* fix: build

* fix: update null checks and improve type safety in data handling

* fix: improve cache handling in useResultTraceDetails hook

* fix: correct cache reference in useResultTraceDetails function

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2025-10-13 19:30:54 +08:00
AinurandDaria Marutkina de0dee29ed dbeaver/pro#7019 grant connection permissions fix (#3807)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2025-10-10 15:02:47 +02:00
Sychev AndreyandDaria Marutkina 3a1ae15ecb dbeaver/pro#7026 fix: update cancel action to revert only selected el… (#3805)
* dbeaver/pro#7026 fix: update cancel action to revert only selected elements

* dbeaver/pro#7026 fix: bind revert action on esc

It was a misunderstanding at first, but esc is actually a revert action, not cancel action.

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2025-10-09 19:30:28 +08:00
Ainurandkseniaguzeeva 8cf064e845 dbeaver/pro#4038 add additional validation for servlets (#3779)
* dbeaver/pro#4038 add additional validation for servlets

* dbeaver/pro#4038 normalize paths for validation

* dbeaver/pro#4038 add fs update permission

---------

Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2025-10-09 11:18:49 +02:00
Ainur ec1b455f4d dbeaver/pro#7082 use same ws connection after relogin fix (#3803) 2025-10-09 10:52:07 +02:00
Ruslan Musaev 683d85c8d6 dbeaver/pro#7089 fix migration hotfix (#3814) 2025-10-08 16:04:34 +02:00
sergeyteleshev 8066904d4d [CB] The text of the settings in the navigator tree began to appear in bold (#3812)
* dbeaver/pro#6943 fixes bold title styles for switch

* build fix
2025-10-08 21:53:45 +08:00
Alexey Potsetsuev 7ea46ea2c5 dbeaver/pro#7111 fix: doubled hot keys handler (#3804) 2025-10-08 16:07:23 +08:00
Alexander Skoblikov 8aee24cf1c dbeaver/pro#6982 sql execution in dbvr (#3802) 2025-10-07 18:21:27 +02:00
sergeyteleshevandDaria Marutkina 5d83098d38 dbeaver/pro#6947 moves secrets parameters to a separate island (#3789)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2025-10-06 21:20:22 +08:00
Daria Marutkina 3e855c3afd dbeaver/pro#7052 Update README file (#3800) 2025-10-06 10:29:52 +02:00
Serge Rider 9c50589586 dbeaver/pro#7054 Nullable annotations 2025-10-03 14:27:52 +02:00
Serge Riderandmr-anton-t f99c2e3ebd dbeaver/pro#7054 Check null in remote clients (#3792)
* dbeaver/pro#7054 Check null in remote clients

* dbeaver/pro#7054 Do not verify nullable params

* dbeaver/pro#7054 Fix nullable

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2025-10-03 14:25:43 +02:00
Serge Rider e141575785 dbeaver/pro#2787 Annotations 2025-10-03 12:09:14 +02:00
b93b85f622 dbeaver/pro#6802 Update SQL schema config file paths (#3790)
* dbeaver/pro#6802 Update SQL schema config file paths

* dbeaver/pro#6802 Fix build

* dbeaver/pro#6802 Fix build

---------

Co-authored-by: Ivan Gagarkin <gagarkin@Ivans-MacBook-Pro.local>
Co-authored-by: MashaKorax <84867187+MashaKorax@users.noreply.github.com>
2025-10-03 11:51:02 +02:00
Serge Rider 267a753d87 Merge branch 'devel' of https://github.com/dbeaver/cloudbeaver into devel 2025-10-02 15:53:17 +02:00
Ruslan Musaev 6cea8cd64f dbeaver/pro#7060 fix connection read only (#3796) 2025-10-02 15:53:08 +02:00
serge-rider b630cb6791 dbeaver/pro#7052 25.2.3 version bump 2025-09-30 19:44:41 +02:00
240 changed files with 3770 additions and 3213 deletions
+12 -2
View File
@@ -25,9 +25,19 @@ You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
## Changelog
### 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;
- Fixed the critical vulnerability (CVE-2025-61927) in the Happy DOM library. The library was updated to version 20.0.2.
### 25.2.2 2025-10-06
- Replaced true/false text with intuitive checkboxes for boolean values in the metadata editor, making editing easier and faster;
- Keyboard navigation was improved across the application. Additional shortcuts are available in the main info panel (top-right corner). To help you discover them faster, relevant keyboard shortcuts are also shown directly in the tooltips of Data Editor buttons;
- Fixed an issue in the Data Editor where resizing one column in a wide table would cause previously resized columns to revert to their original width. Users can resize multiple columns as intended.
### 25.2.1 2025-09-22
- Autocomplete values in SQL Editor are ordered alphabetically to simplify object search.
- "Show all databases" setting was added on the connection page for MySQL and MariaDB.
- Autocomplete values in SQL Editor are ordered alphabetically to simplify object search;
- "Show all databases" setting was added on the connection page for MySQL and MariaDB;
- Different bug fixes and enhancements have been made.
### 25.2.0 2025-09-01
@@ -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.86.qualifier
Bundle-Release-Date: 20251006
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,
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.model</artifactId>
<version>1.0.86-SNAPSHOT</version>
<version>1.0.88-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -32,6 +32,8 @@ public interface DBWConstants {
String PERMISSION_SQL_RESULT_UPDATE = "sql-result-update";
String PERMISSION_SQL_EXECUTE_QUERY = "sql-execute-query";
String PERMISSION_FS_RESOURCE_EDIT = "fs-resource-edit";
String GLOBAL_PERMISSION_SCRIPT_EXECUTE = "permission.sql.script.execution";
String GLOBAL_PERMISSION_DATA_EDITOR_IMPORT = "permission.data-editor.import";
String GLOBAL_PERMISSION_DATA_EDITOR_EDITING = "permission.data-editor.editing";
@@ -36,7 +36,7 @@ public class WebDataSourceConnectEventListener implements DBPEventListener {
}
@Override
public void handleDataSourceEvent(DBPEvent event) {
public void handleDataSourceEvent(@NotNull DBPEvent event) {
if (!(event.getObject() instanceof DBPDataSourceContainer container)) {
return;
}
@@ -10,7 +10,7 @@ import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
public abstract class BaseDatasourceAccessCheckHandler implements DBPDataSourceHandler {
@Override
public void beforeConnect(
DBRProgressMonitor monitor,
@NotNull DBRProgressMonitor monitor,
@NotNull DBPDataSourceContainer dataSourceContainer
) throws DBException {
if (isDriverDisabled(dataSourceContainer.getDriver())) {
@@ -20,7 +20,7 @@ public abstract class BaseDatasourceAccessCheckHandler implements DBPDataSourceH
@Override
public void beforeDisconnect(
DBRProgressMonitor monitor,
@NotNull DBRProgressMonitor monitor,
@NotNull DBPDataSourceContainer dataSourceContainer
) throws DBException {
@@ -252,6 +252,7 @@ public class WebConnectionInfo {
}
@Property
@NotNull
public String[] getFeatures() {
List<String> features = new ArrayList<>();
@@ -281,11 +282,13 @@ public class WebConnectionInfo {
}
@Property
@NotNull
public DBNBrowseSettings getNavigatorSettings() {
return dataSourceContainer.getNavigatorSettings();
}
@Property
@NotNull
public List<WebDataFormat> getSupportedDataFormats() {
List<WebDataFormat> formats = new ArrayList<>();
formats.add(WebDataFormat.resultset);
@@ -300,11 +303,13 @@ public class WebConnectionInfo {
}
@Property
@NotNull
public WebConnectionOriginInfo getOrigin() {
return new WebConnectionOriginInfo(session, dataSourceContainer, dataSourceContainer.getOrigin());
}
@Property
@NotNull
public DBPDriverConfigurationType getConfigurationType() {
DBPDriverConfigurationType configurationType = dataSourceContainer.getConnectionConfiguration().getConfigurationType();
if (configurationType == null) {
@@ -330,6 +335,7 @@ public class WebConnectionInfo {
}
@Property
@NotNull
public String getAuthModel() {
String authModelId = dataSourceContainer.getConnectionConfiguration().getAuthModelId();
if (CommonUtils.isEmpty(authModelId)) {
@@ -339,6 +345,7 @@ public class WebConnectionInfo {
}
@Property
@NotNull
public WebPropertyInfo[] getAuthProperties() {
String authModelId = getAuthModel();
DBPAuthModelDescriptor authModel = DBWorkbench.getPlatform().getDataSourceProviderRegistry().getAuthModel(authModelId);
@@ -361,6 +368,7 @@ public class WebConnectionInfo {
}
@Property
@NotNull
public List<WebNetworkHandlerConfig> getNetworkHandlersConfig() {
var registry = NetworkHandlerRegistry.getInstance();
return dataSourceContainer.getConnectionConfiguration()
@@ -375,20 +383,23 @@ public class WebConnectionInfo {
}
@Property
@Nullable
public Map<String, Object> getCredentials() {
//dataSourceContainer.getConnectionConfiguration().getCredentialsProvider().getCredentials();
return null;
}
@Nullable
public Map<String, Object> getSavedAuthProperties() {
return savedAuthProperties;
}
@Nullable
public List<WebNetworkHandlerConfigInput> getSavedNetworkCredentials() {
return savedNetworkCredentials;
}
public void setSavedCredentials(Map<String, Object> authProperties, List<WebNetworkHandlerConfigInput> networkCredentials) {
public void setSavedCredentials(@Nullable Map<String, Object> authProperties, @Nullable List<WebNetworkHandlerConfigInput> networkCredentials) {
this.savedAuthProperties = authProperties;
this.savedNetworkCredentials = networkCredentials;
}
@@ -413,6 +424,7 @@ public class WebConnectionInfo {
}
@Property
@NotNull
public Map<String, String> getMainPropertyValues() {
Map<String, String> mainProperties = new LinkedHashMap<>();
mainProperties.put(DBConstants.PROP_HOST, getHost());
@@ -468,7 +480,7 @@ public class WebConnectionInfo {
return dataSourceContainer.getRequiredExternalAuth();
}
private boolean hasProjectPermission(RMProjectPermission projectPermission) {
private boolean hasProjectPermission(@NotNull RMProjectPermission projectPermission) {
DBPProject project = dataSourceContainer.getProject();
if (!(project instanceof WebProjectImpl webProject)) {
return false;
@@ -508,18 +520,21 @@ public class WebConnectionInfo {
}
@Property
@Nullable
public String getDefaultCatalogName() {
DBPConnectionConfiguration connectionConfiguration = dataSourceContainer.getConnectionConfiguration();
return connectionConfiguration.getBootstrap().getDefaultCatalogName();
}
@Property
@Nullable
public String getDefaultSchemaName() {
DBPConnectionConfiguration connectionConfiguration = dataSourceContainer.getConnectionConfiguration();
return connectionConfiguration.getBootstrap().getDefaultSchemaName();
}
@Property
@NotNull
public List<WebSecretInfo> getSharedSecrets() throws DBException {
return dataSourceContainer.listSharedCredentials()
.stream()
@@ -527,8 +542,8 @@ public class WebConnectionInfo {
.collect(Collectors.toList());
}
@NotNull
@Property
@NotNull
public List<String> getTools() {
if (!session.hasPermission(RMConstants.PERMISSION_DATABASE_DEVELOPER)) {
return List.of();
@@ -1,6 +1,7 @@
package io.cloudbeaver.model;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.DBPObject;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.connection.DBPDriverConstants;
@@ -52,35 +53,35 @@ public class WebExpertSettingsProperties implements DBPObject {
public static class KeepAliveIntervalFieldValidator implements IPropertyValueValidator<WebExpertSettingsProperties, Object> {
@Override
public boolean isValidValue(WebExpertSettingsProperties object, Object value) throws IllegalArgumentException {
public boolean isValidValue(@NotNull WebExpertSettingsProperties object, @Nullable Object value) throws IllegalArgumentException {
return CommonUtils.toBoolean(!object.driver.isEmbedded(), true);
}
}
public static class AutoCommitFieldValidator implements IPropertyValueValidator<WebExpertSettingsProperties, Object> {
@Override
public boolean isValidValue(WebExpertSettingsProperties object, Object value) throws IllegalArgumentException {
public boolean isValidValue(@NotNull WebExpertSettingsProperties object, @Nullable Object value) throws IllegalArgumentException {
return CommonUtils.toBoolean(object.driver.getDriverParameter(DBPDriverConstants.PARAM_SUPPORTS_TRANSACTIONS), true);
}
}
public static class ReadOnlyFieldValidator implements IPropertyValueValidator<WebExpertSettingsProperties, Object> {
@Override
public boolean isValidValue(WebExpertSettingsProperties object, Object value) throws IllegalArgumentException {
public boolean isValidValue(@NotNull WebExpertSettingsProperties object, @Nullable Object value) throws IllegalArgumentException {
return CommonUtils.toBoolean(object.driver.getDriverParameter(DBPDriverConstants.PARAM_SUPPORTS_READ_ONLY_MODE), true);
}
}
public static class DefaultCatalogFieldVisibleValidator implements IPropertyValueValidator<WebExpertSettingsProperties, Object> {
@Override
public boolean isValidValue(WebExpertSettingsProperties object, Object value) throws IllegalArgumentException {
public boolean isValidValue(@NotNull WebExpertSettingsProperties object, @Nullable Object value) throws IllegalArgumentException {
return CommonUtils.toBoolean(object.driver.getDriverParameter(DBPDriverConstants.PARAM_SUPPORTS_CATALOG_SELECTION), true);
}
}
public static class DefaultSchemaFieldVisibleValidator implements IPropertyValueValidator<WebExpertSettingsProperties, Object> {
@Override
public boolean isValidValue(WebExpertSettingsProperties object, Object value) throws IllegalArgumentException {
public boolean isValidValue(@NotNull WebExpertSettingsProperties object, @Nullable Object value) throws IllegalArgumentException {
return CommonUtils.toBoolean(object.driver.getDriverParameter(DBPDriverConstants.PARAM_SUPPORTS_SCHEMA_SELECTION), true);
}
}
@@ -41,6 +41,7 @@ import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.dbeaver.utils.RuntimeUtils;
import org.jkiss.utils.CommonUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
@@ -204,7 +205,7 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
initializeApplicationServices();
try {
try {
this.instanceServer = new CloudBeaverInstanceServer();
this.instanceServer = createInstanceServer();
} catch (Exception e) {
log.error("Error initializing instance server", e);
}
@@ -216,6 +217,10 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
return EXIT_OK;
}
protected CloudBeaverInstanceServer createInstanceServer() throws IOException {
return new CloudBeaverInstanceServer();
}
protected abstract void startServer() throws DBException;
@Override
@@ -62,6 +62,7 @@ public class ServletSystemInformationCollector<T extends ServletApplication> imp
private DBPConnectionInformation smDatabaseInfo;
private String workspacePath;
private final DeploymentType deploymentType;
private boolean internalDatabaseInfoCollected = false;
public ServletSystemInformationCollector(@NotNull T application) {
this.application = application;
@@ -158,7 +159,15 @@ public class ServletSystemInformationCollector<T extends ServletApplication> imp
/**
* Collects info about internal databases.
*/
public void collectInternalDatabaseUseInformation() throws DBException {
public synchronized void collectInternalDatabaseUseInformation() throws DBException {
if (internalDatabaseInfoCollected) {
return;
}
collectInternalDatabaseInfo();
this.internalDatabaseInfoCollected = true;
}
protected void collectInternalDatabaseInfo() throws DBException {
this.smDatabaseInfo = application.getAdminSecurityController(new NoAuthCredentialsProvider())
.getInternalDatabaseInformation();
}
@@ -16,20 +16,41 @@
*/
package io.cloudbeaver.model.cli;
import org.jkiss.dbeaver.model.cli.ApplicationCommandLine;
import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.cli.*;
import org.jkiss.dbeaver.model.cli.model.CommandLineAuthenticator;
import picocli.CommandLine;
public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationInstanceController> {
private static CloudBeaverCommandLine INSTANCE = null;
private CloudBeaverCommandLine() {
@Nullable
private final CommandLineAuthenticator authenticator;
public CloudBeaverCommandLine(@Nullable CommandLineAuthenticator authenticator) {
super();
this.authenticator = authenticator;
}
public synchronized static CloudBeaverCommandLine getInstance() {
if (INSTANCE == null) {
INSTANCE = new CloudBeaverCommandLine();
@Override
protected CloudBeaverTopLevelCommand createTopLevelCommand(
@Nullable ApplicationInstanceController applicationInstanceController,
@NotNull CommandLineContext context,
@NotNull CLIRunMeta runMeta
) {
if (authenticator != null) {
context.setContextParameter(CLIConstants.CONTEXT_PARAM_AUTHENTICATOR, authenticator);
}
return INSTANCE;
return new CloudBeaverTopLevelCommand(applicationInstanceController, context, runMeta);
}
@NotNull
@Override
protected CommandLine initCommandLine(
@Nullable ApplicationInstanceController applicationInstanceController,
@NotNull CommandLineContext context,
@NotNull CLIRunMeta runMeta
) {
return super.initCommandLine(applicationInstanceController, context, runMeta);
}
}
@@ -16,7 +16,7 @@
*/
package io.cloudbeaver.model.cli;
import org.apache.commons.cli.CommandLine;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
import org.jkiss.dbeaver.model.cli.ApplicationInstanceServer;
@@ -25,20 +25,27 @@ import org.jkiss.dbeaver.model.cli.CLIProcessResult;
import java.io.IOException;
public class CloudBeaverInstanceServer extends ApplicationInstanceServer<ApplicationInstanceController> {
private final CloudBeaverCommandLine commandLine;
public CloudBeaverInstanceServer() throws IOException {
super(ApplicationInstanceController.class);
this.commandLine = new CloudBeaverCommandLine(null);
}
protected CloudBeaverInstanceServer(@NotNull CloudBeaverCommandLine commandLine) throws IOException {
super(ApplicationInstanceController.class);
this.commandLine = commandLine;
}
@NotNull
@Override
public CLIProcessResult handleCommandLine(@NotNull String[] args) {
CommandLine cmd = CloudBeaverCommandLine.getInstance().getCommandLine(args);
try {
return CloudBeaverCommandLine.getInstance().executeCommandLineCommands(
cmd,
return commandLine.executeCommandLineCommands(
this,
false,
false
false,
args
);
} catch (Exception e) {
return new CLIProcessResult(CLIProcessResult.PostAction.ERROR, "Error executing command: " + e.getMessage());
@@ -0,0 +1,37 @@
/*
* 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 org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
import org.jkiss.dbeaver.model.cli.CLIRunMeta;
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", mixinStandardHelpOptions = true)
public class CloudBeaverTopLevelCommand extends AbstractTopLevelCommand {
protected CloudBeaverTopLevelCommand(
@Nullable ApplicationInstanceController controller,
@NotNull CommandLineContext context,
@NotNull CLIRunMeta meta
) {
super(controller, context, meta);
}
}
@@ -60,26 +60,31 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
return project.getId();
}
@NotNull
@Override
public String getNodeType() {
return "rm.project";
}
@NotNull
@Override
public String getNodeDisplayName() {
return project.getDisplayName();
}
@NotNull
@Override
public String getLocalizedName(String locale) {
public String getLocalizedName(@NotNull String locale) {
return getNodeDisplayName();
}
@Nullable
@Override
public String getNodeDescription() {
return project.getDescription();
}
@Nullable
@Override
public DBPImage getNodeIcon() {
return DBIcon.PROJECT;
@@ -90,6 +95,7 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
return true;
}
@Nullable
@Override
public DBNResourceManagerResource[] getChildren(@NotNull DBRProgressMonitor monitor) throws DBException {
if (children == null && !monitor.isForceCacheUsage()) {
@@ -109,14 +115,16 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
return ((DBNResourceManagerRoot) getParentNode()).getResourceController();
}
@NotNull
@Deprecated
@Override
public String getNodeItemPath() {
return getParentNode().getNodeItemPath() + "/" + getName();
}
@Nullable
@Override
public DBNNode refreshNode(DBRProgressMonitor monitor, Object source) throws DBException {
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
children = null;
return this;
}
@@ -141,6 +149,7 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
return project;
}
@NotNull
@Override
public String toString() {
return getNodeDisplayName();
@@ -49,21 +49,25 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
this.resource = resource;
}
@NotNull
@Override
public String getNodeType() {
return "rm.resource";
}
@NotNull
@Override
public String getNodeDisplayName() {
return resource.getName();
}
@Nullable
@Override
public String getNodeDescription() {
return null;
}
@Nullable
@Override
public DBPImage getNodeIcon() {
if (resource.isFolder()) {
@@ -109,6 +113,7 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
return resource.isFolder();
}
@Nullable
@Override
public DBNNode[] getChildren(@NotNull DBRProgressMonitor monitor) throws DBException {
if (children == null && !monitor.isForceCacheUsage()) {
@@ -154,14 +159,16 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
throw new DBException("Can't detect resource root node");
}
@NotNull
@Deprecated
@Override
public String getNodeItemPath() {
return getParentNode().getNodeItemPath() + "/" + getNodeDisplayName();
}
@Nullable
@Override
public DBNNode refreshNode(DBRProgressMonitor monitor, Object source) throws DBException {
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
this.children = null;
return super.refreshNode(monitor, source);
}
@@ -171,7 +178,7 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
}
@Override
public void rename(DBRProgressMonitor monitor, String newName) throws DBException {
public void rename(@NotNull DBRProgressMonitor monitor, @NotNull String newName) throws DBException {
String resourceName = resource.getName();
try {
if (newName.indexOf('.') == -1) {
@@ -192,6 +199,7 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
}
}
@NotNull
@Override
public String toString() {
return getNodeDisplayName();
@@ -19,6 +19,7 @@ package io.cloudbeaver.model.rm;
import io.cloudbeaver.model.session.WebSession;
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.DBPHiddenObject;
@@ -49,6 +50,7 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
return resourceController;
}
@NotNull
@Override
public String getNodeType() {
return "rm";
@@ -60,16 +62,19 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
return "rm";
}
@NotNull
@Override
public String getNodeDisplayName() {
return "resources";
}
@Nullable
@Override
public String getNodeDescription() {
return "Resources";
}
@Nullable
@Override
public DBPImage getNodeIcon() {
return null;
@@ -80,6 +85,7 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
return true;
}
@Nullable
@Override
public DBNResourceManagerProject[] getChildren(@NotNull DBRProgressMonitor monitor) throws DBException {
if (projects == null && !monitor.isForceCacheUsage()) {
@@ -109,6 +115,7 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
return projects;
}
@NotNull
@Deprecated
@Override
public String getNodeItemPath() {
@@ -116,8 +123,9 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
return NodePathType.ext.getPrefix() + getNodeDisplayName();
}
@Nullable
@Override
public DBNNode refreshNode(DBRProgressMonitor monitor, Object source) throws DBException {
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
projects = null;
return this;
}
@@ -54,12 +54,12 @@ import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.IOUtils;
import org.jkiss.utils.Pair;
import org.jkiss.utils.StandardConstants;
import java.io.IOException;
import java.nio.file.*;
import java.text.MessageFormat;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -1262,8 +1262,10 @@ public class LocalResourceController extends BaseLocalResourceController {
project.setProjectPermissions(allProjectPermissions);
if (Files.exists(path)) {
try {
project.setCreateTime(
OffsetDateTime.ofInstant(Files.getLastModifiedTime(path).toInstant(), ZoneId.of("UTC")).toInstant().toEpochMilli());
project.setCreateTime(OffsetDateTime.ofInstant(
Files.getLastModifiedTime(path).toInstant(),
StandardConstants.ZONE_ID_UTC
).toInstant().toEpochMilli());
} catch (IOException e) {
log.error(e);
}
@@ -1271,7 +1273,7 @@ public class LocalResourceController extends BaseLocalResourceController {
// Resource types
project.setResourceTypes(ResourceTypeRegistry.getInstance().getResourceTypes()
.stream()
.filter(ResourceTypeDescriptor::isManagable)
.filter(ResourceTypeDescriptor::isManageable)
.map(RMResourceType::new)
.toArray(RMResourceType[]::new));
@@ -18,22 +18,15 @@ package io.cloudbeaver.model.rm.local;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.SMSessionContext;
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
import org.jkiss.dbeaver.model.rm.RMProjectType;
import org.jkiss.dbeaver.model.rm.RMUtils;
import org.jkiss.dbeaver.registry.DataSourceRegistry;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.Pair;
import org.jkiss.dbeaver.registry.project.LocalProjectImpl;
import java.nio.file.Path;
import java.util.Collection;
public class RMLocalProject extends BaseProjectImpl {
@NotNull
private final Path projectPath;
public class RMLocalProject extends LocalProjectImpl {
@NotNull
private final RMProjectType projectType;
@@ -43,12 +36,10 @@ public class RMLocalProject extends BaseProjectImpl {
@NotNull Path projectPath,
@NotNull RMProjectType type
) {
super(workspace, sessionContext);
this.projectPath = projectPath;
super(workspace, sessionContext, projectPath);
this.projectType = type;
}
@Override
public boolean isVirtual() {
return true;
@@ -60,88 +51,8 @@ public class RMLocalProject extends BaseProjectImpl {
return RMUtils.makeProjectIdFromPath(projectPath, projectType);
}
@NotNull
@Override
public String getName() {
Object projectName = this.getProjectProperty(PROP_PROJECT_NAME);
if (projectName != null) {
return projectName.toString();
}
return projectPath.getFileName().toString();
}
@NotNull
@Override
public Path getAbsolutePath() {
return projectPath;
}
@Override
public boolean isOpen() {
return true;
}
@Override
public void ensureOpen() {
}
@Override
public boolean isUseSecretStorage() {
return false;
}
public Path getMetadataFilePath() {
return getMetadataPath().resolve(METADATA_STORAGE_FILE);
}
/**
* Method for Bulk Update of resources properties paths
*
* @param oldToNewPaths collection of OldPath to NewPath pairs
*/
public void moveResourcePropertiesBatch(@NotNull Collection<Pair<String, String>> oldToNewPaths) {
loadMetadata();
synchronized (metadataSync) {
for (var pathsPair : oldToNewPaths) {
final var oldResourcePath = CommonUtils.normalizeResourcePath(pathsPair.getFirst());
final var newResourcePath = CommonUtils.normalizeResourcePath(pathsPair.getSecond());
final var resProps = resourceProperties.remove(oldResourcePath);
if (resProps != null) {
resourceProperties.put(newResourcePath, resProps);
}
}
}
flushMetadata();
}
/**
* Method for Bulk Remove of resources properties
*/
public boolean resetResourcesPropertiesBatch(@NotNull Collection<String> resourcesPaths) {
loadMetadata();
boolean propertiesChanged = false;
synchronized (metadataSync) {
for (var resourcePath : resourcesPaths) {
var removedProperties = resourceProperties.remove(CommonUtils.normalizeResourcePath(resourcePath));
if (removedProperties != null) {
propertiesChanged = true;
}
}
}
if (propertiesChanged) {
flushMetadata();
}
return propertiesChanged;
}
public boolean canUpdateProjectName() {
return RMProjectType.SHARED.equals(projectType);
}
@NotNull
@Override
protected DBPDataSourceRegistry createDataSourceRegistry() {
return new DataSourceRegistry<>(this);
}
}
@@ -29,8 +29,8 @@ import org.jkiss.dbeaver.model.auth.SMAuthSpace;
import org.jkiss.dbeaver.model.auth.SMSessionContext;
import org.jkiss.dbeaver.model.auth.impl.AbstractSessionPersistent;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.security.user.SMTeam;
import org.jkiss.dbeaver.model.rm.RMProjectInfo;
import org.jkiss.dbeaver.model.security.user.SMTeam;
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
import org.jkiss.dbeaver.model.websocket.event.WSEventDeleteTempFile;
import org.jkiss.dbeaver.model.websocket.event.session.WSSessionExpiredEvent;
@@ -254,8 +254,12 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
@Property
public long getRemainingTime() {
if (application instanceof ServletAuthApplication authApplication) {
return authApplication.getMaxSessionIdleTime() + lastAccessTime - System.currentTimeMillis();
return getMaxSessionIdleTime(authApplication) + lastAccessTime - System.currentTimeMillis();
}
return Integer.MAX_VALUE;
}
protected long getMaxSessionIdleTime(@NotNull ServletAuthApplication authApplication) {
return authApplication.getMaxSessionIdleTime();
}
}
@@ -793,7 +793,7 @@ public class WebSession extends BaseWebSession
}
}
public List<WebAuthInfo> removeAuthInfo(String providerId, boolean needResetUserState) throws DBException {
public List<WebAuthInfo> removeAuthInfo(String providerId) throws DBException {
List<WebAuthInfo> oldInfo;
if (providerId == null) {
oldInfo = clearAuthTokens();
@@ -806,16 +806,12 @@ public class WebSession extends BaseWebSession
oldInfo = List.of();
}
}
if (authTokens.isEmpty() && needResetUserState) {
if (authTokens.isEmpty()) {
resetUserState();
}
return oldInfo;
}
public List<WebAuthInfo> removeAuthInfo(String providerId) throws DBException {
return removeAuthInfo(providerId, true);
}
public List<DBACredentialsProvider> getContextCredentialsProviders() {
return getAdapters(DBACredentialsProvider.class);
}
@@ -190,6 +190,10 @@ public class WebUserContext implements SMCredentialsProvider {
return isAuthorizedInSecurityManager() && getUser() != null;
}
public synchronized boolean isAnonymousUserAuthorizedInSM() {
return isAuthorizedInSecurityManager() && getUser() == null;
}
@Nullable
public synchronized WebUser getUser() {
return user;
@@ -56,6 +56,7 @@ public abstract class BaseServletPlatform extends BasePlatformImpl {
return folder;
}
@NotNull
public abstract ServletApplication getApplication();
private void initTempFolder(@NotNull DBRProgressMonitor monitor) {
@@ -17,11 +17,11 @@
package io.cloudbeaver.utils;
import org.jkiss.dbeaver.model.DBConstants;
import org.jkiss.utils.StandardConstants;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class CBModelConstants {
public static final DateTimeFormatter ISO_DATE_FORMAT = DateTimeFormatter.ofPattern(DBConstants.DEFAULT_ISO_TIMESTAMP_FORMAT)
.withZone(ZoneId.of("UTC"));
.withZone(StandardConstants.ZONE_ID_UTC);
}
@@ -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.2.2.qualifier
Bundle-Release-Date: 20251006
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.2.2-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.131.qualifier
Bundle-Release-Date: 20251006
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.131-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.2.2.qualifier
Bundle-Release-Date: 20251006
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.2.2-SNAPSHOT</version>
<version>25.2.4-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -314,11 +314,6 @@ public abstract class CBApplication<T extends CBServerConfig>
}
grantPermissionsToConnections();
}
try {
this.systemInformationCollector.collectInternalDatabaseUseInformation();
} catch (DBException e) {
log.error("Error collecting system information", e);
}
eventController.scheduleCheckJob();
@@ -400,6 +395,9 @@ public abstract class CBApplication<T extends CBServerConfig>
getAppConfiguration(),
null
);
if (!isMultiNode()) {
grantPermissionsToConnections();
}
} catch (Exception e) {
log.error("Error loading server auto configuration", e);
}
@@ -219,8 +219,13 @@ public class CBStaticServlet extends DefaultServlet {
if (pathInContext.startsWith("/")) {
pathInContext = pathInContext.substring(1);
}
Path filePath = contentRoot.resolve(pathInContext).normalize();
if (!filePath.startsWith(contentRoot)) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Path filePath = contentRoot.resolve(pathInContext);
try (InputStream fis = Files.newInputStream(filePath)) {
IOUtils.copyStream(fis, baos);
}
@@ -304,14 +304,11 @@ public class CBSessionManager implements WebAppSessionManager {
}
public void expireIdleSessions() {
long maxSessionIdleTime = application.getMaxSessionIdleTime();
List<BaseWebSession> expiredList = new ArrayList<>();
synchronized (sessionMap) {
for (Iterator<BaseWebSession> iterator = sessionMap.values().iterator(); iterator.hasNext(); ) {
var session = iterator.next();
long idleMillis = System.currentTimeMillis() - session.getLastAccessTimeMillis();
if (idleMillis >= maxSessionIdleTime) {
if (!session.isValid()) {
iterator.remove();
expiredList.add(session);
}
@@ -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.2.2.qualifier
Bundle-Release-Date: 20251006
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,
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.server</artifactId>
<version>25.2.2-SNAPSHOT</version>
<version>25.2.4-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -114,12 +114,11 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
@Nullable
protected static WebSession getSessionFromContext(DataFetchingEnvironment env) {
WebSession webSession = env.getGraphQlContext().get(WebSession.class.getName());
return webSession;
return env.getGraphQlContext().get(WebSession.class.getName());
}
@NotNull
protected static WebSession getSessionFromContextOrThrow(DataFetchingEnvironment env) throws DBWebException {
protected static WebSession getSessionFromContextOrThrow(@NotNull DataFetchingEnvironment env) throws DBWebException {
WebSession webSession = env.getGraphQlContext().get(WebSession.class.getName());
if (webSession == null) {
throw new DBWebException("Web session not found in GraphQL context");
@@ -127,7 +126,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
return webSession;
}
protected static WebSession getWebSession(DataFetchingEnvironment env, boolean errorOnNotFound) throws DBWebException {
protected static WebSession getWebSession(@NotNull DataFetchingEnvironment env, boolean errorOnNotFound) throws DBWebException {
if (env.getGraphQlContext().getBoolean(CloudbeaverCliConstants.CLI_MODE)) {
return getSessionFromContextOrThrow(env);
}
@@ -135,20 +134,20 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
GraphQLEndpoint.getServletRequestOrThrow(env), getServletResponse(env), errorOnNotFound);
}
protected static String getProjectReference(DataFetchingEnvironment env) {
protected static String getProjectReference(@NotNull DataFetchingEnvironment env) {
return env.getArgument("projectId");
}
@NotNull
protected static WebConnectionInfo getWebConnection(DataFetchingEnvironment env) throws DBWebException {
return getWebConnection(getWebSession(env), getProjectReference(env), env.getArgument("connectionId"));
protected static WebConnectionInfo getWebConnection(@NotNull DataFetchingEnvironment env) throws DBWebException {
return getWebConnection(getWebSession(env), getProjectReference(env), getArgumentVal(env, "connectionId"));
}
/**
* Returns WebSession from cache or null
*/
@Nullable
public static WebSession findWebSession(DataFetchingEnvironment env) {
public static WebSession findWebSession(@NotNull DataFetchingEnvironment env) {
if (env.getGraphQlContext().getBoolean(CloudbeaverCliConstants.CLI_MODE)) {
return getSessionFromContext(env);
}
@@ -156,16 +155,30 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
GraphQLEndpoint.getServletRequestOrThrow(env));
}
public static WebSession findWebSession(DataFetchingEnvironment env, boolean errorOnNotFound) throws DBWebException {
public static WebSession findWebSession(@NotNull DataFetchingEnvironment env, boolean errorOnNotFound) throws DBWebException {
return WebAppUtils.getWebApplication().getSessionManager().findWebSession(
GraphQLEndpoint.getServletRequestOrThrow(env), errorOnNotFound);
}
@NotNull
public static WebConnectionInfo getWebConnection(WebSession session, String projectId, 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);
}
@Nullable
protected static <T> T getArgument(@NotNull DataFetchingEnvironment env, @NotNull String name) {
return env.getArgument(name);
}
@NotNull
protected static <T> T getArgumentVal(@NotNull DataFetchingEnvironment env, @NotNull String name) throws DBWebException {
T value = env.getArgument(name);
if (value == null) {
throw new DBWebException("Argument '" + name + "' is null");
}
return value;
}
private class ServiceInvocationHandler implements InvocationHandler {
private final API_TYPE impl;
private final DataFetchingEnvironment env;
@@ -181,7 +194,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
try {
WebActionSet actionSet = method.getDeclaringClass().getAnnotation(WebActionSet.class);
if (actionSet != null) {
checkServicePermissions(method, actionSet);
checkServicePermissions(actionSet);
}
WebAction webAction = method.getAnnotation(WebAction.class);
if (webAction != null) {
@@ -266,7 +279,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
}
}
private void checkServicePermissions(Method method, WebActionSet actionSet) throws DBWebException {
private void checkServicePermissions(WebActionSet actionSet) throws DBWebException {
String[] features = actionSet.requireFeatures();
ServletApplication servletApplication = ServletAppUtils.getServletApplication();
for (String feature : features) {
@@ -339,9 +352,9 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
Log.setContext(null);
}
protected void setLogContext(Method method, Object[] args) {
protected void setLogContext(Method method, @Nullable Object[] args) {
WebSession activeSession = null;
if (args != null && args.length > 0) {
if (args != null) {
for (Object arg : args) {
if (arg instanceof WebSession) {
activeSession = (WebSession) arg;
@@ -119,7 +119,7 @@ public interface DBWServiceCore extends DBWService {
@NotNull WebSession webSession,
@Nullable String projectId,
@NotNull String connectionId,
@NotNull Map<String, Object> authProperties,
@Nullable Map<String, Object> authProperties,
@Nullable List<WebNetworkHandlerConfigInput> networkCredentials,
boolean saveCredentials,
boolean sharedCredentials,
@@ -181,7 +181,7 @@ public interface DBWServiceCore extends DBWService {
WebConnectionFolderInfo createConnectionFolder(
@NotNull WebSession session,
@Nullable @WebObjectId String projectId,
@NotNull String parentNodePath,
@Nullable String parentNodePath,
@NotNull String newName) throws DBWebException;
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATASOURCES_EDIT})
@@ -54,20 +54,20 @@ public class WebServiceBindingCore extends WebServiceBindingBase<DBWServiceCore>
.dataFetcher("systemInfo", env -> getService(env).getSystemInformationProperties(getWebSession(env)))
.dataFetcher("productSettings", env -> getService(env).getProductSettings(getWebSession(env)))
.dataFetcher("driverList", env -> getService(env).getDriverList(getWebSession(env), env.getArgument("id")))
.dataFetcher("driverList", env -> getService(env).getDriverList(getWebSession(env), getArgument(env, "id")))
.dataFetcher("authModels", env -> getService(env).getAuthModels(getWebSession(env)))
.dataFetcher("networkHandlers", env -> getService(env).getNetworkHandlers(getWebSession(env)))
.dataFetcher("userConnections", env -> getService(env).getUserConnections(
getWebSession(env), getProjectReference(env), env.getArgument("id"), env.getArgument("projectIds")))
getWebSession(env), getProjectReference(env), getArgument(env, "id"), getArgument(env, "projectIds")))
.dataFetcher("connectionFolders", env -> getService(env).getConnectionFolders(
getWebSession(env), getProjectReference(env), env.getArgument("path")))
getWebSession(env), getProjectReference(env), getArgument(env, "path")))
.dataFetcher("sessionPermissions", env -> getService(env).getSessionPermissions(getWebSession(env)))
.dataFetcher("sessionState", env -> getService(env).getSessionState(findWebSession(env, true)))
.dataFetcher("connectionInfo", env -> getService(env).getConnectionState(
getWebSession(env), getProjectReference(env), env.getArgument("id")))
getWebSession(env), getProjectReference(env), getArgument(env, "id")))
.dataFetcher("listProjects", env -> getService(env).getProjects(getWebSession(env)))
.dataFetcher("readSessionLog", env -> {
@@ -78,8 +78,8 @@ public class WebServiceBindingCore extends WebServiceBindingBase<DBWServiceCore>
}
return getService(env).readSessionLog(
session,
env.getArgument("maxEntries"),
env.getArgument("clearEntries"));
getArgument(env, "maxEntries"),
getArgument(env, "clearEntries"));
})
;
@@ -89,7 +89,7 @@ public class WebServiceBindingCore extends WebServiceBindingBase<DBWServiceCore>
HttpServletResponse servletResponse = GraphQLEndpoint.getServletResponse(env);
return getService(env).openSession(
sessionManager.getWebSession(servletRequest, servletResponse, false),
env.getArgument("defaultLocale"),
getArgument(env, "defaultLocale"),
servletRequest,
servletResponse);
})
@@ -100,25 +100,25 @@ public class WebServiceBindingCore extends WebServiceBindingBase<DBWServiceCore>
GraphQLEndpoint.getServletRequestOrThrow(env), GraphQLEndpoint.getServletResponse(env)))
.dataFetcher("refreshSessionConnections", env -> getService(env).refreshSessionConnections(
GraphQLEndpoint.getServletRequestOrThrow(env), GraphQLEndpoint.getServletResponse(env)))
.dataFetcher("changeSessionLanguage", env -> getService(env).changeSessionLanguage(getWebSession(env), env.getArgument("locale")))
.dataFetcher("changeSessionLanguage", env -> getService(env).changeSessionLanguage(getWebSession(env), getArgument(env, "locale")))
.dataFetcher("createConnection", env -> getService(env).createConnection(
getWebSession(env), getProjectReference(env), env.getArgument("config"))
getWebSession(env), getProjectReference(env), getArgumentVal(env, "config"))
)
.dataFetcher("updateConnection", env -> getService(env).updateConnection(
getWebSession(env), getProjectReference(env), env.getArgument("config"))
getWebSession(env), getProjectReference(env), getArgumentVal(env, "config"))
)
.dataFetcher("deleteConnection", env -> getService(env).deleteConnection(
getWebSession(env), getProjectReference(env), env.getArgument("id")))
getWebSession(env), getProjectReference(env), getArgumentVal(env, "id")))
.dataFetcher("copyConnectionFromNode", env -> getService(env).copyConnectionFromNode(
getWebSession(env),
getProjectReference(env),
env.getArgument("nodePath"),
env.getArgument("config")
getArgumentVal(env, "nodePath"),
getArgumentVal(env, "config")
)
)
.dataFetcher("initConnection", env -> {
List<Map<String, Object>> networkCredentials = env.getArgument("networkCredentials");
List<Map<String, Object>> networkCredentials = getArgument(env, "networkCredentials");
List<WebNetworkHandlerConfigInput> nhc = null;
if (networkCredentials != null) {
nhc = networkCredentials.stream().map(WebNetworkHandlerConfigInput::new).collect(Collectors.toList());
@@ -126,52 +126,55 @@ public class WebServiceBindingCore extends WebServiceBindingBase<DBWServiceCore>
return getService(env).initConnection(
getWebSession(env),
getProjectReference(env),
env.getArgument("id"),
env.getArgument("credentials"),
getArgumentVal(env, "id"),
getArgument(env, "credentials"),
nhc,
CommonUtils.toBoolean(env.getArgument("saveCredentials")),
CommonUtils.toBoolean(env.getArgument("sharedCredentials")),
env.getArgument("selectedSecretId")
CommonUtils.toBoolean(getArgument(env, "saveCredentials")),
CommonUtils.toBoolean(getArgument(env, "sharedCredentials")),
getArgument(env, "selectedSecretId")
);
}
)
.dataFetcher("testConnection", env -> getService(env).testConnection(
getWebSession(env), getProjectReference(env), env.getArgument("config")
getWebSession(env), getProjectReference(env), getArgumentVal(env, "config")
))
.dataFetcher("testNetworkHandler", env -> getService(env).testNetworkHandler(
getWebSession(env), new WebNetworkHandlerConfigInput(env.getArgument("config"))
getWebSession(env), new WebNetworkHandlerConfigInput(getArgument(env, "config"))
))
.dataFetcher("closeConnection", env -> getService(env).closeConnection(getWebSession(env), getProjectReference(env), env.getArgument("id")))
.dataFetcher("closeConnection", env -> getService(env).closeConnection(
getWebSession(env),
getProjectReference(env),
getArgumentVal(env, "id")))
.dataFetcher("setConnectionNavigatorSettings", env -> getService(env).setConnectionNavigatorSettings(
getWebSession(env),
getProjectReference(env),
env.getArgument("id"),
WebServiceUtils.parseNavigatorSettings(env.getArgument("settings"))
getArgument(env, "id"),
WebServiceUtils.parseNavigatorSettings(getArgument(env, "settings"))
))
.dataFetcher("asyncTaskInfo", env -> getService(env).getAsyncTaskInfo(
getWebSession(env),
env.getArgument("id"),
env.getArgument("removeOnFinish")))
.dataFetcher("asyncTaskCancel", env -> getService(env).cancelAsyncTask(getWebSession(env), env.getArgument("id")))
getArgument(env, "id"),
getArgument(env, "removeOnFinish")))
.dataFetcher("asyncTaskCancel", env -> getService(env).cancelAsyncTask(getWebSession(env), getArgument(env, "id")))
.dataFetcher("createConnectionFolder", env -> getService(env).createConnectionFolder(
getWebSession(env),
getProjectReference(env),
env.getArgument("parentFolderPath"),
env.getArgument("folderName")
getArgument(env, "parentFolderPath"),
getArgumentVal(env, "folderName")
))
.dataFetcher("renameConnectionFolder", env -> getService(env).renameConnectionFolder(
getWebSession(env),
getProjectReference(env),
env.getArgument("folderPath"),
env.getArgument("newName")
getArgumentVal(env, "folderPath"),
getArgumentVal(env, "newName")
))
.dataFetcher("deleteConnectionFolder", env -> getService(env).deleteConnectionFolder(
getWebSession(env),
getProjectReference(env),
env.getArgument("folderPath")
getArgumentVal(env, "folderPath")
))
;
@@ -20,6 +20,7 @@ package io.cloudbeaver.service.core.impl;
import io.cloudbeaver.*;
import io.cloudbeaver.model.*;
import io.cloudbeaver.model.app.ServletApplication;
import io.cloudbeaver.model.app.ServletSystemInformationCollector;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.registry.WebHandlerRegistry;
import io.cloudbeaver.registry.WebSessionHandlerDescriptor;
@@ -89,10 +90,13 @@ public class WebServiceCore implements DBWServiceCore {
@Override
public WebPropertyInfo[] getSystemInformationProperties(@NotNull WebSession webSession) {
return WebCommonUtils.getObjectProperties(
webSession,
WebAppUtils.getWebApplication().getSystemInformationCollector()
);
ServletSystemInformationCollector<?> collector = WebAppUtils.getWebApplication().getSystemInformationCollector();
try {
collector.collectInternalDatabaseUseInformation();
} catch (DBException e) {
log.error("Error collecting system information", e);
}
return WebCommonUtils.getObjectProperties(webSession, collector);
}
@Override
@@ -295,7 +299,7 @@ public class WebServiceCore implements DBWServiceCore {
@NotNull WebSession webSession,
@Nullable String projectId,
@NotNull String connectionId,
@NotNull Map<String, Object> authProperties,
@Nullable Map<String, Object> authProperties,
@Nullable List<WebNetworkHandlerConfigInput> networkCredentials,
boolean saveCredentials,
boolean sharedCredentials,
@@ -34,15 +34,15 @@ public interface DBWServiceNavigator extends DBWService {
@WebAction
List<WebNavigatorNodeInfo> getNavigatorNodeChildren(
@NotNull WebSession session,
String parentPath,
Integer offset,
Integer limit,
Boolean onlyFolders) throws DBWebException;
@NotNull String parentPath,
@Nullable Integer offset,
@Nullable Integer limit,
@Nullable Boolean onlyFolders) throws DBWebException;
@WebAction
List<WebNavigatorNodeInfo> getNavigatorNodeParents(
@NotNull WebSession session,
String nodePath) throws DBWebException;
@NotNull String nodePath) throws DBWebException;
@WebAction
WebNavigatorNodeInfo getNavigatorNodeInfo(
@@ -38,61 +38,61 @@ public class WebServiceBindingNavigator extends WebServiceBindingBase<DBWService
model.getQueryType()
.dataFetcher("navNodeChildren", env -> getService(env).getNavigatorNodeChildren(
getWebSession(env),
env.getArgument("parentPath"),
env.getArgument("offset"),
env.getArgument("limit"),
env.getArgument("onlyFolders")
getArgumentVal(env, "parentPath"),
getArgument(env, "offset"),
getArgument(env, "limit"),
getArgument(env, "onlyFolders")
))
.dataFetcher("navNodeParents", env -> getService(env).getNavigatorNodeParents(
getWebSession(env),
env.getArgument("nodePath")
getArgumentVal(env, "nodePath")
))
.dataFetcher("navNodeInfo", env -> getService(env).getNavigatorNodeInfo(
getWebSession(env),
env.getArgument("nodePath")
getArgumentVal(env, "nodePath")
))
.dataFetcher("navRefreshNode", env -> getService(env).refreshNavigatorNode(
getWebSession(env),
env.getArgument("nodePath"),
getArgumentVal(env, "nodePath"),
false
) != null)
.dataFetcher("navGetStructContainers", env -> getService(env).getStructContainers(
getProjectReference(env),
getWebConnection(env),
env.getArgument("contextId"),
env.getArgument("catalog")
getArgument(env, "contextId"),
getArgument(env, "catalog")
));
model.getMutationType()
.dataFetcher("navReloadNode", env -> getService(env).refreshNavigatorNode(
getWebSession(env),
env.getArgument("nodePath"),
getArgumentVal(env, "nodePath"),
true
))
.dataFetcher("navSetFolderFilter", env -> getService(env).setNavigatorNodeFilter(
getWebSession(env),
env.getArgument("nodePath"),
env.getArgument("include"),
env.getArgument("exclude")
getArgumentVal(env, "nodePath"),
getArgument(env, "include"),
getArgument(env, "exclude")
))
.dataFetcher("navRenameNode", env -> getService(env).renameNode(
getWebSession(env),
env.getArgument("nodePath"),
env.getArgument("newName")
getArgumentVal(env, "nodePath"),
getArgumentVal(env, "newName")
))
.dataFetcher("navDeleteNodes", env -> getService(env).deleteNodes(
getWebSession(env),
env.getArgument("nodePaths")
getArgumentVal(env, "nodePaths")
))
.dataFetcher("navMoveNodesToFolder", env -> getService(env).moveNodesToFolder(
getWebSession(env),
env.getArgument("nodePaths"),
env.getArgument("folderPath")
getArgumentVal(env, "nodePaths"),
getArgumentVal(env, "folderPath")
));
model.getRuntimeWiring().type(TypeRuntimeWiring.newTypeWiring("DatabaseObjectInfo")
.dataFetcher("properties", env -> {
Map<String, Object> filterProps = env.getArgument("filter");
Map<String, Object> filterProps = getArgument(env, "filter");
WebPropertyFilter filter = filterProps == null ? null : new WebPropertyFilter(filterProps);
return ((WebDatabaseObjectInfo)env.getSource()).filterProperties(filter);
})
@@ -18,6 +18,7 @@ package io.cloudbeaver.service.navigator.impl;
import io.cloudbeaver.BaseWebProjectImpl;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.model.WebCommandContext;
@@ -44,6 +45,7 @@ import org.jkiss.dbeaver.model.exec.DBCExecutionContext;
import org.jkiss.dbeaver.model.exec.DBCExecutionContextDefaults;
import org.jkiss.dbeaver.model.exec.DBExecUtils;
import org.jkiss.dbeaver.model.navigator.*;
import org.jkiss.dbeaver.model.navigator.fs.DBNPathBase;
import org.jkiss.dbeaver.model.navigator.meta.DBXTreeItem;
import org.jkiss.dbeaver.model.rm.RMControllerProvider;
import org.jkiss.dbeaver.model.rm.RMProject;
@@ -157,7 +159,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
@Override
public List<WebNavigatorNodeInfo> getNavigatorNodeParents(
@NotNull WebSession session,
String nodePath
@NotNull String nodePath
) throws DBWebException {
try {
DBRProgressMonitor monitor = session.getProgressMonitor();
@@ -462,12 +464,14 @@ public class WebServiceNavigator implements DBWServiceNavigator {
@NotNull
private String renameConnectionFolder(@NotNull WebSession session, DBNNode node, @NotNull String newName) throws DBException {
WebConnectionFolderUtils.validateConnectionFolder(newName);
List<String> siblings = Arrays.stream(
((DBNLocalFolder) node).getLogicalParent().getChildren(session.getProgressMonitor()))
.filter(n -> n instanceof DBNLocalFolder)
.map(DBNNode::getName).toList();
if (siblings.contains(newName)) {
throw new DBWebException("Name " + newName + " is unavailable or invalid");
DBNNode[] children = ((DBNLocalFolder) node).getLogicalParent().getChildren(session.getProgressMonitor());
if (children != null) {
List<String> siblings = Arrays.stream(children)
.filter(n -> n instanceof DBNLocalFolder)
.map(DBNNode::getName).toList();
if (siblings.contains(newName)) {
throw new DBWebException("Name " + newName + " is unavailable or invalid");
}
}
node.rename(session.getProgressMonitor(), newName);
return node.getName();
@@ -596,6 +600,8 @@ public class WebServiceNavigator implements DBWServiceNavigator {
return SMUtils.hasProjectPermission(session, rmProject, RMProjectPermission.DATA_SOURCES_EDIT);
} else if (node instanceof DBNAbstractResourceManagerNode) {
return SMUtils.hasProjectPermission(session, rmProject, RMProjectPermission.RESOURCE_EDIT);
} else if (node instanceof DBNPathBase) {
return !DBWorkbench.isDistributed() || session.hasPermission(DBWConstants.PERMISSION_FS_RESOURCE_EDIT);
}
return true;
}
@@ -48,10 +48,10 @@ import org.jkiss.dbeaver.model.struct.rdb.DBSSchema;
import org.jkiss.dbeaver.model.websocket.event.WSTransactionalCountEvent;
import org.jkiss.dbeaver.utils.RuntimeUtils;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.StandardConstants;
import java.lang.reflect.InvocationTargetException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
@@ -74,7 +74,7 @@ public class WebSQLContextInfo implements WebSessionProvider {
private final AtomicInteger resultId = new AtomicInteger();
public static final DateTimeFormatter ISO_DATE_FORMAT = DateTimeFormatter.ofPattern(DBConstants.DEFAULT_ISO_TIMESTAMP_FORMAT)
.withZone(ZoneId.of("UTC"));
.withZone(StandardConstants.ZONE_ID_UTC);
public WebSQLContextInfo(
WebSQLProcessor processor, String id, String catalogName, String schemaName, String projectId
@@ -19,6 +19,7 @@ package io.cloudbeaver.service.sql;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.app.ServletApplication;
import io.cloudbeaver.model.session.WebSession;
@@ -33,6 +34,7 @@ import org.eclipse.jetty.ee10.servlet.ServletContextRequest;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import java.io.IOException;
import java.lang.reflect.Type;
@@ -73,6 +75,10 @@ public class WebSQLFileLoaderServlet extends WebServiceServletBase {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Update for users only");
return;
}
if (DBWorkbench.isDistributed() && !session.hasPermission(DBWConstants.PERMISSION_SQL_RESULT_UPDATE)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Permission denied");
return;
}
if (!"POST".equalsIgnoreCase(request.getMethod())) {
return;
@@ -81,12 +81,13 @@ public class WebSQLQueryDataContainer implements DBSDataContainer, DBPContextPro
return queryDataContainer.getDescription();
}
@Nullable
@NotNull
@Override
public DBPDataSource getDataSource() {
return dataSource;
}
@NotNull
@Override
public String[] getSupportedFeatures() {
return queryDataContainer.getSupportedFeatures();
@@ -22,12 +22,10 @@ import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.service.WebServiceServletBase;
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
import org.eclipse.jetty.ee10.servlet.ServletContextRequest;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.utils.CommonUtils;
@@ -37,7 +35,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.UUID;
import java.util.regex.Pattern;
@MultipartConfig
@@ -57,44 +54,41 @@ public class WebSQLResultServlet extends WebServiceServletBase {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setAttribute(ServletContextRequest.MULTIPART_CONFIG_ELEMENT, MULTI_PART_CONFIG);
String fileName = UUID.randomUUID().toString();
for (Part part : request.getParts()) {
part.write(WebSQLDataLOBReceiver.DATA_EXPORT_FOLDER + "/" + fileName);
protected void processServiceRequest(
WebSession session,
HttpServletRequest request,
HttpServletResponse response
) throws DBException, IOException {
if (!request.getMethod().equals("GET")) {
return;
}
response.addHeader("fileName", fileName);
Path dataFile = getDataFile(request);
session.addInfoMessage("Download LOB file ...");
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=\"" + dataFile.getFileName().toString() + "\"");
response.setHeader("Content-Length", String.valueOf(Files.size(dataFile)));
response.setDateHeader("Expires", System.currentTimeMillis() + CBConstants.STATIC_CACHE_SECONDS * 1000);
response.setHeader("Cache-Control", "public, max-age=" + CBConstants.STATIC_CACHE_SECONDS);
try (InputStream is = Files.newInputStream(dataFile)) {
IOUtils.copyStream(is, response.getOutputStream());
}
Files.deleteIfExists(dataFile);
}
@Override
protected void processServiceRequest(WebSession session, HttpServletRequest request, HttpServletResponse response) throws DBException, IOException {
if (request.getMethod().equals("POST")) {
try {
doPost(request, response);
} catch (Exception e) {
throw new DBWebException("Servlet exception ", e);
}
} else {
String valuePath = request.getPathInfo();
if (CommonUtils.isEmpty(valuePath)) {
throw new DBWebException("Result value ID not specified");
}
while (valuePath.startsWith("/")) {
valuePath = valuePath.substring(1);
}
Path dataFile = WebSQLDataLOBReceiver.DATA_EXPORT_FOLDER.resolve(valuePath);
session.addInfoMessage("Download LOB file ...");
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=\"" + dataFile.getFileName().toString() + "\"");
response.setHeader("Content-Length", String.valueOf(Files.size(dataFile)));
response.setDateHeader("Expires", System.currentTimeMillis() + CBConstants.STATIC_CACHE_SECONDS * 1000);
response.setHeader("Cache-Control", "public, max-age=" + CBConstants.STATIC_CACHE_SECONDS);
try (InputStream is = Files.newInputStream(dataFile)) {
IOUtils.copyStream(is, response.getOutputStream());
}
Files.deleteIfExists(dataFile);
@NotNull
private Path getDataFile(HttpServletRequest request) throws DBWebException {
String valuePath = request.getPathInfo();
if (CommonUtils.isEmpty(valuePath)) {
throw new DBWebException("Result value ID not specified");
}
while (valuePath.startsWith("/")) {
valuePath = valuePath.substring(1);
}
Path dataFile = WebSQLDataLOBReceiver.DATA_EXPORT_FOLDER.resolve(valuePath).normalize();
if (!dataFile.startsWith(WebSQLDataLOBReceiver.DATA_EXPORT_FOLDER)) {
throw new DBWebException("Bad result value ID");
}
return dataFile;
}
}
@@ -55,55 +55,55 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL>
.dataFetcher("sqlListContexts", env ->
getService(env).listContexts(getWebSession(env),
getProjectReference(env),
env.getArgument("connectionId"),
env.getArgument("contextId"))
getArgument(env, "connectionId"),
getArgument(env, "contextId"))
)
.dataFetcher("sqlCompletionProposals", env ->
getService(env).getCompletionProposals(
getSQLContext(env),
env.getArgument("query"),
env.getArgument("position"),
env.getArgument("maxResults"),
env.getArgument("simpleMode")
getArgumentVal(env, "query"),
getArgument(env, "position"),
getArgument(env, "maxResults"),
getArgument(env, "simpleMode")
)
)
.dataFetcher("sqlFormatQuery", env ->
getService(env).formatQuery(
getSQLContext(env),
env.getArgument("query")
getArgumentVal(env, "query")
)
)
.dataFetcher("sqlSupportedOperations", env ->
getService(env).getSupportedOperations(
getSQLContext(env),
env.getArgument("resultsId"),
env.getArgument("attributeIndex"))
getArgumentVal(env, "resultsId"),
getArgumentVal(env, "attributeIndex"))
)
.dataFetcher("sqlEntityQueryGenerators", env ->
getService(env).getEntityQueryGenerators(
getWebSession(env),
env.getArgument("nodePathList"))
getArgumentVal(env, "nodePathList"))
)
.dataFetcher("sqlGenerateEntityQuery", env ->
getService(env).generateEntityQuery(
getWebSession(env),
env.getArgument("generatorId"),
env.getArgument("options"),
env.getArgument("nodePathList"))
getArgumentVal(env, "generatorId"),
getArgumentVal(env, "options"),
getArgumentVal(env, "nodePathList"))
).dataFetcher("sqlParseScript", env ->
getService(env).parseSqlScript(getWebConnection(env), env.getArgument("script"))
getService(env).parseSqlScript(getWebConnection(env), getArgumentVal(env, "script"))
).dataFetcher("sqlParseQuery", env ->
getService(env).parseSqlQuery(
getWebConnection(env),
env.getArgument("script"),
env.getArgument("position"))
getArgumentVal(env, "script"),
getArgumentVal(env, "position"))
).dataFetcher("sqlGenerateGroupingQuery", env ->
getService(env).generateGroupByQuery(
getSQLContext(env),
env.getArgument("resultsId"),
env.getArgument("columnNames"),
env.getArgument("functions"),
env.getArgument("showDuplicatesOnly"))
getArgumentVal(env, "resultsId"),
getArgumentVal(env, "columnNames"),
getArgument(env, "functions"),
getArgument(env, "showDuplicatesOnly"))
)
;
@@ -111,14 +111,14 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL>
.dataFetcher("sqlContextCreate", env -> getService(env).createContext(
getSQLProcessor(env),
getProjectReference(env),
env.getArgument("defaultCatalog"),
env.getArgument("defaultSchema")))
getArgument(env, "defaultCatalog"),
getArgument(env, "defaultSchema")))
.dataFetcher("sqlContextDestroy", env -> { getService(env).destroyContext(getSQLContext(env)); return true; } )
.dataFetcher("sqlContextSetDefaults", env -> {
getService(env).setContextDefaults(
getSQLContext(env),
env.getArgument("defaultCatalog"),
env.getArgument("defaultSchema"));
getArgument(env, "defaultCatalog"),
getArgument(env, "defaultSchema"));
return true;
})
@@ -130,36 +130,36 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL>
}
return getService(env).closeResult(
getSQLContext(env),
env.getArgument("resultId"));
getArgumentVal(env, "resultId"));
})
.dataFetcher("readLobValue", env -> // deprecated
getService(env).readLobValue(
getSQLContext(env),
env.getArgument("resultsId"),
env.getArgument("lobColumnIndex"),
getArgumentVal(env, "resultsId"),
getArgumentVal(env, "lobColumnIndex"),
getResultsRow(env, "row").get(0)))
.dataFetcher("sqlReadLobValue", env ->
getService(env).readLobValue(
getSQLContext(env),
env.getArgument("resultsId"),
env.getArgument("lobColumnIndex"),
new WebSQLResultsRow(env.getArgument("row"))))
getArgumentVal(env, "resultsId"),
getArgumentVal(env, "lobColumnIndex"),
new WebSQLResultsRow(getArgument(env, "row"))))
.dataFetcher("sqlReadStringValue", env ->
getService(env).getCellValue(
getSQLContext(env),
env.getArgument("resultsId"),
env.getArgument("columnIndex"),
new WebSQLResultsRow(env.getArgument("row"))))
getArgumentVal(env, "resultsId"),
getArgumentVal(env, "columnIndex"),
new WebSQLResultsRow(getArgument(env, "row"))))
.dataFetcher("sqlGetDynamicTrace", env ->
getService(env).readDynamicTrace(
getWebSession(env),
getSQLContext(env),
env.getArgument("resultsId")
getArgumentVal(env, "resultsId")
))
.dataFetcher("updateResultsDataBatch", env ->
getService(env).updateResultsDataBatch(
getSQLContext(env),
env.getArgument("resultsId"),
getArgumentVal(env, "resultsId"),
getResultsRow(env, "updatedRows"),
getResultsRow(env, "deletedRows"),
getResultsRow(env, "addedRows"),
@@ -168,7 +168,7 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL>
getService(env).asyncUpdateResultsDataBatch(
getWebSession(env),
getSQLContext(env),
env.getArgument("resultsId"),
getArgumentVal(env, "resultsId"),
getResultsRow(env, "updatedRows"),
getResultsRow(env, "deletedRows"),
getResultsRow(env, "addedRows"),
@@ -176,7 +176,7 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL>
.dataFetcher("updateResultsDataBatchScript", env ->
getService(env).updateResultsDataBatchScript(
getSQLContext(env),
env.getArgument("resultsId"),
getArgumentVal(env, "resultsId"),
getResultsRow(env, "updatedRows"),
getResultsRow(env, "deletedRows"),
getResultsRow(env, "addedRows"),
@@ -187,51 +187,51 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL>
getWebSession(env),
getProjectReference(env),
getSQLContext(env),
env.getArgument("sql"),
env.getArgument("resultId"),
getArgumentVal(env, "sql"),
getArgument(env, "resultId"),
getDataFilter(env),
getDataFormat(env),
CommonUtils.toBoolean(env.getArgument("readLogs"))
CommonUtils.toBoolean(getArgument(env, "readLogs"))
)
)
.dataFetcher("asyncReadDataFromContainer", env ->
getService(env).asyncReadDataFromContainer(
getSQLContext(env),
env.getArgument("containerNodePath"),
env.getArgument("resultId"),
getArgumentVal(env, "containerNodePath"),
getArgument(env, "resultId"),
getDataFilter(env),
getDataFormat(env)
))
.dataFetcher("asyncSqlExecuteResults", env ->
getService(env).asyncGetQueryResults(
getWebSession(env), env.getArgument("taskId")
getWebSession(env), getArgumentVal(env, "taskId")
))
.dataFetcher("asyncSqlExplainExecutionPlan", env ->
getService(env).asyncSqlExplainExecutionPlan(
getSQLContext(env),
env.getArgument("query"),
env.getArgument("configuration")
getArgumentVal(env, "query"),
getArgumentVal(env, "configuration")
))
.dataFetcher("asyncSqlExplainExecutionPlanResult", env ->
getService(env).asyncSqlExplainExecutionPlanResult(
getWebSession(env), env.getArgument("taskId")
getWebSession(env), getArgumentVal(env, "taskId")
))
.dataFetcher("asyncSqlRowDataCount", env ->
getService(env).getRowDataCount(
getWebSession(env),
getSQLContext(env),
env.getArgument("resultsId")
getArgumentVal(env, "resultsId")
))
.dataFetcher("asyncSqlRowDataCountResult", env ->
getService(env).getRowDataCountResult(
getWebSession(env),
env.getArgument("taskId")
getArgumentVal(env, "taskId")
))
.dataFetcher("asyncSqlSetAutoCommit", env ->
getService(env).asyncSqlSetAutoCommit(
getWebSession(env),
getSQLContext(env),
env.getArgument("autoCommit")
getArgumentVal(env, "autoCommit")
))
.dataFetcher("asyncSqlCommitTransaction", env ->
getService(env).asyncSqlCommitTransaction(
@@ -252,7 +252,7 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL>
@NotNull
public static WebDataFormat getDataFormat(DataFetchingEnvironment env) {
String dataFormat = env.getArgument("dataFormat");
String dataFormat = getArgument(env, "dataFormat");
return CommonUtils.valueOf(WebDataFormat.class, dataFormat, WebDataFormat.resultset);
}
@@ -298,7 +298,7 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL>
if (processor == null) {
return null;
}
String contextId = env.getArgument("contextId");
String contextId = getArgument(env, "contextId");
return getSQLContext(processor, contextId);
}
@@ -374,12 +374,12 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL>
///////////////////////////////////////
// Helpers
public static WebSQLDataFilter getDataFilter(DataFetchingEnvironment env) {
Map<String, Object> filterProps = env.getArgument("filter");
Map<String, Object> filterProps = getArgument(env, "filter");
return filterProps == null ? null : new WebSQLDataFilter(filterProps);
}
private static List<WebSQLResultsRow> getResultsRow(DataFetchingEnvironment env, String param) {
List<Map<String, Object>> mapList = env.getArgument(param);
List<Map<String, Object>> mapList = getArgument(env, param);
if (CommonUtils.isEmpty(mapList)) {
return null;
}
@@ -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.130.qualifier
Bundle-Release-Date: 20251006
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.130-SNAPSHOT</version>
<version>1.0.132-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -74,39 +74,63 @@ public interface DBWServiceAdmin extends DBWService {
List<String> listTeamRoles();
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
boolean deleteUser(@NotNull WebSession webSession, String userName) throws DBWebException;
boolean deleteUser(@NotNull WebSession webSession, @NotNull String userName) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
@NotNull
AdminTeamInfo createTeam(@NotNull WebSession webSession, String teamId, String teamName, String description) throws DBWebException;
AdminTeamInfo createTeam(
@NotNull WebSession webSession,
@NotNull String teamId,
@NotNull String teamName,
@Nullable String description
) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
@NotNull
AdminTeamInfo updateTeam(@NotNull WebSession webSession, String teamId, String teamName, String description) throws DBWebException;
AdminTeamInfo updateTeam(
@NotNull WebSession webSession,
@NotNull String teamId,
@NotNull String teamName,
@Nullable String description
) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
boolean deleteTeam(@NotNull WebSession webSession, String teamId, boolean force) throws DBWebException;
boolean deleteTeam(@NotNull WebSession webSession, @NotNull String teamId, boolean force) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
boolean grantUserTeam(@NotNull WebSession webSession, String user, String team) throws DBWebException;
boolean grantUserTeam(@NotNull WebSession webSession, @NotNull String user, @NotNull String team) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
boolean revokeUserTeam(@NotNull WebSession webSession, String user, String team) throws DBWebException;
boolean revokeUserTeam(@NotNull WebSession webSession, @NotNull String user, @NotNull String team) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
List<AdminPermissionInfo> setSubjectPermissions(@NotNull WebSession webSession, String subjectID, List<String> permissions) throws DBWebException;
@NotNull
List<AdminPermissionInfo> setSubjectPermissions(
@NotNull WebSession webSession,
@NotNull String subjectID,
@NotNull List<String> permissions
) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
boolean setUserCredentials(@NotNull WebSession webSession, @NotNull String userID, @NotNull String providerId, @NotNull Map<String, Object> credentials) throws DBWebException;
boolean setUserCredentials(
@NotNull WebSession webSession,
@NotNull String userID,
@NotNull String providerId,
@NotNull Map<String, Object> credentials
) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
boolean deleteUserCredentials(@NotNull WebSession webSession, @NotNull String userId, @NotNull String providerId) throws DBWebException;
boolean deleteUserCredentials(
@NotNull WebSession webSession,
@NotNull String userId,
@NotNull String providerId
) throws DBWebException;
////////////////////////////////////////////////////////////////////
// Connection management
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
List<AdminConnectionSearchInfo> searchConnections(WebSession webSession, List<String> hostNames) throws DBWebException;
List<AdminConnectionSearchInfo> searchConnections(WebSession webSession, @NotNull List<String> hostNames) throws DBWebException;
////////////////////////////////////////////////////////////////////
// Features
@@ -129,7 +153,8 @@ public interface DBWServiceAdmin extends DBWService {
@NotNull String providerId,
@NotNull String id,
@NotNull String displayName,
boolean disabled, @Nullable String iconURL,
boolean disabled,
@Nullable String iconURL,
@Nullable String description,
@Nullable Map<String, Object> parameters) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
@@ -139,12 +164,12 @@ public interface DBWServiceAdmin extends DBWService {
// Server configuration
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
boolean configureServer(WebSession webSession, Map<String, Object> params) throws DBWebException;
boolean configureServer(@NotNull WebSession webSession, @NotNull Map<String, Object> params) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
boolean setDefaultNavigatorSettings(WebSession webSession, DBNBrowseSettings settings) throws DBWebException;
boolean setDefaultNavigatorSettings(@NotNull WebSession webSession, @NotNull DBNBrowseSettings settings) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
boolean updateProductConfiguration(WebSession webSession, Map<String, Object> productConfiguration) throws DBWebException;
boolean updateProductConfiguration(@NotNull WebSession webSession, @NotNull Map<String, Object> productConfiguration) throws DBWebException;
////////////////////////////////////////////////////////////////////
// Permissions
@@ -153,7 +178,7 @@ public interface DBWServiceAdmin extends DBWService {
SMDataSourceGrant[] getConnectionSubjectAccess(
@NotNull WebSession webSession,
@Nullable String projectId,
String connectionId) throws DBWebException;
@NotNull String connectionId) throws DBWebException;
@Deprecated
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
@@ -191,26 +216,50 @@ public interface DBWServiceAdmin extends DBWService {
// User meta parameters
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
WebPropertyInfo saveUserMetaParameter(WebSession webSession, String id, String displayName, String description, Boolean required) throws
DBWebException;
WebPropertyInfo saveUserMetaParameter(
@NotNull WebSession webSession,
@NotNull String id,
@NotNull String displayName,
@Nullable String description,
@Nullable Boolean required
) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
Boolean deleteUserMetaParameter(WebSession webSession, String id) throws DBWebException;
Boolean deleteUserMetaParameter(@NotNull WebSession webSession, @NotNull String id) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
Boolean setUserMetaParameterValues(WebSession webSession, String userId, Map<String, String> parameters) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
Boolean setTeamMetaParameterValues(WebSession webSession, String teamId, Map<String, String> parameters) throws DBWebException;
Boolean setUserMetaParameterValues(
@NotNull WebSession webSession,
@NotNull String userId,
@NotNull Map<String, String> parameters
) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
Boolean enableUser(WebSession webSession, String userId, Boolean enabled) throws DBWebException;
Boolean setTeamMetaParameterValues(
@NotNull WebSession webSession,
@NotNull String teamId,
@NotNull Map<String, String> parameters
) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
Boolean setUserAuthRole(WebSession webSession, String userId, String authRole) throws DBWebException;
Boolean enableUser(
@NotNull WebSession webSession,
@NotNull String userId,
@NotNull Boolean enabled
) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
Boolean setUserAuthRole(
@NotNull WebSession webSession,
@NotNull String userId,
@NotNull String authRole
) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
Boolean setUserTeamRole(
@NotNull WebSession webSession, @NotNull String userId,
@NotNull String teamId, @Nullable String teamRole
@NotNull WebSession webSession,
@NotNull String userId,
@NotNull String teamId,
@Nullable String teamRole
) throws DBWebException;
}
@@ -16,7 +16,6 @@
*/
package io.cloudbeaver.service.admin;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
@@ -33,7 +32,7 @@ import org.jkiss.utils.CommonUtils;
* Web service implementation
*/
public class WebServiceBindingAdmin extends WebServiceBindingBase<DBWServiceAdmin>
implements DBWServiceBindingServlet<CBApplication> {
implements DBWServiceBindingServlet<CBApplication<?>> {
private static final String SCHEMA_FILE_NAME = "schema/service.admin.graphqls";
@@ -42,21 +41,21 @@ public class WebServiceBindingAdmin extends WebServiceBindingBase<DBWServiceAdmi
}
@Override
public void bindWiring(DBWBindingContext model) throws DBWebException {
public void bindWiring(DBWBindingContext model) {
model.getQueryType()
.dataFetcher(
"adminUserInfo",
env -> getService(env).getUserById(getWebSession(env), env.getArgument("userId"))
env -> getService(env).getUserById(getWebSession(env), getArgumentVal(env, "userId"))
)
.dataFetcher(
"listUsers",
env -> getService(env).listUsers(
getWebSession(env),
new AdminUserInfoFilter(env.getArgument("filter"), env.getArgument("page"))
new AdminUserInfoFilter(getArgumentVal(env, "filter"), getArgumentVal(env, "page"))
)
)
.dataFetcher("listTeams",
env -> getService(env).listTeams(getWebSession(env), env.getArgument("teamId")))
env -> getService(env).listTeams(getWebSession(env), getArgument(env, "teamId")))
.dataFetcher("listPermissions",
env -> getService(env).listPermissions(getWebSession(env)))
.dataFetcher("listAuthRoles",
@@ -68,141 +67,141 @@ public class WebServiceBindingAdmin extends WebServiceBindingBase<DBWServiceAdmi
.dataFetcher("createUser",
env -> getService(env).createUser(
getWebSession(env),
env.getArgument("userId"),
env.getArgument("enabled"),
env.getArgument("authRole")
getArgument(env, "userId"),
getArgumentVal(env, "enabled"),
getArgument(env, "authRole")
))
.dataFetcher("deleteUser",
env -> getService(env).deleteUser(getWebSession(env), env.getArgument("userId")))
env -> getService(env).deleteUser(getWebSession(env), getArgumentVal(env, "userId")))
.dataFetcher("createTeam",
env -> getService(env).createTeam(
getWebSession(env),
env.getArgument("teamId"),
env.getArgument("teamName"),
env.getArgument("description")))
getArgumentVal(env, "teamId"),
getArgumentVal(env, "teamName"),
getArgument(env, "description")))
.dataFetcher("updateTeam",
env -> getService(env).updateTeam(
getWebSession(env),
env.getArgument("teamId"),
env.getArgument("teamName"),
env.getArgument("description")))
getArgumentVal(env, "teamId"),
getArgumentVal(env, "teamName"),
getArgument(env, "description")))
.dataFetcher("deleteTeam",
env -> getService(env).deleteTeam(
getWebSession(env),
env.getArgument("teamId"),
CommonUtils.toBoolean(env.getArgument("force"))))
getArgumentVal(env, "teamId"),
CommonUtils.toBoolean(getArgument(env, "force"))))
.dataFetcher("grantUserTeam",
env -> getService(env).grantUserTeam(getWebSession(env), env.getArgument("userId"), env.getArgument("teamId")))
env -> getService(env).grantUserTeam(getWebSession(env), getArgumentVal(env, "userId"), getArgumentVal(env, "teamId")))
.dataFetcher("revokeUserTeam",
env -> getService(env).revokeUserTeam(getWebSession(env), env.getArgument("userId"), env.getArgument("teamId")))
env -> getService(env).revokeUserTeam(getWebSession(env), getArgumentVal(env, "userId"), getArgumentVal(env, "teamId")))
.dataFetcher("setSubjectPermissions",
env -> getService(env).setSubjectPermissions(getWebSession(env), env.getArgument("subjectId"), env.getArgument("permissions")))
env -> getService(env).setSubjectPermissions(getWebSession(env), getArgumentVal(env, "subjectId"), getArgumentVal(env, "permissions")))
.dataFetcher("setUserCredentials",
env -> getService(env).setUserCredentials(getWebSession(env),
env.getArgument("userId"),
env.getArgument("providerId"),
env.getArgument("credentials")))
getArgumentVal(env, "userId"),
getArgumentVal(env, "providerId"),
getArgumentVal(env, "credentials")))
.dataFetcher("deleteUserCredentials",
env -> getService(env).deleteUserCredentials(getWebSession(env), env.getArgument("userId"), env.getArgument("providerId")))
env -> getService(env).deleteUserCredentials(getWebSession(env), getArgumentVal(env, "userId"), getArgumentVal(env, "providerId")))
.dataFetcher("enableUser",
env -> getService(env).enableUser(getWebSession(env), env.getArgument("userId"), env.getArgument("enabled")))
env -> getService(env).enableUser(getWebSession(env), getArgumentVal(env, "userId"), getArgumentVal(env, "enabled")))
.dataFetcher("setUserAuthRole",
env -> getService(env).setUserAuthRole(getWebSession(env), env.getArgument("userId"), env.getArgument("authRole")))
env -> getService(env).setUserAuthRole(getWebSession(env), getArgumentVal(env, "userId"), getArgumentVal(env, "authRole")))
.dataFetcher("setUserTeamRole",
env -> getService(env).setUserTeamRole(
getWebSession(env),
env.getArgument("userId"),
env.getArgument("teamId"),
env.getArgument("teamRole")
getArgumentVal(env, "userId"),
getArgumentVal(env, "teamId"),
getArgument(env, "teamRole")
)
)
.dataFetcher("searchConnections", env -> getService(env).searchConnections(getWebSession(env), env.getArgument("hostNames")))
.dataFetcher("searchConnections", env -> getService(env).searchConnections(getWebSession(env), getArgumentVal(env, "hostNames")))
.dataFetcher("getConnectionSubjectAccess",
env -> getService(env).getConnectionSubjectAccess(
getWebSession(env),
getProjectReference(env),
env.getArgument("connectionId")))
getArgumentVal(env, "connectionId")))
.dataFetcher("setConnectionSubjectAccess",
env -> getService(env).setConnectionSubjectAccess(
getWebSession(env),
getProjectReference(env),
env.getArgument("connectionId"),
env.getArgument("subjects")))
getArgumentVal(env, "connectionId"),
getArgumentVal(env, "subjects")))
.dataFetcher("addConnectionsAccess",
env -> getService(env).addConnectionsAccess(
getWebSession(env),
getProjectReference(env),
env.getArgument("connectionIds"),
env.getArgument("subjects")))
getArgumentVal(env, "connectionIds"),
getArgumentVal(env, "subjects")))
.dataFetcher("deleteConnectionsAccess",
env -> getService(env).deleteConnectionsAccess(
getWebSession(env),
getProjectReference(env),
env.getArgument("connectionIds"),
env.getArgument("subjects")))
getArgumentVal(env, "connectionIds"),
getArgumentVal(env, "subjects")))
.dataFetcher("getSubjectConnectionAccess",
env -> getService(env).getSubjectConnectionAccess(getWebSession(env), env.getArgument("subjectId")))
env -> getService(env).getSubjectConnectionAccess(getWebSession(env), getArgumentVal(env, "subjectId")))
.dataFetcher("setSubjectConnectionAccess",
env -> getService(env).setSubjectConnectionAccess(getWebSession(env), env.getArgument("subjectId"), env.getArgument("connections")))
env -> getService(env).setSubjectConnectionAccess(getWebSession(env), getArgumentVal(env, "subjectId"), getArgumentVal(env, "connections")))
.dataFetcher("listFeatureSets",
env -> getService(env).listFeatureSets(getWebSession(env)))
.dataFetcher("listAuthProviderConfigurationParameters",
env -> getService(env).listAuthProviderConfigurationParameters(getWebSession(env), env.getArgument("providerId")))
env -> getService(env).listAuthProviderConfigurationParameters(getWebSession(env), getArgumentVal(env, "providerId")))
.dataFetcher("listAuthProviderConfigurations",
env -> getService(env).listAuthProviderConfigurations(
GraphQLEndpoint.getServletRequestOrThrow(env),
getWebSession(env), env.getArgument("providerId"))
getWebSession(env), getArgument(env, "providerId"))
)
.dataFetcher("saveAuthProviderConfiguration",
env -> getService(env).saveAuthProviderConfiguration(
GraphQLEndpoint.getServletRequestOrThrow(env),
getWebSession(env),
env.getArgument("providerId"),
env.getArgument("id"),
env.getArgument("displayName"),
CommonUtils.toBoolean((Boolean)env.getArgument("disabled")),
env.getArgument("iconURL"),
env.getArgument("description"), env.getArgument("parameters")
getArgumentVal(env, "providerId"),
getArgumentVal(env, "id"),
getArgumentVal(env, "displayName"),
CommonUtils.toBoolean(getArgument(env, "disabled")),
getArgument(env, "iconURL"),
getArgument(env, "description"), getArgument(env, "parameters")
))
.dataFetcher("deleteAuthProviderConfiguration",
env -> getService(env).deleteAuthProviderConfiguration(getWebSession(env), env.getArgument("id")))
env -> getService(env).deleteAuthProviderConfiguration(getWebSession(env), getArgumentVal(env, "id")))
.dataFetcher("saveUserMetaParameter",
env -> getService(env).saveUserMetaParameter(
getWebSession(env),
env.getArgument("id"),
env.getArgument("displayName"),
env.getArgument("description"),
env.getArgument("required")))
getArgumentVal(env, "id"),
getArgumentVal(env, "displayName"),
getArgument(env, "description"),
getArgument(env, "required")))
.dataFetcher("deleteUserMetaParameter",
env -> getService(env).deleteUserMetaParameter(
getWebSession(env),
env.getArgument("id")))
getArgumentVal(env, "id")))
.dataFetcher("setUserMetaParameterValues",
env -> getService(env).setUserMetaParameterValues(
getWebSession(env),
env.getArgument("userId"),
env.getArgument("parameters")))
getArgumentVal(env, "userId"),
getArgumentVal(env, "parameters")))
.dataFetcher("setTeamMetaParameterValues",
env -> getService(env).setTeamMetaParameterValues(
getWebSession(env),
env.getArgument("teamId"),
env.getArgument("parameters")))
getArgumentVal(env, "teamId"),
getArgumentVal(env, "parameters")))
.dataFetcher("configureServer",
env -> getService(env).configureServer(getWebSession(env), env.getArgument("configuration")))
env -> getService(env).configureServer(getWebSession(env), getArgumentVal(env, "configuration")))
.dataFetcher("setDefaultNavigatorSettings",
env -> getService(env).setDefaultNavigatorSettings(getWebSession(env), WebServiceUtils.parseNavigatorSettings(env.getArgument("settings"))))
env -> getService(env).setDefaultNavigatorSettings(getWebSession(env), WebServiceUtils.parseNavigatorSettings(getArgument(env, "settings"))))
;
model.getMutationType()
.dataFetcher("adminUpdateProductConfiguration",
env -> getService(env).updateProductConfiguration(getWebSession(env), env.getArgument("configuration")));
env -> getService(env).updateProductConfiguration(getWebSession(env), getArgumentVal(env, "configuration")));
}
@Override
@@ -193,7 +193,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
}
@Override
public boolean deleteUser(@NotNull WebSession webSession, String userName) throws DBWebException {
public boolean deleteUser(@NotNull WebSession webSession, @NotNull String userName) throws DBWebException {
if (CommonUtils.equalObjects(userName, webSession.getUser().getUserId())) {
throw new DBWebException("You cannot delete yourself");
}
@@ -253,7 +253,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
@NotNull
@Override
public AdminTeamInfo updateTeam(@NotNull WebSession webSession, String teamId, String teamName, String description) throws DBWebException {
public AdminTeamInfo updateTeam(@NotNull WebSession webSession, @NotNull String teamId, @NotNull String teamName, @Nullable String description) throws DBWebException {
if (teamId.isEmpty()) {
throw new DBWebException("Empty team ID");
}
@@ -270,7 +270,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
}
@Override
public boolean deleteTeam(@NotNull WebSession webSession, String teamId, boolean force) throws DBWebException {
public boolean deleteTeam(@NotNull WebSession webSession, @NotNull String teamId, boolean force) throws DBWebException {
try {
webSession.addInfoMessage("Delete team - " + teamId);
@@ -291,7 +291,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
}
@Override
public boolean grantUserTeam(@NotNull WebSession webSession, String user, String team) throws DBWebException {
public boolean grantUserTeam(@NotNull WebSession webSession, @NotNull String user, @NotNull String team) throws DBWebException {
WebUser grantor = webSession.getUser();
if (grantor == null) {
throw new DBWebException("Cannot grant team in anonymous mode");
@@ -312,7 +312,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
}
@Override
public boolean revokeUserTeam(@NotNull WebSession webSession, String user, String team) throws DBWebException {
public boolean revokeUserTeam(@NotNull WebSession webSession, @NotNull String user, @NotNull String team) throws DBWebException {
WebUser grantor = webSession.getUser();
if (grantor == null) {
throw new DBWebException("Cannot revoke team in anonymous mode");
@@ -337,8 +337,9 @@ public class WebServiceAdmin implements DBWServiceAdmin {
}
}
@NotNull
@Override
public List<AdminPermissionInfo> setSubjectPermissions(@NotNull WebSession webSession, String subjectID, List<String> permissions) throws DBWebException {
public List<AdminPermissionInfo> setSubjectPermissions(@NotNull WebSession webSession, @NotNull String subjectID, @NotNull List<String> permissions) throws DBWebException {
validatePermissions(SMConstants.SUBJECT_PERMISSION_SCOPE, permissions);
WebUser grantor = webSession.getUser();
if (grantor == null) {
@@ -415,7 +416,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
}
@Override
public Boolean setUserAuthRole(WebSession webSession, String userId, String authRole) throws DBWebException {
public Boolean setUserAuthRole(@NotNull WebSession webSession, @NotNull String userId, @NotNull String authRole) throws DBWebException {
try {
log.info(String.format("User set auth role: [grantorUserId=%s]", webSession.getUserId()));
webSession.getAdminSecurityController().setUserAuthRole(userId, authRole);
@@ -569,7 +570,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
@Override
public boolean configureServer(WebSession webSession, Map<String, Object> params) throws DBWebException {
public boolean configureServer(@NotNull WebSession webSession, @NotNull Map<String, Object> params) throws DBWebException {
try {
CBAppConfig appConfig = new CBAppConfig(CBApplication.getInstance().getAppConfiguration());
CBServerConfig serverConfig = new CBServerConfig();
@@ -715,7 +716,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
}
@Override
public boolean setDefaultNavigatorSettings(WebSession webSession, DBNBrowseSettings settings) throws DBWebException {
public boolean setDefaultNavigatorSettings(@NotNull WebSession webSession, @NotNull DBNBrowseSettings settings) throws DBWebException {
CBApplication.getInstance().getAppConfiguration().setDefaultNavigatorSettings(settings);
if (CBApplication.getInstance().isConfigurationMode()) {
return true;
@@ -730,7 +731,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
@Override
public boolean updateProductConfiguration(WebSession webSession, Map<String, Object> productConfiguration) throws DBWebException {
public boolean updateProductConfiguration(@NotNull WebSession webSession, @NotNull Map<String, Object> productConfiguration) throws DBWebException {
try {
CBApplication.getInstance().saveProductConfiguration(webSession, productConfiguration);
return true;
@@ -746,7 +747,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
public SMDataSourceGrant[] getConnectionSubjectAccess(
@NotNull WebSession webSession,
@Nullable String projectId,
String connectionId
@NotNull String connectionId
) throws DBWebException {
DBPProject globalProject = webSession.getProjectById(projectId);
if (!WebServiceUtils.isGlobalProject(globalProject)) {
@@ -903,17 +904,17 @@ public class WebServiceAdmin implements DBWServiceAdmin {
}
@Override
public WebPropertyInfo saveUserMetaParameter(WebSession webSession, String id, String displayName, String description, Boolean required) throws DBWebException {
public WebPropertyInfo saveUserMetaParameter(@NotNull WebSession webSession, @NotNull String id, @NotNull String displayName, String description, Boolean required) throws DBWebException {
throw new DBWebException("Not implemented");
}
@Override
public Boolean deleteUserMetaParameter(WebSession webSession, String id) throws DBWebException {
public Boolean deleteUserMetaParameter(@NotNull WebSession webSession, @NotNull String id) throws DBWebException {
throw new DBWebException("Not implemented");
}
@Override
public Boolean setUserMetaParameterValues(WebSession webSession, String userId, Map<String, String> parameters) throws DBWebException {
public Boolean setUserMetaParameterValues(@NotNull WebSession webSession, @NotNull String userId, @NotNull Map<String, String> parameters) throws DBWebException {
try {
webSession.getAdminSecurityController().setSubjectMetas(userId, parameters);
return true;
@@ -923,7 +924,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
}
@Override
public Boolean setTeamMetaParameterValues(WebSession webSession, String teamId, Map<String, String> parameters) throws DBWebException {
public Boolean setTeamMetaParameterValues(@NotNull WebSession webSession, @NotNull String teamId, @NotNull Map<String, String> parameters) throws DBWebException {
try {
webSession.getAdminSecurityController().setSubjectMetas(teamId, parameters);
return true;
@@ -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.130.qualifier
Bundle-Release-Date: 20251006
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.130-SNAPSHOT</version>
<version>1.0.132-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -16,7 +16,6 @@
*/
package io.cloudbeaver.service.auth;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
import io.cloudbeaver.service.DBWBindingContext;
import io.cloudbeaver.service.WebServiceBindingBase;
@@ -35,45 +34,45 @@ public class WebServiceBindingAuth extends WebServiceBindingBase<DBWServiceAuth>
}
@Override
public void bindWiring(DBWBindingContext model) throws DBWebException {
public void bindWiring(DBWBindingContext model) {
model.getQueryType()
.dataFetcher("authLogin", env -> getService(env).authLogin(
getWebSession(env, false),
env.getArgument("provider"),
env.getArgument("configuration"),
env.getArgument("credentials"),
CommonUtils.toBoolean(env.getArgument("linkUser")),
CommonUtils.toBoolean(env.getArgument("forceSessionsLogout"))
getArgumentVal(env, "provider"),
getArgument(env, "configuration"),
getArgument(env, "credentials"),
CommonUtils.toBoolean(getArgument(env, "linkUser")),
CommonUtils.toBoolean(getArgument(env, "forceSessionsLogout"))
))
.dataFetcher("federatedAuthTaskResult", env -> getService(env).federatedAuthTaskResult(
getWebSession(env, false),
env.getArgument("taskId")
getArgumentVal(env, "taskId")
))
.dataFetcher("authLogoutExtended", env -> getService(env).authLogout(
GraphQLEndpoint.getServletRequestOrThrow(env),
getWebSession(env, false),
env.getArgument("provider"),
env.getArgument("configuration")
getArgument(env, "provider"),
getArgument(env, "configuration")
))
.dataFetcher("authLogout", env -> {
getService(env).authLogout(
GraphQLEndpoint.getServletRequestOrThrow(env),
getWebSession(env, false),
env.getArgument("provider"),
env.getArgument("configuration"));
getArgument(env, "provider"),
getArgument(env, "configuration"));
return true;
})
.dataFetcher("authUpdateStatus", env -> getService(env).authUpdateStatus(
getWebSession(env, false),
env.getArgument("authId"),
CommonUtils.toBoolean(env.getArgument("linkUser"))
getArgumentVal(env, "authId"),
CommonUtils.toBoolean(getArgument(env, "linkUser"))
))
.dataFetcher("activeUser", env -> getService(env).activeUser(getWebSession(env, false)))
.dataFetcher("authProviders", env -> getService(env).getAuthProviders(GraphQLEndpoint.getServletRequestOrThrow(env)))
.dataFetcher("authChangeLocalPassword", env -> getService(env).changeLocalPassword(
getWebSession(env),
env.getArgument("oldPassword"),
env.getArgument("newPassword")
getArgumentVal(env, "oldPassword"),
getArgumentVal(env, "newPassword")
))
.dataFetcher("listUserProfileProperties",
env -> getService(env).listUserProfileProperties(getWebSession(env)))
@@ -81,18 +80,18 @@ public class WebServiceBindingAuth extends WebServiceBindingBase<DBWServiceAuth>
model.getMutationType()
.dataFetcher("setUserConfigurationParameter",
env -> getService(env).setUserConfigurationParameter(getWebSession(env),
env.getArgument("name"),
env.getArgument("value")))
getArgumentVal(env, "name"),
getArgument(env, "value")))
.dataFetcher("setUserPreferences",
env -> getService(env).setUserConfigurationParameters(getWebSession(env),
env.getArgument("preferences")))
getArgumentVal(env, "preferences")))
.dataFetcher("federatedLogin", env -> getService(env).federatedLogin(
GraphQLEndpoint.getServletRequestOrThrow(env),
getWebSession(env, false),
env.getArgument("provider"),
env.getArgument("configuration"),
CommonUtils.toBoolean(env.getArgument("linkUser")),
CommonUtils.toBoolean(env.getArgument("forceSessionsLogout"))
getArgumentVal(env, "provider"),
getArgument(env, "configuration"),
CommonUtils.toBoolean(getArgument(env, "linkUser")),
CommonUtils.toBoolean(getArgument(env, "forceSessionsLogout"))
))
;
}
@@ -224,10 +224,8 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
throw new DBWebException("Not logged in");
}
try {
List<WebAuthInfo> removedInfos = webSession.removeAuthInfo(providerId, false);
List<WebAuthInfo> removedInfos = webSession.removeAuthInfo(providerId);
var cbApp = CBApplication.getInstance();
cbApp.getSessionManager().closeSession(webSession.getSessionId(), false);
webSession.resetUserState();
List<String> logoutUrls = new ArrayList<>();
String origin = ServletAppUtils.getOriginFromRequest(httpRequest);
@@ -268,11 +266,7 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
public WebUserInfo activeUser(@NotNull WebSession webSession) throws DBWebException {
if (webSession.getUser() == null) {
ServletApplication application = webSession.getApplication();
if (application.getAppConfiguration().isAnonymousAccessEnabled() && webSession.isAuthorizedInSecurityManager()) {
SMUser anonymous = new SMUser("anonymous", true, null);
return new WebUserInfo(webSession, new WebUser(anonymous));
}
if (!application.isAnonymousAccessEnabled()) {
if (!application.getAppConfiguration().isAnonymousAccessEnabled() || !webSession.isAuthorizedInSecurityManager()) {
return null;
}
SMUser anonymous = new SMUser("anonymous", true, null);
@@ -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.131.qualifier
Bundle-Release-Date: 20251006
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.131-SNAPSHOT</version>
<version>1.0.133-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -47,17 +47,17 @@ public class WebServiceBindingDataTransfer extends WebServiceBindingBase<DBWServ
env -> getService(env).getAvailableImportStreamProcessors(getWebSession(env)))
.dataFetcher("dataTransferExportDataFromContainer", env -> getService(env).dataTransferExportDataFromContainer(
WebServiceBindingSQL.getSQLProcessor(env),
env.getArgument("containerNodePath"),
new WebDataTransferParameters(env.getArgument("parameters"))
getArgument(env, "containerNodePath"),
new WebDataTransferParameters(getArgument(env, "parameters"))
))
.dataFetcher("dataTransferExportDataFromResults", env -> getService(env).dataTransferExportDataFromResults(
WebServiceBindingSQL.getSQLContext(env),
env.getArgument("resultsId"),
new WebDataTransferParameters(env.getArgument("parameters"))
getArgument(env, "resultsId"),
new WebDataTransferParameters(getArgument(env, "parameters"))
))
.dataFetcher("dataTransferRemoveDataFile", env -> getService(env).dataTransferRemoveDataFile(
getWebSession(env),
env.getArgument("dataFileId")
getArgument(env, "dataFileId")
))
.dataFetcher("dataTransferDefaultExportSettings", env -> getService(env).defaultExportSettings())
;
@@ -39,6 +39,7 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import java.io.IOException;
import java.io.InputStream;
@@ -72,6 +73,10 @@ public class WebDataTransferImportServlet extends WebServiceServletBase {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Import for users only");
return;
}
if (DBWorkbench.isDistributed() && !session.hasPermission(DBWConstants.PERMISSION_SQL_RESULT_UPDATE)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Permission denied");
return;
}
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.48.qualifier
Bundle-Release-Date: 20251006
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.48-SNAPSHOT</version>
<version>1.0.50-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -30,7 +30,7 @@ import org.jkiss.utils.CommonUtils;
/**
* Web service implementation
*/
public class WebServiceBindingFS extends WebServiceBindingBase<DBWServiceFS> implements DBWServiceBindingServlet<CBApplication> {
public class WebServiceBindingFS extends WebServiceBindingBase<DBWServiceFS> implements DBWServiceBindingServlet<CBApplication<?>> {
private static final String SCHEMA_FILE_NAME = "schema/service.fs.graphqls";
@@ -42,82 +42,82 @@ public class WebServiceBindingFS extends WebServiceBindingBase<DBWServiceFS> imp
public void bindWiring(DBWBindingContext model) throws DBWebException {
model.getQueryType()
.dataFetcher("fsListFileSystems",
env -> getService(env).getAvailableFileSystems(getWebSession(env), env.getArgument("projectId")))
env -> getService(env).getAvailableFileSystems(getWebSession(env), getArgumentVal(env, "projectId")))
.dataFetcher("fsFileSystem",
env -> getService(env).getFileSystem(
getWebSession(env),
env.getArgument("projectId"),
env.getArgument("nodePath")
getArgumentVal(env, "projectId"),
getArgumentVal(env, "nodePath")
)
)
.dataFetcher("fsFile",
env -> getService(env).getFile(getWebSession(env),
env.getArgument("nodePath")
getArgumentVal(env, "nodePath")
)
)
.dataFetcher("fsListFiles",
env -> getService(env).getFiles(getWebSession(env),
env.getArgument("folderPath")
getArgumentVal(env, "folderPath")
)
)
.dataFetcher("fsReadFileContentAsString",
env -> getService(env).readFileContent(getWebSession(env),
env.getArgument("nodePath")
getArgumentVal(env, "nodePath")
)
)
;
model.getMutationType()
.dataFetcher("fsCreateFile",
env -> getService(env).createFile(getWebSession(env),
env.getArgument("parentPath"),
env.getArgument("fileName")
getArgumentVal(env, "parentPath"),
getArgumentVal(env, "fileName")
)
)
.dataFetcher("fsCreateFolder",
env -> getService(env).createFolder(getWebSession(env),
env.getArgument("parentPath"),
env.getArgument("folderName")
getArgumentVal(env, "parentPath"),
getArgumentVal(env, "folderName")
)
)
.dataFetcher("fsDelete",
env -> getService(env).deleteFile(getWebSession(env),
env.getArgument("nodePath")
getArgumentVal(env, "nodePath")
)
)
.dataFetcher("fsMove",
env -> getService(env).moveFile(
getWebSession(env),
env.getArgument("nodePath"),
env.getArgument("toParentNodePath")
getArgumentVal(env, "nodePath"),
getArgumentVal(env, "toParentNodePath")
)
)
.dataFetcher("fsRename",
env -> getService(env).renameFile(
getWebSession(env),
env.getArgument("nodePath"),
env.getArgument("newName")
getArgumentVal(env, "nodePath"),
getArgumentVal(env, "newName")
)
)
.dataFetcher("fsCopy",
env -> getService(env).copyFile(
getWebSession(env),
env.getArgument("nodePath"),
env.getArgument("toParentNodePath")
getArgumentVal(env, "nodePath"),
getArgumentVal(env, "toParentNodePath")
)
)
.dataFetcher("fsWriteFileStringContent",
env -> getService(env).writeFileContent(
getWebSession(env),
env.getArgument("nodePath"),
env.getArgument("data"),
CommonUtils.toBoolean(env.getArgument("forceOverwrite"))
getArgumentVal(env, "nodePath"),
getArgumentVal(env, "data"),
CommonUtils.toBoolean(getArgument(env, "forceOverwrite"))
)
)
;
}
@Override
public void addServlets(CBApplication application, DBWServletContext servletContext) throws DBException {
public void addServlets(CBApplication<?> application, DBWServletContext servletContext) throws DBException {
if (!application.isMultiuser()) {
return;
}
@@ -16,6 +16,7 @@
*/
package io.cloudbeaver.service.fs.impl;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.fs.FSUtils;
import io.cloudbeaver.model.session.WebSession;
@@ -30,6 +31,7 @@ import org.jkiss.dbeaver.model.navigator.fs.DBNFileSystem;
import org.jkiss.dbeaver.model.navigator.fs.DBNFileSystems;
import org.jkiss.dbeaver.model.navigator.fs.DBNPathBase;
import org.jkiss.dbeaver.registry.fs.FileSystemProviderRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -61,10 +63,14 @@ public class WebServiceFS implements DBWServiceFS {
}
DBNFileSystems dbnFileSystems = projectNode.getExtraNode(DBNFileSystems.class);
var fsRegistry = FileSystemProviderRegistry.getInstance();
return Arrays.stream(dbnFileSystems.getChildren(webSession.getProgressMonitor()))
DBNFileSystem[] children = dbnFileSystems.getChildren(webSession.getProgressMonitor());
if (children == null) {
return new FSFileSystem[0];
}
return Arrays.stream(children)
.map(fs -> new FSFileSystem(
FSUtils.makeUniqueFsId(fs.getFileSystem()),
fs.getNodeUri(),
fs.getNodeUri(),
fsRegistry.getProvider(fs.getFileSystem().getProviderId()).getRequiredAuth()
)
)
@@ -116,6 +122,9 @@ public class WebServiceFS implements DBWServiceFS {
try {
DBNPathBase folderPath = FSUtils.getNodeByPath(webSession, parentPath);
var children = folderPath.getChildren(webSession.getProgressMonitor());
if (children == null) {
return new FSFile[0];
}
return Arrays.stream(children)
.filter(c -> c instanceof DBNPathBase)
.map(c -> (DBNPathBase) c)
@@ -147,6 +156,7 @@ public class WebServiceFS implements DBWServiceFS {
boolean forceOverwrite
)
throws DBWebException {
validateEditPermissions(webSession);
try {
DBNPathBase node = FSUtils.getNodeByPath(webSession, nodePath);
Path filePath = node.getPath();
@@ -167,6 +177,7 @@ public class WebServiceFS implements DBWServiceFS {
@NotNull String parentPath,
@NotNull String fileName
) throws DBWebException {
validateEditPermissions(webSession);
try {
DBNPathBase parentNode = FSUtils.getNodeByPath(webSession, parentPath);
if (!Files.isDirectory(parentNode.getPath())) {
@@ -187,6 +198,7 @@ public class WebServiceFS implements DBWServiceFS {
@NotNull String oldNodePath,
@NotNull String parentNodePath
) throws DBWebException {
validateEditPermissions(webSession);
try {
DBNPathBase oldNode = FSUtils.getNodeByPath(webSession, oldNodePath);
DBNPathBase oldParentNode = (DBNPathBase) oldNode.getParentNode();
@@ -212,6 +224,7 @@ public class WebServiceFS implements DBWServiceFS {
@NotNull String nodePath,
@NotNull String newName
) throws DBWebException {
validateEditPermissions(webSession);
validateFilename(newName);
try {
DBNPathBase node = FSUtils.getNodeByPath(webSession, nodePath);
@@ -228,6 +241,7 @@ public class WebServiceFS implements DBWServiceFS {
@NotNull String oldNodePath,
@NotNull String parentNodePath
) throws DBWebException {
validateEditPermissions(webSession);
try {
DBNPathBase oldNode = FSUtils.getNodeByPath(webSession, oldNodePath);
String fileName = oldNode.getName();
@@ -251,6 +265,7 @@ public class WebServiceFS implements DBWServiceFS {
@NotNull String parentPath,
@NotNull String folderName
) throws DBWebException {
validateEditPermissions(webSession);
try {
DBNPathBase parentNode = FSUtils.getNodeByPath(webSession, parentPath);
if (!Files.isDirectory(parentNode.getPath())) {
@@ -270,6 +285,7 @@ public class WebServiceFS implements DBWServiceFS {
@NotNull WebSession webSession,
@NotNull String nodePath
) throws DBWebException {
validateEditPermissions(webSession);
try {
DBNPathBase node = FSUtils.getNodeByPath(webSession, nodePath);
Path path = node.getPath();
@@ -289,4 +305,10 @@ public class WebServiceFS implements DBWServiceFS {
throw new DBWebException(String.format("File %s contains forbidden symbols", filename));
}
}
private void validateEditPermissions(@NotNull WebSession webSession) throws DBWebException {
if (DBWorkbench.isDistributed() && !webSession.hasPermission(DBWConstants.PERMISSION_FS_RESOURCE_EDIT)) {
throw new DBWebException("Permission denied");
}
}
}
@@ -16,6 +16,7 @@
*/
package io.cloudbeaver.service.fs.model;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.fs.FSUtils;
import io.cloudbeaver.model.session.WebSession;
@@ -32,6 +33,7 @@ import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.navigator.fs.DBNPathBase;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.IOUtils;
@@ -48,7 +50,7 @@ public class WebFSServlet extends WebServiceServletBase {
private static final String PARAM_PROJECT_ID = "projectId";
private final DBWServiceFS fs;
public WebFSServlet(CBApplication application, DBWServiceFS fs) {
public WebFSServlet(CBApplication<?> application, DBWServiceFS fs) {
super(application);
this.fs = fs;
}
@@ -60,6 +62,10 @@ public class WebFSServlet extends WebServiceServletBase {
return;
}
if (request.getMethod().equals("POST")) {
if (DBWorkbench.isDistributed() && !session.hasPermission(DBWConstants.PERMISSION_SQL_RESULT_UPDATE)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Permission denied.");
return;
}
doPost(session, request, response);
} else {
doGet(session, request, response);
@@ -109,15 +115,17 @@ public class WebFSServlet extends WebServiceServletBase {
@Override
protected Map<String, Object> getVariables(HttpServletRequest request) {
Map<String, Object> variables = super.getVariables(request);
try {
for (Part part : request.getParts()) {
if (part.getSubmittedFileName()!= null && !part.getSubmittedFileName().isEmpty()) {
variables.put("fileName", part.getSubmittedFileName());
break;
if (request.getMethod().equals("POST")) {
try {
for (Part part : request.getParts()) {
if (part.getSubmittedFileName() != null && !part.getSubmittedFileName().isEmpty()) {
variables.put("fileName", part.getSubmittedFileName());
break;
}
}
} catch (Exception e) {
log.debug("Failed to get fileName from request for logging", e);
}
} catch (Exception e) {
log.debug("Failed to get fileName from request for logging", e);
}
return variables;
}
@@ -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.13.qualifier
Bundle-Release-Date: 20251006
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,
@@ -22,4 +22,5 @@ prop.auth.model.ldap.ldap-enable-ssl = Enable SSL
prop.auth.model.ldap.ldap-enable-ssl.description = Use a secure SSL connection to the LDAP server
prop.auth.model.ldap.ldap-ssl-cert = Certificate (PEM format)
prop.auth.model.ldap.ldap-ssl-cert.description = PEM-encoded certificate used to validate the LDAP server when SSL is enabled
prop.auth.model.ldap.ldap-referral = Referral handling
prop.auth.model.ldap.ldap-referral.description = Specify how LDAP referrals are handled. Default is 'ignore'
@@ -21,4 +21,6 @@ prop.auth.model.ldap.ldap-login.description = Атрибут LDAP, которы
prop.auth.model.ldap.ldap-enable-ssl = Включить SSL
prop.auth.model.ldap.ldap-enable-ssl.description = Использовать защищённое SSL-соединение с сервером LDAP
prop.auth.model.ldap.ldap-ssl-cert = Сертификат (в формате PEM)
prop.auth.model.ldap.ldap-ssl-cert.description = PEM-сертификат, используемый для проверки подлинности LDAP-сервера при включённом SSL
prop.auth.model.ldap.ldap-ssl-cert.description = PEM-сертификат, используемый для проверки подлинности LDAP-сервера при включённом SSL
prop.auth.model.ldap.ldap-referral = Обработка перенаправлений
prop.auth.model.ldap.ldap-referral.description = Укажите, как обрабатываются перенаправления LDAP. По умолчанию 'ignore'
@@ -31,6 +31,7 @@
description="%ldap-login.description"/>
<property id="ldap-enable-ssl" label="%ldap-enable-ssl" description="%ldap-enable-ssl.description" type="boolean" defaultValue="false"/>
<property id="ldap-ssl-cert" label="%ldap-ssl-cert" description="%ldap-ssl-cert.description" type="file" features="file"/>
<property id="ldap-referral" label="%ldap-referral" description="%ldap-referral.description" type="string" defaultValue="ignore"/>
</propertyGroup>
</configuration>
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.ldap.auth</artifactId>
<version>1.0.13-SNAPSHOT</version>
<version>1.0.15-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -540,6 +540,12 @@ public class LdapAuthProvider implements SMAuthProviderExternal<SMSession>, SMBr
//it's a hack. Otherwise password will be written to database
authParameters.remove(LdapConstants.CRED_PASSWORD);
String referralHandlingMode = ldapSettings.getReferralHandlingMode();
environment.put(Context.REFERRAL, referralHandlingMode);
if ("follow".equalsIgnoreCase(referralHandlingMode)) {
environment.put("java.naming.ldap.referral.limit", "5");
}
context = initConnection(environment);
String searchFilter = "(member=" + fullDN + ")";
@@ -548,7 +554,12 @@ public class LdapAuthProvider implements SMAuthProviderExternal<SMSession>, SMBr
searchResults = context.search(ldapSettings.getBaseDN(), searchFilter, searchControls);
while (searchResults.hasMore()) {
result.add(searchResults.next().getName());
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);
@@ -27,6 +27,7 @@ public interface LdapConstants {
String PARAM_LOGIN = "ldap-login";
String PARAM_SSL_ENABLE = "ldap-enable-ssl";
String PARAM_SSL_CERT = "ldap-ssl-cert";
String PARAM_REFERRAL_HANDLING = "ldap-referral";
String CRED_USERNAME = "user";
String CRED_DISPLAY_NAME = "displayName";
@@ -36,6 +36,7 @@ public class LdapSettings {
private final String filter;
private final String loginAttribute;
private final LdapSslSetting ldapSslSetting;
private final String referralHandlingMode;
public LdapSettings(
@@ -56,6 +57,7 @@ public class LdapSettings {
providerConfiguration.getParameter(LdapConstants.PARAM_SSL_ENABLE),
providerConfiguration.getParameter(LdapConstants.PARAM_SSL_CERT)
);
this.referralHandlingMode = providerConfiguration.getParameterOrDefault(LdapConstants.PARAM_REFERRAL_HANDLING, "ignore");
}
@@ -109,4 +111,8 @@ public class LdapSettings {
public SMAuthProviderCustomConfiguration getProviderConfiguration() {
return providerConfiguration;
}
public String getReferralHandlingMode() {
return referralHandlingMode;
}
}
@@ -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.134.qualifier
Bundle-Release-Date: 20251006
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.134-SNAPSHOT</version>
<version>1.0.136-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -17,7 +17,6 @@
package io.cloudbeaver.service.metadata;
import graphql.schema.DataFetchingEnvironment;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.service.DBWBindingContext;
import io.cloudbeaver.service.WebServiceBindingBase;
@@ -37,12 +36,12 @@ public class WebServiceBindingMetadata extends WebServiceBindingBase<DBWServiceM
}
@Override
public void bindWiring(DBWBindingContext model) throws DBWebException {
public void bindWiring(DBWBindingContext model) {
model.getQueryType()
.dataFetcher("metadataGetNodeDDL", env -> getService(env).getNodeDDL(
getWebSession(env),
getNodeFromPath(env),
env.getArgument("options"))
getArgument(env, "options"))
).dataFetcher("metadataGetNodeExtendedDDL", env -> getService(env).getNodeExtendedDDL(
getWebSession(env),
getNodeFromPath(env)
@@ -51,7 +50,7 @@ public class WebServiceBindingMetadata extends WebServiceBindingBase<DBWServiceM
private DBNNode getNodeFromPath(DataFetchingEnvironment env) throws DBException {
WebSession webSession = getWebSession(env);
String nodePath = env.getArgument("nodeId");
String nodePath = getArgument(env, "nodeId");
if (nodePath == null) {
throw new DBException("Node path is null");
}
@@ -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.48.qualifier
Bundle-Release-Date: 20251006
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.48-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.83.qualifier
Bundle-Release-Date: 20251006
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.83-SNAPSHOT</version>
<version>1.0.85-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -16,7 +16,6 @@
*/
package io.cloudbeaver.service.rm;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.service.DBWBindingContext;
import io.cloudbeaver.service.WebServiceBindingBase;
import io.cloudbeaver.service.rm.impl.WebServiceRM;
@@ -36,74 +35,74 @@ public class WebServiceBindingRM extends WebServiceBindingBase<DBWServiceRM> {
}
@Override
public void bindWiring(DBWBindingContext model) throws DBWebException {
public void bindWiring(DBWBindingContext model) {
model.getQueryType()
.dataFetcher("rmListProjects",
env -> getService(env).listProjects(getWebSession(env)))
.dataFetcher("rmListSharedProjects",
env -> getService(env).listSharedProjects(getWebSession(env)))
.dataFetcher("rmProject",
env -> getService(env).getProject(getWebSession(env), env.getArgument("projectId")))
env -> getService(env).getProject(getWebSession(env), getArgumentVal(env, "projectId")))
.dataFetcher("rmListResources",
env -> getService(env).listResources(getWebSession(env),
env.getArgument("projectId"),
env.getArgument("folder"),
env.getArgument("nameMask"),
CommonUtils.toBoolean(env.getArgument("readProperties")),
CommonUtils.toBoolean(env.getArgument("readHistory"))))
getArgumentVal(env, "projectId"),
getArgument(env, "folder"),
getArgument(env, "nameMask"),
CommonUtils.toBoolean(getArgument(env, "readProperties")),
CommonUtils.toBoolean(getArgument(env, "readHistory"))))
.dataFetcher("rmReadResourceAsString",
env -> getService(env).readResourceAsString(getWebSession(env),
env.getArgument("projectId"),
env.getArgument("resourcePath")))
getArgumentVal(env, "projectId"),
getArgumentVal(env, "resourcePath")))
.dataFetcher("rmListProjectPermissions", env -> getService(env).listProjectPermissions())
.dataFetcher("rmListProjectGrantedPermissions", env -> getService(env).listProjectGrantedPermissions(
getWebSession(env),
env.getArgument("projectId")
getArgumentVal(env, "projectId")
))
.dataFetcher("rmListSubjectProjectsPermissionGrants", env -> getService(env).listSubjectProjectsPermissionGrants(
getWebSession(env),
env.getArgument("subjectId")
getArgumentVal(env, "subjectId")
))
.dataFetcher(
"rmUserProjectSettings", env -> getService(env).getProjectSettings(
getWebSession(env),
env.getArgument("projectId"),
env.getArgument("settingId")
getArgumentVal(env, "projectId"),
getArgument(env, "settingId")
)
)
;
model.getMutationType()
.dataFetcher("rmCreateResource",
env -> getService(env).createResource(getWebSession(env),
env.getArgument("projectId"),
env.getArgument("resourcePath"),
CommonUtils.toBoolean(env.getArgument("isFolder"))))
getArgumentVal(env, "projectId"),
getArgumentVal(env, "resourcePath"),
CommonUtils.toBoolean(getArgument(env, "isFolder"))))
.dataFetcher("rmMoveResource",
env -> getService(env).moveResource(getWebSession(env),
env.getArgument("projectId"),
env.getArgument("oldResourcePath"),
env.getArgument("newResourcePath")))
getArgumentVal(env, "projectId"),
getArgumentVal(env, "oldResourcePath"),
getArgumentVal(env, "newResourcePath")))
.dataFetcher("rmDeleteResource",
env -> getService(env).deleteResource(getWebSession(env),
env.getArgument("projectId"),
env.getArgument("resourcePath")))
getArgumentVal(env, "projectId"),
getArgumentVal(env, "resourcePath")))
.dataFetcher("rmWriteResourceStringContent",
env -> getService(env).writeResourceStringContent(getWebSession(env),
env.getArgument("projectId"),
env.getArgument("resourcePath"),
env.getArgument("data"),
env.getArgument("forceOverwrite")))
getArgumentVal(env, "projectId"),
getArgumentVal(env, "resourcePath"),
getArgumentVal(env, "data"),
getArgumentVal(env, "forceOverwrite")))
.dataFetcher("rmCreateProject", env -> getService(env).createProject(
getWebSession(env),
env.getArgument("projectName"),
env.getArgument("description")
getArgumentVal(env, "projectName"),
getArgument(env, "description")
))
.dataFetcher(
"rmUpdateProject", env -> getService(env).updateProject(
getWebSession(env),
getProjectReference(env),
env.getArgument("projectName"),
env.getArgument("description")
getArgument(env, "projectName"),
getArgument(env, "description")
)
)
.dataFetcher("rmDeleteProject", env -> getService(env).deleteProject(
@@ -112,45 +111,45 @@ public class WebServiceBindingRM extends WebServiceBindingBase<DBWServiceRM> {
))
.dataFetcher("rmSetProjectPermissions", env -> getService(env).setProjectPermissions(
getWebSession(env),
env.getArgument("projectId"),
new RMSubjectProjectPermissions(env.getArgument("permissions"))
getArgumentVal(env, "projectId"),
new RMSubjectProjectPermissions(getArgumentVal(env, "permissions"))
))
.dataFetcher("rmSetResourceProperty", env -> getService(env).setResourceProperty(
getWebSession(env),
env.getArgument("projectId"),
env.getArgument("resourcePath"),
env.getArgument("name"),
env.getArgument("value")
getArgumentVal(env, "projectId"),
getArgumentVal(env, "resourcePath"),
getArgumentVal(env, "name"),
getArgument(env, "value")
))
.dataFetcher("rmSetSubjectProjectPermissions", env -> getService(env).setSubjectProjectPermissions(
getWebSession(env),
env.getArgument("subjectId"),
new RMProjectPermissions(env.getArgument("permissions"))
getArgumentVal(env, "subjectId"),
new RMProjectPermissions(getArgumentVal(env, "permissions"))
))
.dataFetcher("rmAddProjectsPermissions", env -> getService(env).addProjectsPermissions(
getWebSession(env),
env.getArgument("projectIds"),
env.getArgument("subjectIds"),
env.getArgument("permissions")
getArgumentVal(env, "projectIds"),
getArgumentVal(env, "subjectIds"),
getArgumentVal(env, "permissions")
))
.dataFetcher("rmDeleteProjectsPermissions", env -> getService(env).deleteProjectsPermissions(
getWebSession(env),
env.getArgument("projectIds"),
env.getArgument("subjectIds"),
env.getArgument("permissions")
getArgumentVal(env, "projectIds"),
getArgumentVal(env, "subjectIds"),
getArgumentVal(env, "permissions")
))
.dataFetcher(
"rmAddUserProjectSettings", env -> getService(env).addProjectSettings(
getWebSession(env),
env.getArgument("projectId"),
env.getArgument("settings")
getArgumentVal(env, "projectId"),
getArgumentVal(env, "settings")
)
)
.dataFetcher(
"rmDeleteUserProjectSettings", env -> getService(env).deleteProjectSettings(
getWebSession(env),
env.getArgument("projectId"),
env.getArgument("settingIds")
getArgumentVal(env, "projectId"),
getArgument(env, "settingIds")
)
)
@@ -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.86.qualifier
Bundle-Release-Date: 20251006
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,
@@ -256,3 +256,46 @@ CREATE TABLE {table_prefix}CB_OBJECT_SETTINGS
FOREIGN KEY (SUBJECT_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT(SUBJECT_ID) ON DELETE CASCADE
);
-- Secrets
CREATE TABLE {table_prefix}CB_ACCESS_TOKEN
(
TOKEN_ID VARCHAR(128) NOT NULL,
USER_ID VARCHAR(128) NOT NULL,
TOKEN_NAME VARCHAR(128) NOT NULL,
CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
EXPIRATION_TIME TIMESTAMP NULL,
PRIMARY KEY (USER_ID, TOKEN_ID),
UNIQUE (USER_ID, TOKEN_NAME),
FOREIGN KEY (USER_ID) REFERENCES {table_prefix}CB_USER(USER_ID) ON DELETE CASCADE
);
-- Tasks
CREATE TABLE {table_prefix}CB_TASKS
(
TASK_ID VARCHAR(128) NOT NULL,
CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
TIMEOUT INTEGER NOT NULL,
PRIMARY KEY (TASK_ID)
);
CREATE TABLE {table_prefix}CB_SUBJECT_SECRETS
(
SUBJECT_ID VARCHAR(128) NOT NULL,
SECRET_ID VARCHAR(255) NOT NULL,
PROJECT_ID VARCHAR(128),
OBJECT_TYPE VARCHAR(32),
OBJECT_ID VARCHAR(128),
SECRET_VALUE TEXT NOT NULL,
ENCODING_TYPE VARCHAR(32) DEFAULT 'PLAINTEXT' NOT NULL,
CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
UPDATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (SUBJECT_ID, SECRET_ID),
FOREIGN KEY (SUBJECT_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE CASCADE
);
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.security</artifactId>
<version>1.0.86-SNAPSHOT</version>
<version>1.0.88-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -314,9 +314,13 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
}
for (String possibleUserId : List.of(userId, userId.toLowerCase())) {
if (isSubjectExists(possibleUserId)) {
log.info("User already exist : " + possibleUserId);
setUserAuthRole(connection, possibleUserId, authRole);
enableUser(connection, possibleUserId, true, null, null);
if (getSubjectType(possibleUserId) == SMSubjectType.team) {
log.error("Cannot import user '%s': a team with this name already exists.".formatted(possibleUserId));
} else {
log.info("User already exist : " + possibleUserId);
setUserAuthRole(connection, possibleUserId, authRole);
enableUser(connection, possibleUserId, true, null, null);
}
continue outer;
}
}
@@ -812,6 +816,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
}
}
@NotNull
@Override
public Map<String, Object> getCurrentUserParameters() throws DBCException {
String userId = getUserIdOrThrow();
@@ -1212,6 +1217,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
return getUserCredentials(getUserIdOrThrow(), authProviderId);
}
@NotNull
@Override
public String[] getCurrentUserLinkedProviders() throws DBException {
return getUserLinkedProviders(getUserIdOrThrow());
@@ -1631,7 +1637,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
// Sessions
@Override
public boolean isSessionPersisted(String id) throws DBException {
public boolean isSessionPersisted(@NotNull String id) throws DBException {
try (Connection dbCon = database.openConnection()) {
try (PreparedStatement dbStat = dbCon.prepareStatement("SELECT 1 FROM {table_prefix}CB_SESSION WHERE SESSION_ID=?")) {
dbStat.setString(1, id);
@@ -1647,6 +1653,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
}
}
@NotNull
private String createSmSession(
@NotNull String appSessionId,
@Nullable String userId,
@@ -1678,6 +1685,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
}
}
@NotNull
@Override
public SMAuthInfo authenticateAnonymousUser(@NotNull String appSessionId, @NotNull Map<String, Object> sessionParameters, @NotNull SMSessionType sessionType) throws DBException {
if (!application.isAnonymousAccessEnabled()) {
@@ -1709,6 +1717,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
return getSubjectPermissions(anonymousUserTeam);
}
@NotNull
@Override
public SMAuthInfo authenticate(
@NotNull String appSessionId,
@@ -2057,6 +2066,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
}
}
@NotNull
@Override
public SMAuthInfo getAuthStatus(@NotNull String authId) throws DBException {
var smAuthInfo = getAuthStatus(authId, false);
@@ -2903,6 +2913,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
return new SMAuthPermissions(userId, sessionId, permissions);
}
@NotNull
@Override
public SMAuthProviderDescriptor[] getAvailableAuthProviders() throws DBException {
ServletAuthConfiguration appConfiguration = application.getAuthConfiguration();
@@ -77,7 +77,7 @@ public class CBDatabase extends InternalDB<WebDatabaseConfig> {
private static final SQLSchemaConfig SCHEMA_CREATE_CONFIG = new SQLSchemaConfig(
SCHEMA_ID,
"db/cb_schema_create.sql",
"db/cb_schema_create",
"db/cb_schema_update_",
CURRENT_SCHEMA_VERSION,
0,
@@ -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.46.qualifier
Bundle-Release-Date: 20251006
Bundle-Version: 1.0.48.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Fragment-Host: slf4j.api
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.slf4j</artifactId>
<version>1.0.46-SNAPSHOT</version>
<version>1.0.48-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -2,7 +2,7 @@
<feature
id="io.cloudbeaver.ce.drivers.feature"
label="Cloudbeaver CE 3rd Party Drivers"
version="1.0.154.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.154-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.2.2.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.2.2-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.2.2.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.2.2-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.84.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.84-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.2.2.qualifier" useFeatures="true"
application="io.cloudbeaver.product.ce.application" version="25.2.4.qualifier" useFeatures="true"
includeLaunchers="false">
<configIni use="default">
+1 -1
View File
@@ -9,7 +9,7 @@
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
<version>25.2.2-SNAPSHOT</version>
<version>25.2.4-SNAPSHOT</version>
<artifactId>web-server</artifactId>
<packaging>eclipse-repository</packaging>
<name>Cloudbeaver Server Product</name>
@@ -12,6 +12,7 @@ import {
ComboboxPopover as AriaComboboxPopover,
ComboboxItem as AriaComboboxItem,
ComboboxDisclosure,
ComboboxCancel,
ComboboxValue,
type ComboboxValueProps,
type ComboboxProviderProps,
@@ -29,7 +30,7 @@ export interface ComboboxProps extends Omit<AriaComboboxProps, 'size'> {
size?: 'small' | 'medium' | 'large' | 'xlarge';
}
export function ComboboxInput({ size, ...props }: ComboboxProps) {
export function ComboboxInput({ size, ...props }: ComboboxProps): React.JSX.Element {
return <Combobox {...props} className={clsx('dbv-kit-combobox', `dbv-kit-combobox--${size ?? 'medium'}`, props.className)} autoSelect />;
}
@@ -40,7 +41,7 @@ export interface ComboboxPopoverProps extends AriaComboboxPopoverProps {
/**
* ComboboxPopover - Wrapper around AriaKit's popover with default styles
*/
export function ComboboxPopover({ children, className, ...props }: ComboboxPopoverProps) {
export function ComboboxPopover({ children, className, ...props }: ComboboxPopoverProps): React.JSX.Element {
return (
<AriaComboboxPopover gutter={8} className={clsx('dbv-kit-combobox__popover', className)} portal sameWidth unmountOnHide {...props}>
{children}
@@ -48,7 +49,7 @@ export function ComboboxPopover({ children, className, ...props }: ComboboxPopov
);
}
export function ComboboxItem(props: ComboboxItemProps) {
export function ComboboxItem(props: ComboboxItemProps): React.JSX.Element {
return <AriaComboboxItem {...props} className={clsx('dbv-kit-combobox__item', props.className)} />;
}
@@ -57,6 +58,7 @@ export {
useComboboxStore,
useStoreState,
ComboboxDisclosure,
ComboboxCancel,
ComboboxValue,
ComboboxProvider,
type ComboboxProviderProps,
+1 -1
View File
@@ -16,7 +16,7 @@
"@dbeaver/prettier-config": "workspace:^",
"@dbeaver/react-tests": "workspace:^",
"@wroud/ts-template": "^0",
"happy-dom": "^18",
"happy-dom": "^20",
"prettier": "^3.6.2",
"rimraf": "^6",
"vitest": "^3"
+1 -1
View File
@@ -40,7 +40,7 @@
"@types/react-dom": "^19",
"concurrently": "^9",
"eslint": "^9.30.1",
"happy-dom": "^18",
"happy-dom": "^20",
"mobx": "^6",
"mobx-react-lite": "^4",
"msw": "^2",

Some files were not shown because too many files have changed in this diff Show More