Compare commits

..
Author SHA1 Message Date
sergeyteleshev 124c1f32bc dbeaver/pro#7183 updates happy-dom to 20.0.2 (#3834) 2025-10-17 00:42:31 +08:00
2105 changed files with 20450 additions and 55656 deletions
+1 -1
View File
@@ -46,7 +46,7 @@ jobs:
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: "24.13.1"
node-version: "lts/*"
cache: "yarn"
cache-dependency-path: |
cloudbeaver/webapp/yarn.lock
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: "24.13.1"
node-version: "lts/*"
cache: "yarn"
cache-dependency-path: "./cloudbeaver/webapp/yarn.lock"
+5 -4
View File
@@ -6,7 +6,6 @@ on:
- opened
- synchronize
- reopened
- ready_for_review
push:
branches: [devel]
@@ -25,6 +24,11 @@ jobs:
project-deps: ./cloudbeaver/project.deps
timeout-minutes: 5
# build-server:
# name: Server
# uses: ./.github/workflows/backend-build.yml
# secrets: inherit
build-frontend:
name: Frontend
uses: ./.github/workflows/frontend-build.yml
@@ -34,9 +38,6 @@ jobs:
name: Server
uses: dbeaver/dbeaver-common/.github/workflows/java-checkstyle.yml@devel
secrets: inherit
permissions:
contents: read
pull-requests: read
lint-frontend:
name: Frontend
+2 -1
View File
@@ -14,7 +14,8 @@
"redhat.vscode-yaml",
"streetsidesoftware.code-spell-checker",
"streetsidesoftware.code-spell-checker-russian",
"github.copilot-chat",
"syler.sass-indented",
"VisualStudioExptTeam.vscodeintellicode",
"yzhang.markdown-all-in-one",
"GraphQL.vscode-graphql-syntax",
"mrmlnc.vscode-json5",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vscode-java-configs",
"packageManager": "yarn@4.14.1",
"packageManager": "yarn@4.9.2",
"devDependencies": {
"typescript": "^5.8.3"
}
+40 -91
View File
@@ -1,20 +1,17 @@
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/cloudbeaver-logo.png" alt="CloudBeaver logo" align="right" width="250"/>
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/cloudbeaver-logo.png" align="right" width="250"/>
# CloudBeaver Community
Cloud Database Manager - Community Edition.
CloudBeaver is a web server that provides a rich web interface. The server itself is a Java application, and the web part is written in TypeScript and React.
It is free to use and open-source (licensed under [Apache 2](https://github.com/dbeaver/cloudbeaver/blob/devel/LICENSE) license).
See our [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
<a><img src="https://github.com/dbeaver/cloudbeaver/wiki/images/connection-creation-demo.png" width="400"/></a>
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/gis-demo.png" width="400"/>
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/data-transfer-demo.png" width="400"/>
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/sql-editor-demo.png" width="400"/>
## Documentation
- [GitHub WIKI](https://github.com/dbeaver/cloudbeaver/wiki)
- [Official documentation](https://dbeaver.com/docs/cloudbeaver/)
## Run in Docker
- [Official Docker repository](https://hub.docker.com/r/dbeaver/cloudbeaver)
@@ -22,100 +19,52 @@ It is free to use and open-source (licensed under [Apache 2](https://github.com/
## Demo server
You can see a live demo of CloudBeaver server here: https://demo.cloudbeaver.io
You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
[Database access instructions](https://github.com/dbeaver/cloudbeaver/wiki/Demo-Server)
## Changelog
### 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.
### 26.1.1 2026-06-22
### Changes since 26.1.0:
- General: Added a "Project Info" tab containing the project description to the project context menu.
- Security:
- Fixed a cross-site scripting vulnerability caused by unsanitized user input in the web interface.
- Resolved a security flaw related to user-controlled file paths by adding strict validation to prevent unauthorized resource access.
- Fixed the critical vulnerability (CVE-2026-9277) in the shell-quote library (updated to version 1.8.4).
### 26.1.0 2026-06-01
### Changes since 26.0.0:
### 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;
- Different bug fixes and enhancements have been made.
### 25.2.0 2025-09-01
### Changes since 25.1.0:
- Administration:
- Added support for custom startup arguments when running CloudBeaver in a container. Administrators can specify configuration file locations to adapt the application to their environment. (thanks to @MalteHei)
- Updated the manifest.webmanifest file to generate dynamically based on the CLOUDBEAVER_ROOT_URI environment variable. (thanks to @houssemexo26)
- Authorization:
- Added HTTP header authentication support for GraphQL queries. Users can provide API tokens via Authorization or X-Api-Key headers, allowing external data tools to authenticate without modifying request bodies.
- Fixed a bug in the authentication form: the existing password is now required for password changes.
- Added support for multiple server URLs to accommodate different access policies for internal and external users, improving flexibility in network-restricted environments. To set it up, use the Allowed Server URLs field in the Server configuration within the Administration section;
- Added the Force HTTPS mode setting in Server Configuration to enforce redirecting from HTTP to HTTPS. This setting helps avoid a potential man-in-the-middle attack. This setting is turned off by default. Please remember to configure your proxy before enabling;
- Added the CLOUDBEAVER_BIND_SESSION_TO_IP option to improve session security by linking user sessions to their IP address. It is disabled by default. When enabled, this helps protect against certain types of session hijacking attacks, where an attacker could try to take over a user’s session. Note: Users will be logged out automatically on the IP address change (switching networks or using mobile data);
- A password confirmation field has been added for administrators in the Easy Config section to prevent accidental misconfigurations.
- SQL Editor:
- Updated the SQL Editor to use real tab characters for indentation. The application displayed tabs as four-space gaps with a distinct visual marker.
- Added an ability to view and edit scripts without connecting to the database.
- Added the ability to open generated queries right from the Generate SQL window.
- Added fuzzy search to SQL Editor auto-completion for object names. The feature allows users to find tables and containers even with typos or incorrect letter ordering.
- Added the tooltip about quoting rules for the Bind variables parameters dialog.
- Fixed keyword autocompletion for partially typed keywords to avoid redundant letters.
- Changed the default engine used for autocompletion in the SQL Editor. This Semantic engine offers improved suggestions for database objects, keywords, and functions. You can switch back to the Legacy engine in Preferences > SQL Editor;
- A “Clear” button was added to the output panel.
- Data Editor:
- Added the ability to copy-paste multiple cells at once. Pasted values will be distributed across selected cells.
- Added an ability to add, duplicate, delete, or set values to NULL in multiple highlighted rows via the Data Editor context menu.
- Added the Find and Replace functionality for the Data Editor with the ability to find data by matching case, whole word, or using regular expressions.
- Data Editor started to keep the state of column configurations, such as filters, sorting, and ordering, after the reconnect, page refresh, and re-login.
- Added key column mark for tables with unique keys in the data grid.
- Added more settings to the Generated SQL window, including the ability to select fully qualified names for query or use compact SQL formatting.
- Added a DDL generation option to the Data Editor and Result Set context menu.
- Fixed copy and paste behavior for multiline and special characters in the Data Editor. Added visual glyphs to indicate line breaks and tabs in the data grid.
- Fixed the ability to copy Boolean values in the clipboard in the Data Editor.
- Fixed the ability to calculate row count for non-standard SELECT statements. (thanks to @fdcastel)
- Navigator tree:
- Reorganized the context menu on the connection level to make it more compact.
- Added support for special symbols (pipe, comma, and asterisk) for the search field.
- Removed the "Folders" setting from the settings list for the Navigator Tree.
- Accessibility:
- Added the Skip to content option for quick keyboard access to the Navigator Tree, editors, and shortcuts list tab to improve application accessibility.
- Improved keyboard navigation for context menu and buttons for Data Editor, SQL Editor, and Navigator tree.
- Added the Ctrl + / keyboard shortcut to open the context menu for selected cells in the Data Editor.
- Fixed the ability to switch tabs in the Data Editor using the keyboard.
- Fixed contrast for elements across different application parts in the light and dark themes to meet WCAG requirements.
- Column descriptions were added in the Data Editor under column names to provide more metadata context. You can disable this in Preferences > Data Viewer;
- Added an ability to review the script before execution when users edit tables without primary keys.
- General:
- Marked icons for non-saved (temporary) scripts with the hourglass symbol to add visual distinction from the saved ones.
- Added the ability to set to NULL any value in the Driver Properties.
- Fixed custom driver properties display for the connection page.
- Fixed data export in SQL format for values containing single quotes.
- Added a new welcome screen for a freshly opened application. This screen contains shortcuts to create a new connection, open SQL Editor, or documentation;
- Added a search panel for SQL Editor and Value panel: press Ctrl/Cmd + F to open a panel that allows searching and replacing by keyword or regular expression;
- Changed the data transfer mechanism to avoid intermediate file creation. The parameter dataExportFileSizeLimit was removed from the server configuration as deprecated;
- The CloudBeaver default theme matches the device theme by default now. You can change this behavior in user preferences under the Theme section;
- Added the option to display tabs across multiple rows, allowing you to see all tabs without scrolling. You can enable this in Preferences > Interface;
- Improved dark theme accessibility by enhancing color contrast across the app for better readability and compliance with accessibility standards;
- The database navigator now automatically hides empty folders in shared projects, reducing visual clutter and speeding up the process of finding active connections.
- Databases and drivers:
- ClickHouse:
- Fixed the display of Array(JSON) types in the data grid.
- Updated driver version to 0.9.7.
- Fixed IP address display in the Data Editor by removing the leading slash.
- Added support for the "prompt=false" connection property for DB2 for IBM i to turn off interactive prompts and prevent related errors.
- DuckDB:
- Fixed map rendering for GEOMETRY types with CRS parameters.
- Fixed geometry type display for database versions > 1.5.
- H2 database:
- Added new embedded driver version 2.4.2.
- Updated server driver to version 2.4.2.
- Firebird: Expanded database-specific SQL dialect coverage for the SQL Editor. Added more keywords, built-in functions, and SQL generators.
- MySQL: Fixed query boundaries recognition in the SQL Editor for scripts with the DELIMITER keyword.
- PostgreSQL:
- Updated driver to version 42.7.11
- Fixed missing INOUT parameters in CHECK generation for procedures and functions.
- Fixed handling of 24:xx values in time columns.
- Fixed an error when connecting via URL if the database name contains a hyphen.
- SQL Server:
- Fixed DDL generation for external tables.
- Fixed the database connectivity issue after the Java update.
- Security:
- Added an administrative setting to restrict SSH tunneling capabilities. Administrators can now limit tunnel configuration to authorized users, reducing the risk of unauthorized network access.
- Fixed the critical vulnerability (CVE-2025-62718) in the axios library. The library was updated to version 1.15.0.
- Fixed the high vulnerability (CVE-2026-4800) in the lodash library. The library was updated to version 4.18.0.
- Fixed the high vulnerability (CVE-2026-39363) in the vite library. The library was updated to version 7.3.2.
- Fixed the high vulnerability (CVE-2026-42035) in the axios library. The library was updated to version 1.15.1.
- Fixed the high vulnerability (CVE-2026-3505) in the bcpg-jdk18on library. The library was updated to version 1.84.0.
- Fixed the high vulnerability (CVE-2026-33671) in the picomatch library. The library was updated to version 2.3.2.
- Fixed the high vulnerability (CVE-2026-33943) in the happy-dom library. The library was updated to version 20.8.8.
- Fixed the high vulnerability (CVE-2025-45141) in the tar library. The library was updated to version 7.5.11.
- Fixed the high vulnerability (CVE-2026-27148) in the storybook library. The library was updated to version 10.2.10.
- Fixed the high vulnerability (CVE-2026-27606) in the rollup library. The library was updated to version 4.59.0.
- Fixed the high vulnerability (CVE-2026-27904) in the minimatch library. The library was updated to version 3.1.3.
- Fixed the high vulnerability (CVE-2026-27959) in the koa library. The library was updated to version 2.16.4.
- Fixed the high vulnerability (CVE-2026-32141) in the flatted library. The library was updated to version 4.4.0.
- Fixed the medium vulnerability (CVE-2026-41305) in the postcss library. The library was updated to version 8.5.14.
- Clickhouse: fixed the presentation of tuples and map data types in the Data Editor;
- Databend database support has been added (thanks to @hantmac);
- DuckDB: driver has been updated to version 1.3;
- MySQL: Improved performance when retrieving foreign keys metadata;
- PostgreSQL: fixed misplaced comment for table DDL generation.
## Contribution
As a community-driven open-source project, we warmly welcome contributions through GitHub pull requests.
[We are happy to reward](https://dbeaver.com/help-dbeaver/) our most active contributors every major sprint.
The most significant contribution to our code for the major release 25.2.0 was made by:
1. [hantmac](https://github.com/hantmac) - added support for Databend in CloudBeaver Community Edition.
+13 -1
View File
@@ -1,6 +1,18 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | --------- |
| 22.x | yes |
| 23.x | yes |
| 24.x | yes |
## Reporting a Vulnerability
Please report (suspected) security vulnerabilities to <security@dbeaver.com>.
Please report (suspected) security vulnerabilities to devops@dbeaver.com.
You will receive a response from us within 48 hours.
If the issue is confirmed, we will release a patch as soon as possible, depending on complexity, but historically, within a few days.
-61
View File
@@ -1,61 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.cloudbeaver</groupId>
<artifactId>config-generator</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.13.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>io.cloudbeaver.ConfigGenerator</mainClass>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<executions>
<execution>
<id>copy-external-config</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.outputDirectory}/config</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/../../config/template</directory>
<includes>
<include>cloudbeaver-base.conf</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,235 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.io.Reader;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
public class ConfigGenerator {
private static final Gson gson = new GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.create();
private static final String EMPTY_STRING = "''";
public static void main(String[] args) throws Exception {
String baseConfigPath = System.getProperty("config.base");
if (baseConfigPath == null || baseConfigPath.isEmpty()) {
URL resource = ConfigGenerator.class.getClassLoader().getResource("config/cloudbeaver-base.conf");
if (resource == null) {
System.out.println("Base config path is required");
return;
}
baseConfigPath = resource.toURI().getPath();
if (System.getProperty("os.name").toLowerCase().contains("win")) {
// Fix for Windows in dev mode. Resource URI starts with / (e.g. /C:/some/folder)
// And this breaks Java NIO
if (baseConfigPath.startsWith("/")) {
baseConfigPath = baseConfigPath.substring(1);
}
}
}
String outputPath = System.getProperty("config.output");
if (outputPath == null || outputPath.isEmpty()) {
System.out.println("Output config path is required");
return;
}
String configPatches = System.getProperty("config.patches");
List<String> overrideConfigs = configPatches == null ? List.of() : Arrays.asList(configPatches.split(","));
Map<String, Object> config = readConfigurationFile(baseConfigPath);
System.out.println("Base config loaded");
for (String overrideConfig : overrideConfigs) {
if (Files.exists(Path.of(overrideConfig))) {
System.out.println("Applying override config: " + overrideConfig);
} else {
System.out.println("Override config not found, skipping: " + overrideConfig);
continue;
}
System.out.println("Processing override config: " + overrideConfig);
Map<String, Object> overrideConfigMap = readConfigurationFile(overrideConfig);
System.out.println("Override config loaded: " + overrideConfig);
applyProductConfig(config, overrideConfigMap);
System.out.println("Override config applied: " + overrideConfig);
}
Map<String, Object> finalMap = convertToOutputFormat(config);
System.out.println("Config converted to output format");
writeConfigurationFile(finalMap, outputPath);
System.out.println("Config written to: " + outputPath);
}
private static Map<String, Object> readConfigurationFile(String filePath) throws Exception {
Path path = Path.of(filePath);
if (!Files.exists(path)) {
throw new IllegalArgumentException("Configuration file not found: " + filePath);
}
try (Reader reader = Files.newBufferedReader(path)) {
return gson.fromJson(reader, TypeToken.getParameterized(Map.class, String.class, Object.class).getType());
}
}
private static void applyProductConfig(Map<String, Object> baseConfig, Map<String, Object> productConfig) {
if (productConfig.containsKey("add")) {
Map<String, Object> add = (Map<String, Object>) productConfig.get("add");
deepMergeMap(baseConfig, add);
}
if (productConfig.containsKey("remove")) {
Map<String, Object> remove = (Map<String, Object>) productConfig.get("remove");
deepRemoveMap(baseConfig, remove);
}
}
private static void deepMergeMap(Map<String, Object> target, Map<String, Object> source) {
for (Map.Entry<String, Object> entry : source.entrySet()) {
Object sourceValue = entry.getValue();
Object targetValue = target.get(entry.getKey());
if (sourceValue instanceof Map && targetValue instanceof Map) {
deepMergeMap((Map<String, Object>) targetValue, (Map<String, Object>) sourceValue);
} else {
target.put(entry.getKey(), sourceValue);
}
}
}
private static void deepRemoveMap(Map<String, Object> target, Map<String, Object> source) {
for (Map.Entry<String, Object> entry : source.entrySet()) {
Object sourceValue = entry.getValue();
Object targetValue = target.get(entry.getKey());
if (sourceValue instanceof Map && targetValue instanceof Map) {
deepRemoveMap((Map<String, Object>) targetValue, (Map<String, Object>) sourceValue);
} else {
target.remove(entry.getKey());
}
}
}
private static void writeConfigurationFile(Map<String, Object> map, String filePath) throws Exception {
JsonElement tree = gson.toJsonTree(map);
StringBuilder sb = new StringBuilder();
writeElement(tree, sb, 0);
Files.write(Path.of(filePath), sb.toString().getBytes());
}
private static void writeElement(JsonElement elem, StringBuilder sb, int count) {
String indent = " ";
if (elem.isJsonObject()) {
sb.append("{\n");
JsonObject obj = elem.getAsJsonObject();
var iterator = obj.entrySet().iterator();
while (iterator.hasNext()) {
var entry = iterator.next();
sb.repeat(indent, count + 1).append(entry.getKey()).append(": ");
writeElement(entry.getValue(), sb, count + 1);
if (iterator.hasNext()) {
sb.append(",");
}
sb.append("\n");
}
sb.repeat(indent, count).append("}");
} else if (elem.isJsonArray()) {
sb.append("[\n");
JsonArray array = elem.getAsJsonArray();
for (int i = 0; i < array.size(); i++) {
sb.repeat(indent, count + 1);
writeElement(array.get(i), sb, count + 1);
if (i < array.size() - 1) {
sb.append(",");
}
sb.append("\n");
}
sb.repeat(indent, count).append("]");
} else {
String valueStr = gson.toJson(elem).replace("\n", "\n" + indent);
sb.append(valueStr);
}
}
private static Map<String, Object> convertToOutputFormat(Map<String, Object> map) {
Map<String, Object> result = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
result.put(entry.getKey(), convertValue(entry.getValue()));
}
return result;
}
private static Object convertValue(Object value) {
if (value instanceof Map) {
Map<String, Object> map = (Map<String, Object>) value;
if (!map.isEmpty() && map.containsKey("value")) {
if (map.containsKey("env")) {
String env = (String) map.get("env");
Object defValue = map.get("value");
return "${" + env + ":" + formatDefaultValue(defValue) + "}";
} else {
return convertValue(map.get("value"));
}
}
Map<String, Object> result = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
result.put(entry.getKey(), convertValue(entry.getValue()));
}
return result;
} else if (value instanceof List) {
List<Object> list = (List<Object>) value;
List<Object> result = new ArrayList<>();
for (Object item : list) {
result.add(convertValue(item));
}
return result;
}
return value;
}
private static String formatDefaultValue(Object value) {
return switch (value) {
case null -> EMPTY_STRING;
case String str -> str.isEmpty() ? EMPTY_STRING : str;
case Boolean b -> value.toString();
case Number number -> {
if (number instanceof Double || number instanceof Float) {
long longVal = number.longValue();
if (longVal == number.doubleValue()) {
yield String.valueOf(longVal);
}
}
yield number.toString();
}
// TODO: handle list elements if needed
default -> value.toString();
};
}
}
+99
View File
@@ -0,0 +1,99 @@
{
server: {
serverPort: "${CLOUDBEAVER_WEB_SERVER_PORT:8978}",
forceHttps: "${CLOUDBEAVER_FORCE_HTTPS:false}",
contentRoot: "web",
driversLocation: "drivers",
sslConfigurationPath:"${CLOUDBEAVER_SSL_CONF_PATH:workspace/.data/ssl-config.xml}",
rootURI: "${CLOUDBEAVER_ROOT_URI:/}",
serviceURI: "/api/",
supportedHosts: [],
productSettings: {
# Global properties
core.theming.theme: "${CLOUDBEAVER_CORE_THEMING_THEME:system}",
core.localization.language: "${CLOUDBEAVER_CORE_LOCALIZATION:en}",
plugin.sql-editor.autoSave: "${CLOUDBEAVER_SQL_EDITOR_AUTOSAVE:true}",
plugin.sql-editor.disabled: "${CLOUDBEAVER_SQL_EDITOR_DISABLED:false}",
# max size of the file that can be uploaded to the editor (in kilobytes)
plugin.sql-editor.maxFileSize: "${CLOUDBEAVER_SQL_EDITOR_MAX_FILE_SIZE:10240}",
plugin.log-viewer.disabled: "${CLOUDBEAVER_LOG_VIEWER_DISABLED:false}",
plugin.log-viewer.logBatchSize: "${CLOUDBEAVER_LOG_VIEWER_LOG_BATCH_SIZE:1000}",
plugin.log-viewer.maxLogRecords: "${CLOUDBEAVER_LOG_VIEWER_MAX_LOG_RECORDS:2000}",
sql.proposals.insert.table.alias: "${CLOUDBEAVER_SQL_PROPOSALS_INSERT_TABLE_ALIAS:PLAIN}",
SQLEditor.ContentAssistant.experimental.mode: "${CLOUDBEAVER_SQL_EDITOR_CONTENT_ASSISTANT_EXPERIMENTAL_MODE:NEW}"
},
expireSessionAfterPeriod: "${CLOUDBEAVER_EXPIRE_SESSION_AFTER_PERIOD:1800000}",
bindSessionToIp: "${CLOUDBEAVER_BIND_SESSION_TO_IP:disable}",
develMode: "${CLOUDBEAVER_DEVEL_MODE:false}",
enableSecurityManager: false,
sm: {
enableBruteForceProtection: "${CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED:true}",
maxFailedLogin: "${CLOUDBEAVER_MAX_FAILED_LOGINS:10}",
minimumLoginTimeout: "${CLOUDBEAVER_MINIMUM_LOGIN_TIMEOUT:1}",
blockLoginPeriod: "${CLOUDBEAVER_BLOCK_PERIOD:300}",
passwordPolicy: {
minLength: "${CLOUDBEAVER_POLICY_MIN_LENGTH:8}",
requireMixedCase: "${CLOUDBEAVER_POLICY_REQUIRE_MIXED_CASE:true}",
minNumberCount: "${CLOUDBEAVER_POLICY_MIN_NUMBER_COUNT:1}",
minSymbolCount: "${CLOUDBEAVER_POLICY_MIN_SYMBOL_COUNT:0}"
}
},
database: {
driver: "${CLOUDBEAVER_DB_DRIVER:h2_embedded_v2}",
url: "${CLOUDBEAVER_DB_URL:jdbc:h2:${workspace}/.data/cb.h2v2.dat}",
schema: "${CLOUDBEAVER_DB_SCHEMA:''}",
user: "${CLOUDBEAVER_DB_USER:''}",
password: "${CLOUDBEAVER_DB_PASSWORD:''}",
initialDataConfiguration: "${CLOUDBEAVER_DB_INITIAL_DATA:conf/initial-data.conf}",
pool: {
minIdleConnections: "${CLOUDBEAVER_DB_MIN_IDLE_CONNECTIONS:4}",
maxIdleConnections: "${CLOUDBEAVER_DB_MAX_IDLE_CONNECTIONS:10}",
maxConnections: "${CLOUDBEAVER_DB_MAX_CONNECTIONS:100}",
validationQuery: "${CLOUDBEAVER_DB_VALIDATION_QUERY:SELECT 1}"
},
backupEnabled: "${CLOUDBEAVER_DB_BACKUP_ENABLED:true}"
}
},
app: {
anonymousAccessEnabled: "${CLOUDBEAVER_APP_ANONYMOUS_ACCESS_ENABLED:true}",
anonymousUserRole: user,
defaultUserTeam: "${CLOUDBEAVER_APP_DEFAULT_USER_TEAM:user}",
grantConnectionsAccessToAnonymousTeam: "${CLOUDBEAVER_APP_GRANT_CONNECTIONS_ACCESS_TO_ANONYMOUS_TEAM:false}",
supportsCustomConnections: "${CLOUDBEAVER_APP_SUPPORTS_CUSTOM_CONNECTIONS:false}",
showReadOnlyConnectionInfo: "${CLOUDBEAVER_APP_READ_ONLY_CONNECTION_INFO:false}",
systemVariablesResolvingEnabled: "${CLOUDBEAVER_SYSTEM_VARIABLES_RESOLVING_ENABLED:false}",
forwardProxy: "${CLOUDBEAVER_APP_FORWARD_PROXY:false}",
publicCredentialsSaveEnabled: "${CLOUDBEAVER_APP_PUBLIC_CREDENTIALS_SAVE_ENABLED:true}",
adminCredentialsSaveEnabled: "${CLOUDBEAVER_APP_ADMIN_CREDENTIALS_SAVE_ENABLED:true}",
resourceManagerEnabled: "${CLOUDBEAVER_APP_RESOURCE_MANAGER_ENABLED:true}",
resourceQuotas: {
resourceManagerFileSizeLimit: "${CLOUDBEAVER_RESOURCE_QUOTA_RESOURCE_MANAGER_FILE_SIZE_LIMIT:500000}",
sqlMaxRunningQueries: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_MAX_RUNNING_QUERIES:100}",
sqlResultSetRowsLimit: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_RESULT_SET_ROWS_LIMIT:100000}",
sqlTextPreviewMaxLength: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_TEXT_PREVIEW_MAX_LENGTH:4096}",
sqlBinaryPreviewMaxLength: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_BINARY_PREVIEW_MAX_LENGTH:261120}"
},
enabledAuthProviders: [
"local"
],
disabledBetaFeatures: [
]
}
}
-271
View File
@@ -1,271 +0,0 @@
{
server: {
serverPort: {
env: "CLOUDBEAVER_WEB_SERVER_PORT",
value: 8978
},
forceHttps: {
env: "CLOUDBEAVER_FORCE_HTTPS",
value: false
},
contentRoot: {
env: "CLOUDBEAVER_CONTENT_ROOT",
value: "web"
},
driversLocation: {
env: "CLOUDBEAVER_DRIVERS_LOCATION",
value: "drivers"
},
sslConfigurationPath: {
env: "CLOUDBEAVER_SSL_CONF_PATH",
value: "workspace/.data/ssl-config.xml"
},
rootURI: {
env: "CLOUDBEAVER_ROOT_URI",
value: "/"
},
serviceURI: {
env: "CLOUDBEAVER_SERVICE_URI",
value: "/api/"
},
supportedHosts: [],
productSettings: {
core.theming.theme: {
env: "CLOUDBEAVER_CORE_THEMING_THEME",
value: "system"
},
core.localization.language: {
env: "CLOUDBEAVER_CORE_LOCALIZATION",
value: "en"
},
plugin.sql-editor.autoSave: {
env: "CLOUDBEAVER_SQL_EDITOR_AUTOSAVE",
value: true
},
plugin.sql-editor.disabled: {
env: "CLOUDBEAVER_SQL_EDITOR_DISABLED",
value: false
},
plugin.sql-editor.maxFileSize: {
env: "CLOUDBEAVER_SQL_EDITOR_MAX_FILE_SIZE",
value: 10240
},
plugin.log-viewer.disabled: {
env: "CLOUDBEAVER_LOG_VIEWER_DISABLED",
value: false
},
plugin.log-viewer.logBatchSize: {
env: "CLOUDBEAVER_LOG_VIEWER_LOG_BATCH_SIZE",
value: 1000
},
plugin.log-viewer.maxLogRecords: {
env: "CLOUDBEAVER_LOG_VIEWER_MAX_LOG_RECORDS",
value: 2000
},
sql.proposals.insert.table.alias: {
env: "CLOUDBEAVER_SQL_PROPOSALS_INSERT_TABLE_ALIAS",
value: "PLAIN"
},
SQLEditor.ContentAssistant.experimental.mode: {
env: "CLOUDBEAVER_SQL_EDITOR_CONTENT_ASSISTANT_EXPERIMENTAL_MODE",
value: "NEW"
}
},
expireSessionAfterPeriod: {
env: "CLOUDBEAVER_EXPIRE_SESSION_AFTER_PERIOD",
value: 1800000
},
bindSessionToIp: {
env: "CLOUDBEAVER_BIND_SESSION_TO_IP",
value: "disable"
},
develMode: {
env: "CLOUDBEAVER_DEVEL_MODE",
value: false
},
sm: {
enableBruteForceProtection: {
env: "CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED",
value: true
},
maxFailedLogin: {
env: "CLOUDBEAVER_MAX_FAILED_LOGINS",
value: 10
},
minimumLoginTimeout: {
env: "CLOUDBEAVER_MINIMUM_LOGIN_TIMEOUT",
value: 1
},
blockLoginPeriod: {
env: "CLOUDBEAVER_BLOCK_PERIOD",
value: 300
},
passwordPolicy: {
minLength: {
env: "CLOUDBEAVER_POLICY_MIN_LENGTH",
value: 8
},
requireMixedCase: {
env: "CLOUDBEAVER_POLICY_REQUIRE_MIXED_CASE",
value: true
},
minNumberCount: {
env: "CLOUDBEAVER_POLICY_MIN_NUMBER_COUNT",
value: 1
},
minSymbolCount: {
env: "CLOUDBEAVER_POLICY_MIN_SYMBOL_COUNT",
value: 0
}
}
},
database: {
driver: {
env: "CLOUDBEAVER_DB_DRIVER",
value: "h2_embedded_v2"
},
url: {
env: "CLOUDBEAVER_DB_URL",
value: "jdbc:h2:${workspace}/.data/cb.h2v2.dat"
},
schema: {
env: "CLOUDBEAVER_DB_SCHEMA",
value: ""
},
user: {
env: "CLOUDBEAVER_DB_USER",
value: ""
},
password: {
env: "CLOUDBEAVER_DB_PASSWORD",
value: ""
},
initialDataConfiguration: {
env: "CLOUDBEAVER_DB_INITIAL_DATA",
value: "conf/initial-data.conf"
},
pool: {
minIdleConnections: {
env: "CLOUDBEAVER_DB_MIN_IDLE_CONNECTIONS",
value: 4
},
maxIdleConnections: {
env: "CLOUDBEAVER_DB_MAX_IDLE_CONNECTIONS",
value: 10
},
maxConnections: {
env: "CLOUDBEAVER_DB_MAX_CONNECTIONS",
value: 100
},
validationQuery: {
env: "CLOUDBEAVER_DB_VALIDATION_QUERY",
value: "SELECT 1"
}
},
backupEnabled: {
env: "CLOUDBEAVER_DB_BACKUP_ENABLED",
value: true
}
}
},
app: {
anonymousAccessEnabled: {
env: "CLOUDBEAVER_APP_ANONYMOUS_ACCESS_ENABLED",
value: true
},
anonymousUserRole: {
env: "CLOUDBEAVER_APP_ANONYMOUS_USER_ROLE",
value: "user"
},
defaultUserTeam: {
env: "CLOUDBEAVER_APP_DEFAULT_USER_TEAM",
value: "user"
},
grantConnectionsAccessToAnonymousTeam: {
env: "CLOUDBEAVER_APP_GRANT_CONNECTIONS_ACCESS_TO_ANONYMOUS_TEAM",
value: false
},
supportsCustomConnections: {
env: "CLOUDBEAVER_APP_SUPPORTS_CUSTOM_CONNECTIONS",
value: false
},
showReadOnlyConnectionInfo: {
env: "CLOUDBEAVER_APP_READ_ONLY_CONNECTION_INFO",
value: false
},
systemVariablesResolvingEnabled: {
env: "CLOUDBEAVER_SYSTEM_VARIABLES_RESOLVING_ENABLED",
value: false
},
forwardProxy: {
env: "CLOUDBEAVER_APP_FORWARD_PROXY",
value: false
},
publicCredentialsSaveEnabled: {
env: "CLOUDBEAVER_APP_PUBLIC_CREDENTIALS_SAVE_ENABLED",
value: true
},
adminCredentialsSaveEnabled: {
env: "CLOUDBEAVER_APP_ADMIN_CREDENTIALS_SAVE_ENABLED",
value: true
},
resourceManagerEnabled: {
env: "CLOUDBEAVER_APP_RESOURCE_MANAGER_ENABLED",
value: true
},
resourceQuotas: {
resourceManagerFileSizeLimit: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_RESOURCE_MANAGER_FILE_SIZE_LIMIT",
value: 500000
},
sqlMaxRunningQueries: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_MAX_RUNNING_QUERIES",
value: 100
},
sqlResultSetRowsLimit: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_RESULT_SET_ROWS_LIMIT",
value: 100000
},
sqlTextPreviewMaxLength: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_TEXT_PREVIEW_MAX_LENGTH",
value: 4096
},
sqlBinaryPreviewMaxLength: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_BINARY_PREVIEW_MAX_LENGTH",
value: 261120
}
},
enabledAuthProviders: ["local"],
disabledBetaFeatures: []
}
}
+1 -3
View File
@@ -18,6 +18,7 @@ cd ../..
echo "Pull dbeaver platform"
[ ! -d dbeaver ] && git clone --depth 1 https://github.com/dbeaver/dbeaver.git
[ ! -d dbeaver-common ] && git clone --depth 1 https://github.com/dbeaver/dbeaver-common.git
[ ! -d dbeaver-jdbc-libsql ] && git clone --depth 1 https://github.com/dbeaver/dbeaver-jdbc-libsql.git
cd cloudbeaver/deploy
@@ -31,9 +32,6 @@ if [[ "$?" -ne 0 ]] ; then
fi
cd ../../../deploy
echo "Generate cloudbeaver.conf file"
mvn -f ../apps/config-generator compile exec:java -Dconfig.output="cloudbeaver/conf/cloudbeaver.conf"
echo "Copy server packages"
cp -rp ../server/product/web-server/target/products/io.cloudbeaver.product/all/all/all/* ./cloudbeaver/server
+1 -3
View File
@@ -20,6 +20,7 @@ echo Pull dbeaver platform
IF NOT EXIST dbeaver git clone https://github.com/dbeaver/dbeaver.git
IF NOT EXIST dbeaver-common git clone https://github.com/dbeaver/dbeaver-common.git
IF NOT EXIST dbeaver-jdbc-libsql git clone https://github.com/dbeaver/dbeaver-jdbc-libsql.git
cd cloudbeaver\deploy
@@ -42,9 +43,6 @@ copy ..\config\DefaultConfiguration\GlobalConfiguration\.dbeaver\data-sources.js
move drivers cloudbeaver >NUL
echo Generate cloudbeaver.conf file
call mvn -f ..\apps\config-generator compile exec:java -Dconfig.output="cloudbeaver\conf\cloudbeaver.conf" || goto :error
echo "Build static content"
mkdir .\cloudbeaver\web
+2 -3
View File
@@ -1,5 +1,4 @@
# Ubuntu 24.04 (Noble Numbat)
FROM ubuntu:noble
FROM ubuntu:24.04
MAINTAINER DBeaver Corp, devops@dbeaver.com
@@ -32,7 +31,7 @@ RUN set -eux; \
ENV LANG=en_US.UTF-8
ENV JAVA_HOME=/opt/java/openjdk
COPY --from=eclipse-temurin:25-jdk-noble $JAVA_HOME $JAVA_HOME
COPY --from=eclipse-temurin:21 $JAVA_HOME $JAVA_HOME
ENV PATH="${JAVA_HOME}/bin:${PATH}"
RUN set -eux; \
+3 -3
View File
@@ -13,7 +13,7 @@ if [ "$(id -u)" -eq 0 ]; then
chown -R $DBEAVER_UID:$DBEAVER_GID $PWD/workspace
# Execute run-cloudbeaver-server.sh as the dbeaver user with the JAVA_HOME and PATH environment variables
exec su "$TARGET_USER" -c "JAVA_HOME=$JAVA_HOME PATH=$PATH ./run-cloudbeaver-server.sh $*"
exec su "$TARGET_USER" -c "JAVA_HOME=$JAVA_HOME PATH=$PATH ./run-cloudbeaver-server.sh"
else
exec ./run-cloudbeaver-server.sh "$@"
fi
exec ./run-cloudbeaver-server.sh
fi
+1 -1
View File
@@ -35,7 +35,7 @@ exec java ${JAVA_OPTS} \
--add-opens=java.sql/java.sql=ALL-UNNAMED \
-jar ${launcherJar} \
-product io.cloudbeaver.product.ce.product \
"$@" \
-web-config conf/cloudbeaver.conf \
"$@" \
-nl en \
-registryMultiLanguage
+1
View File
@@ -1,2 +1,3 @@
dbeaver-common
dbeaver-jdbc-libsql
dbeaver
@@ -1,13 +0,0 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Model
Bundle-SymbolicName: io.cloudbeaver.model.cli;singleton:=true
Bundle-Version: 1.0.13.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.model.cli;visibility:=reexport
Export-Package: io.cloudbeaver.model.cli
Automatic-Module-Name: io.cloudbeaver.model.cli
Import-Package: org.slf4j
@@ -1,5 +0,0 @@
source.. = src/
output.. = target/classes/
bin.includes = .,\
META-INF/,\
plugin.xml
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
</plugin>
@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.cloudbeaver</groupId>
<artifactId>bundles</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.model.cli</artifactId>
<version>1.0.13-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -1,66 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.ApplicationCommandLine;
import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
import org.jkiss.dbeaver.model.cli.CLIContextImpl;
import org.jkiss.dbeaver.model.cli.CLIRunMeta;
import org.jkiss.dbeaver.model.cli.registry.CLICommandDescriptor;
import picocli.CommandLine;
import java.util.List;
public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationInstanceController> {
private final CloudBeaverMixin mixin;
public CloudBeaverCommandLine() {
super();
this.mixin = new CloudBeaverMixin();
}
public CloudBeaverCommandLine(
@NotNull CloudBeaverMixin mixin
) {
super();
this.mixin = mixin;
}
@Override
protected CloudBeaverTopLevelCommand createTopLevelCommand(
@Nullable ApplicationInstanceController applicationInstanceController,
@NotNull CLIContextImpl context,
@NotNull CLIRunMeta runMeta
) {
return new CloudBeaverTopLevelCommand(applicationInstanceController, context, runMeta);
}
@NotNull
@Override
protected CommandLine initCommandLine(
@Nullable ApplicationInstanceController applicationInstanceController,
@NotNull CLIContextImpl context,
@NotNull CLIRunMeta runMeta,
@NotNull List<CLICommandDescriptor> commandsToExecute
) {
var cmd = super.initCommandLine(applicationInstanceController, context, runMeta, commandsToExecute);
cmd.addMixin("cloudbeaver", mixin);
return cmd;
}
}
@@ -1,34 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.dbeaver.model.cli.model.NonExecutableOption;
import picocli.CommandLine;
/**
* CloudBeaver specific mixin options
*/
public class CloudBeaverMixin {
@NonExecutableOption
@CommandLine.Option(
names = {"-web-config"},
arity = "1",
hidden = true,
scope = CommandLine.ScopeType.INHERIT
)
private String webConfig;
}
@@ -1,46 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.CLIContextImpl;
import org.jkiss.dbeaver.model.cli.CLIRunMeta;
import org.jkiss.dbeaver.model.cli.command.AbstractTopLevelCommand;
import org.jkiss.dbeaver.model.cli.model.NonExecutableOption;
import picocli.CommandLine;
@CommandLine.Command(name = "cbvr", description = "CloudBeaver commands")
public class CloudBeaverTopLevelCommand extends AbstractTopLevelCommand {
@NonExecutableOption
@CommandLine.Option(
names = {NOSPASH_OPTION},
hidden = true,
scope = CommandLine.ScopeType.INHERIT
)
private boolean noSplash;
protected CloudBeaverTopLevelCommand(
@Nullable ApplicationInstanceController controller,
@NotNull CLIContextImpl context,
@NotNull CLIRunMeta meta
) {
super(controller, context, meta);
}
}
@@ -3,20 +3,21 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Model
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
Bundle-Version: 1.0.104.qualifier
Bundle-Release-Date: 20260706
Bundle-Version: 1.0.87.qualifier
Bundle-Release-Date: 20251020
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
org.jkiss.dbeaver.model;visibility:=reexport,
org.jkiss.dbeaver.model.cli;visibility:=reexport,
org.jkiss.dbeaver.model.sm;visibility:=reexport,
org.jkiss.dbeaver.model.event;visibility:=reexport,
org.jkiss.dbeaver.model.nio;visibility:=reexport,
org.jkiss.dbeaver.registry;visibility:=reexport,
org.jkiss.dbeaver.net.ssh,
org.jkiss.bundle.logback;visibility:=reexport,
org.jkiss.bundle.graphql;visibility:=reexport,
org.jkiss.bundle.apache.dbcp,
com.google.gson;visibility:=reexport,
jakarta.servlet-api;bundle-version:="6.0.0";visibility:=reexport
Export-Package: io.cloudbeaver,
io.cloudbeaver.auth,
@@ -25,21 +26,17 @@ Export-Package: io.cloudbeaver,
io.cloudbeaver.auth.provider.local,
io.cloudbeaver.auth.provisioning,
io.cloudbeaver.websocket,
io.cloudbeaver.websocket.event.client,
io.cloudbeaver.websocket.event.task,
io.cloudbeaver.model,
io.cloudbeaver.model.api.annotation,
io.cloudbeaver.model.api.annotation.enums,
io.cloudbeaver.model.api.annotation.media,
io.cloudbeaver.model.api.annotation.responses,
io.cloudbeaver.model.api.annotation.tags,
io.cloudbeaver.model.app,
io.cloudbeaver.model.config,
io.cloudbeaver.model.apilog,
io.cloudbeaver.model.cli,
io.cloudbeaver.model.events,
io.cloudbeaver.model.fs,
io.cloudbeaver.model.log,
io.cloudbeaver.model.rm,
io.cloudbeaver.model.rm.local,
io.cloudbeaver.model.rm.lock,
io.cloudbeaver.model.session,
io.cloudbeaver.model.user,
io.cloudbeaver.model.utils,
@@ -25,34 +25,4 @@
label="Default secret controller" description="Default void secret controller"/>
</extension>
<extension point="io.cloudbeaver.feature">
<feature id="ssh" label="SSH tunnels for databases" description="Enables SSH tunnels for secure database connections, allowing access to remote databases through encrypted channels" enabledByDefault="true"/>
</extension>
<extension point="org.jkiss.dbeaver.ws.event">
<event topicId="cb_session_task" id="cb_session_task_info_updated"
class="io.cloudbeaver.websocket.event.task.WSSessionTaskInfoEvent"/>
<event topicId="cb_session_task" id="cb_session_task_confirmation_request"
class="io.cloudbeaver.websocket.event.task.WSSessionTaskConfirmationRequestEvent"/>
<event topicId="cb_session_task" id="cb_session_task_query_confirmation_request"
class="io.cloudbeaver.websocket.event.task.WSSessionTaskQueryConfirmationRequestEvent"/>
<event topicId="cb_session_task" id="cb_session_task_query_params_confirmation_request"
class="io.cloudbeaver.websocket.event.task.WSSessionTaskQueryParamsConfirmationEvent"/>
<clientEvent id="cb_client_session_ping" class="io.cloudbeaver.websocket.event.client.WSSessionPingClientEvent"/>
<clientEvent id="cb_client_topic_subscribe" class="io.cloudbeaver.websocket.event.client.WSSubscribeOnTopicClientEvent"/>
<clientEvent id="cb_client_topic_unsubscribe"
class="io.cloudbeaver.websocket.event.client.WSUnsubscribeFromTopicClientEvent"/>
<clientEvent id="cb_client_projects_active"
class="io.cloudbeaver.websocket.event.client.WSUpdateActiveProjectsClientEvent"/>
<clientEvent id="cb_client_session_task_confirmation"
class="io.cloudbeaver.websocket.event.client.WSSessionTaskConfirmationEvent"/>
<clientEvent id="cb_client_session_task_with_parameters_confirmation"
class="io.cloudbeaver.websocket.event.client.WSSessionTaskWithParametersConfirmationEvent"/>
<clientEvent id="cb_client_cancel_action"
class="io.cloudbeaver.websocket.event.client.WSSessionCancelActionClientEvent"/>
</extension>
</plugin>
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.model</artifactId>
<version>1.0.104-SNAPSHOT</version>
<version>1.0.87-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -146,8 +146,6 @@ public class DBWebException extends DBException implements GraphQLError {
} else {
return message;
}
} else if (cause.getMessage() == null && cause.getCause() != null) {
return message + ":\n" + cause.getCause().getClass().getSimpleName();
}
if (CommonUtils.isEmpty(message)) {
if (cause.getMessage() != null) {
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package io.cloudbeaver;
import io.cloudbeaver.model.WebConnectionConfig;
import io.cloudbeaver.model.app.WebAppConfiguration;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.utils.ServletAppUtils;
import io.cloudbeaver.utils.WebDataSourceUtils;
import org.jkiss.code.NotNull;
@@ -27,17 +26,14 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
import org.jkiss.dbeaver.registry.DataSourcePreferenceStore;
import org.jkiss.utils.CommonUtils;
public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C extends DataSourceDescriptor> {
private static final Log log = Log.getLog(WebConnectionConfigInputHandler.class);
protected final T input;
protected final DBPDataSourceRegistry registry;
protected final WebSession webSession;
public WebConnectionConfigInputHandler(@NotNull WebSession webSession, @NotNull DBPDataSourceRegistry registry, T configInput) {
this.webSession = webSession;
public WebConnectionConfigInputHandler(@NotNull DBPDataSourceRegistry registry, T configInput) {
this.registry = registry;
this.input = configInput;
}
@@ -56,7 +52,6 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
}
WebDataSourceUtils.saveAuthProperties(
webSession.getProgressMonitor(),
newDataSource,
newDataSource.getConnectionConfiguration(),
input.getCredentials(),
@@ -78,7 +73,7 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
dataSource.setFolder(input.getFolder() != null ? registry.getFolder(input.getFolder()) : null);
if (input.isDefaultAutoCommit() != null) {
dataSource.getConnectionConfiguration().getBootstrap().setDefaultAutoCommit(input.isDefaultAutoCommit());
dataSource.setDefaultAutoCommit(input.isDefaultAutoCommit());
}
WebDataSourceUtils.setConnectionConfiguration(
dataSource.getDriver(),
@@ -86,7 +81,6 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
input
);
WebDataSourceUtils.saveAuthProperties(
webSession.getProgressMonitor(),
dataSource,
dataSource.getConnectionConfiguration(),
input.getCredentials(),
@@ -94,9 +88,6 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
input.isSharedCredentials()
);
dataSource.setConnectionReadOnly(input.isReadOnly());
DataSourcePreferenceStore preferenceStore = dataSource.getPreferenceStore();
preferenceStore.clear();
preferenceStore.setProperties(input.getDefaultUserPreferences());
}
@NotNull
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* 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.
@@ -17,7 +17,6 @@
package io.cloudbeaver;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.utils.WebEventUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBPEvent;
@@ -47,7 +46,7 @@ public class WebDataSourceConnectEventListener implements DBPEventListener {
new WSDataSourceConnectEvent(
container.getProject().getId(),
container.getId(),
WebEventUtils.getSmSessionId(webSession),
webSession.getSessionId(),
webSession.getUserId()
)
);
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* 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.
@@ -30,7 +30,6 @@ import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.net.DBWNetworkProfile;
import org.jkiss.dbeaver.model.net.DBWNetworkProfileManager;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.secret.DBSSecretController;
@@ -259,12 +258,28 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
dataSourceRegistry.removeSavedFilter(filterName);
}
@Nullable
@Override
public DBWNetworkProfile getNetworkProfile(@Nullable String source, @NotNull String name) {
return dataSourceRegistry.getNetworkProfile(source, name);
}
@NotNull
@Override
public DBWNetworkProfileManager getNetworkProfiles() {
public List<DBWNetworkProfile> getNetworkProfiles() {
return dataSourceRegistry.getNetworkProfiles();
}
@Override
public void updateNetworkProfile(@NotNull DBWNetworkProfile profile) {
dataSourceRegistry.updateNetworkProfile(profile);
}
@Override
public void removeNetworkProfile(@NotNull DBWNetworkProfile profile) {
dataSourceRegistry.removeNetworkProfile(profile);
}
@Nullable
@Override
public DBAAuthProfile getAuthProfile(@NotNull String id) {
@@ -362,12 +377,6 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
return dataSourceRegistry.getPreferenceStore();
}
@Override
public void initializeDataSources() {
dataSourceRegistry.initializeDataSources();
}
@NotNull
@Override
public DataSourceParseResults loadDataSources(
@NotNull List<DBPDataSourceConfigurationStorage> storages,
@@ -383,7 +392,6 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
dataSourceRegistry.saveDataSources();
}
@NotNull
@Override
public DataSourceConfigurationManager getConfigurationManager() {
return dataSourceRegistry.getConfigurationManager();
@@ -399,12 +407,12 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
}
@Override
public void persistSecrets(@NotNull DBSSecretController secretController) throws DBException {
public void persistSecrets(DBSSecretController secretController) throws DBException {
dataSourceRegistry.persistSecrets(secretController);
}
@Override
public void resolveSecrets(@NotNull DBSSecretController secretController) throws DBException {
public void resolveSecrets(DBSSecretController secretController) throws DBException {
dataSourceRegistry.resolveSecrets(secretController);
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,8 +23,8 @@ import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBPEvent;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.security.user.SMObjectPermissions;
import java.util.Set;
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* 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.
@@ -24,46 +24,37 @@ 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.DBPAdaptable;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBPObjectSettingsProvider;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistryCache;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.navigator.DBNModel;
import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.rm.RMUtils;
import org.jkiss.dbeaver.model.security.SMControllerUtils;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
import org.jkiss.dbeaver.registry.DataSourceRegistry;
import org.jkiss.dbeaver.registry.project.BaseProjectSettings;
import org.jkiss.dbeaver.runtime.jobs.DisconnectJob;
import org.jkiss.utils.CommonUtils;
import java.nio.file.Path;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptable {
public class WebSessionProjectImpl extends WebProjectImpl {
private static final Log log = Log.getLog(WebSessionProjectImpl.class);
protected final WebSession webSession;
private final Map<String, WebConnectionInfo> connections = new HashMap<>();
private final BaseProjectSettings projectSettings;
private final Map<String, Object> projectSettings = new ConcurrentHashMap<>();
private boolean registryIsLoaded = false;
public WebSessionProjectImpl(
@NotNull WebSession webSession,
@NotNull RMProject project
) {
this(webSession, project, null);
}
public WebSessionProjectImpl(
@NotNull WebSession webSession,
@NotNull RMProject project,
@Nullable Path path
) {
super(
webSession.getWorkspace(),
@@ -71,36 +62,25 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
webSession.getSessionContext(),
project,
webSession.getUserPreferenceStore(),
path == null ? RMUtils.getProjectPath(project) : path
RMUtils.getProjectPath(project)
);
this.webSession = webSession;
this.projectSettings = new BaseProjectSettings(this) {
@NotNull
@Override
protected Map<SMObjectType, Map<String, Map<String, String>>> loadAllProjectSettings() throws DBException {
if (webSession.getUser() == null) {
return new LinkedHashMap<>();
}
return SMControllerUtils.getObjectSettingsMap(WebSessionProjectImpl.this, webSession.getSecurityController());
}
@Override
protected void saveProjectSettings(
@NotNull SMObjectType objectType,
@NotNull String objectId,
@NotNull Map<String, String> settings
) throws DBException {
if (webSession.getUserContext().isNonAnonymousUserAuthorizedInSM()) {
webSession.getSecurityController().setObjectSettings(getId(), objectType, objectId, settings);
}
}
};
}
@NotNull
public BaseProjectSettings getProjectSettings() {
return projectSettings;
public WebSessionProjectImpl(
@NotNull WebSession webSession,
@NotNull RMProject project,
@NotNull Path path
) {
super(
webSession.getWorkspace(),
webSession.getRmController(),
webSession.getSessionContext(),
project,
webSession.getUserPreferenceStore(),
path
);
this.webSession = webSession;
}
@Nullable
@@ -109,14 +89,6 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
return webSession.getNavigatorModel();
}
@Override
public <T> T getAdapter(@NotNull Class<T> adapter) {
if (adapter == DBPObjectSettingsProvider.class) {
return adapter.cast(projectSettings);
}
return null;
}
@NotNull
@Override
protected DBPDataSourceRegistry createDataSourceRegistry() {
@@ -218,7 +190,6 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
*
* @return connections from cache.
*/
@NotNull
public List<WebConnectionInfo> getConnections() {
if (!registryIsLoaded) {
addDataSourcesToCache();
@@ -252,14 +223,14 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
}
case WSDataSourceEvent.UPDATED -> {
if (event.getProperty() == WSDataSourceProperty.CONFIGURATION) {
WebDataSourceUtils.disconnectDataSource(webSession, ds, true);
WebDataSourceUtils.disconnectDataSource(webSession, ds);
}
if (event.getProperty() != WSDataSourceProperty.INTERNAL) {
sendDataSourceUpdatedEvent = true;
}
}
case WSDataSourceEvent.DELETED -> {
WebDataSourceUtils.disconnectDataSource(webSession, ds, false);
WebDataSourceUtils.disconnectDataSource(webSession, ds);
if (registry instanceof DBPDataSourceRegistryCache dsrc) {
dsrc.removeDataSourceFromList(ds);
}
@@ -273,6 +244,20 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
return sendDataSourceUpdatedEvent;
}
public void refreshProjectSettings() throws DBException {
if (webSession.getUser() == null) {
projectSettings.clear();
return;
}
Map<String, Object> loadedSettings = webSession.getSecurityController().getObjectSettings(
getId(),
SMObjectType.project,
null
);
projectSettings.clear();
projectSettings.putAll(loadedSettings);
}
@NotNull
public WebConnectionInfo createConnectionInfo(@NotNull DBPDataSourceContainer dataSourceDescriptor) {
return new WebConnectionInfo(webSession, dataSourceDescriptor);
@@ -283,7 +268,7 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
if (CommonUtils.isEmpty(configMap)) {
throw new DBWebException("Connection configuration parameters are missing");
}
DBPDataSourceContainer newDataSource = getDataSourceContainerFromInput(getConnectionConfigInput(configMap));
DBPDataSourceContainer newDataSource = getDataSourceContainerFromInput(configMap);
return addDataSourceToProject(newDataSource);
}
@@ -317,7 +302,7 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
webSession.addInfoMessage("Update connection - " + WebDataSourceUtils.getConnectionContainerInfo(dataSource));
DBPDataSourceRegistry registry = getDataSourceRegistry();
getInputConfigHandler(config).updateDataSource(dataSource);
getInputConfigHandler(configMap).updateDataSource(dataSource);
connectionInfo.setCredentialsSavedInSession(null);
try {
registry.updateDataSource(dataSource);
@@ -345,16 +330,26 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
@NotNull
private WebConnectionInfo closeAndDeleteConnection(@NotNull WebConnectionInfo connectionInfo) throws DBWebException {
DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer();
WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer, false);
boolean disconnected = WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer);
DBPDataSourceRegistry registry = getDataSourceRegistry();
registry.removeDataSource(dataSourceContainer);
try {
registry.checkForErrors();
} catch (DBException e) {
try {
registry.addDataSource(dataSourceContainer);
} catch (DBException ex) {
log.error("Error re-adding after delete attempt", e);
}
throw new DBWebException("Failed to delete connection", e);
}
removeConnection(dataSourceContainer);
return connectionInfo;
}
@NotNull
public DataSourceDescriptor getDataSourceContainerFromInput(@NotNull WebConnectionConfig configInput) throws DBWebException {
return getInputConfigHandler(configInput).createDataSourceContainer();
public DataSourceDescriptor getDataSourceContainerFromInput(@NotNull Map<String, Object> configMap) throws DBWebException {
return getInputConfigHandler(configMap).createDataSourceContainer();
}
@NotNull
@@ -363,8 +358,8 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
}
@NotNull
protected WebConnectionConfigInputHandler getInputConfigHandler(@NotNull WebConnectionConfig configInput) {
return new WebConnectionConfigInputHandler<>(webSession, getDataSourceRegistry(), configInput);
protected WebConnectionConfigInputHandler getInputConfigHandler(@NotNull Map<String, Object> configMap) {
return new WebConnectionConfigInputHandler<>(getDataSourceRegistry(), getConnectionConfigInput(configMap));
}
@@ -74,8 +74,4 @@ public interface SMAuthProviderExternal<AUTH_SESSION extends SMSession> extends
*/
default void postAuthentication() {}
@Override
default boolean supportsOpeningSessionAsChild() {
return true;
}
}
@@ -1,40 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.utils.WebEventUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.runtime.AbstractJob;
public abstract class AbstractCancelableJob extends AbstractJob implements CustomCancelableJob {
protected AbstractCancelableJob(@NotNull String name) {
super(name);
}
@Override
public void cancelJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo taskInfo) {
cancelJob(webSession, taskInfo, "Canceled by the user");
}
public void cancelJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo taskInfo, @NotNull String errorMessage) {
taskInfo.setRunning(false);
taskInfo.setJobError(new DBException(errorMessage));
WebEventUtils.sendAsyncTaskEvent(webSession, taskInfo);
}
}
@@ -1,19 +1,3 @@
/*
* 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;
import org.jkiss.code.NotNull;
@@ -1,50 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model;
import org.jkiss.code.NotNull;
public class WebBasicEnumObjectInfo {
@NotNull
private String displayName;
@NotNull
private String value;
public WebBasicEnumObjectInfo(@NotNull String displayName, @NotNull String value) {
this.displayName = displayName;
this.value = value;
}
@NotNull
public String getDisplayName() {
return displayName;
}
public void setDisplayName(@NotNull String displayName) {
this.displayName = displayName;
}
@NotNull
public String getValue() {
return value;
}
public void setValue(@NotNull String value) {
this.value = value;
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@ import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.meta.Property;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -43,7 +42,7 @@ public class WebConnectionConfig {
private String databaseName;
private String url;
private Integer keepAliveInterval;
private int keepAliveInterval;
private String name;
private String description;
@@ -54,19 +53,17 @@ public class WebConnectionConfig {
private String authModelId;
private Map<String, Object> credentials;
private Boolean saveCredentials;
private Boolean sharedCredentials;
private boolean saveCredentials;
private boolean sharedCredentials;
private Map<String, Object> mainPropertyValues;
private Map<String, Object> expertSettingsValues;
private Map<String, Object> providerProperties;
private List<WebNetworkHandlerConfigInput> networkHandlersConfig;
private DBPDriverConfigurationType configurationType;
private String selectedSecretId;
private Boolean defaultAutoCommit;
private boolean defaultAutoCommit;
private String defaultCatalogName;
private String defaultSchemaName;
@NotNull
private Map<String, String> defaultUserPreferences = new LinkedHashMap<>();
public WebConnectionConfig() {
}
@@ -114,8 +111,6 @@ public class WebConnectionConfig {
String configType = JSONUtils.getString(params, "configurationType");
configurationType = configType == null ? null : DBPDriverConfigurationType.valueOf(configType);
Map<String, Object> stringObjectUserPrefMap = JSONUtils.getObject(params, "defaultUserPreferences");
stringObjectUserPrefMap.forEach((key, value) -> defaultUserPreferences.put(key, value.toString()));
networkHandlersConfig = new ArrayList<>();
for (Map<String, Object> nhc : JSONUtils.getObjectList(params, "networkHandlersConfig")) {
networkHandlersConfig.add(new WebNetworkHandlerConfigInput(nhc));
@@ -127,178 +122,102 @@ public class WebConnectionConfig {
return connectionId;
}
public void setConnectionId(String connectionId) {
this.connectionId = connectionId;
}
@Property
public String getDriverId() {
return driverId;
}
public void setDriverId(String driverId) {
this.driverId = driverId;
}
@Property
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
@Property
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Property
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Property
public String getFolder() {
return folder;
}
public void setFolder(String folder) {
this.folder = folder;
}
@Property
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
@Property
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
@Property
public String getServerName() {
return serverName;
}
public void setServerName(String serverName) {
this.serverName = serverName;
}
@Property
public String getDatabaseName() {
return databaseName;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
@Property
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Property
public Map<String, Object> getProperties() {
return properties;
}
public void setProperties(Map<String, Object> properties) {
this.properties = properties;
}
@Property
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Property
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
@Property
public String getAuthModelId() {
return authModelId;
}
public void setAuthModelId(String authModelId) {
this.authModelId = authModelId;
}
@Property
public DBPDriverConfigurationType getConfigurationType() {
return configurationType;
}
public void setConfigurationType(DBPDriverConfigurationType configurationType) {
this.configurationType = configurationType;
}
@Property
public Map<String, Object> getCredentials() {
return credentials;
}
public void setCredentials(Map<String, Object> credentials) {
this.credentials = credentials;
}
public List<WebNetworkHandlerConfigInput> getNetworkHandlersConfig() {
return networkHandlersConfig;
}
public void setNetworkHandlersConfig(List<WebNetworkHandlerConfigInput> networkHandlersConfig) {
this.networkHandlersConfig = networkHandlersConfig;
}
@Property
public boolean isSaveCredentials() {
return saveCredentials;
}
public void setSaveCredentials(boolean saveCredentials) {
this.saveCredentials = saveCredentials;
}
@Property
public boolean isSharedCredentials() {
return sharedCredentials;
}
public void setSharedCredentials(boolean sharedCredentials) {
this.sharedCredentials = sharedCredentials;
public void setSaveCredentials(boolean saveCredentials) {
this.saveCredentials = saveCredentials;
}
@Property
@@ -306,79 +225,38 @@ public class WebConnectionConfig {
return mainPropertyValues;
}
public void setMainPropertyValues(Map<String, Object> mainPropertyValues) {
this.mainPropertyValues = mainPropertyValues;
}
@Property
public Map<String, Object> getExpertSettingsValues() {
return expertSettingsValues;
}
public void setExpertSettingsValues(Map<String, Object> expertSettingsValues) {
this.expertSettingsValues = expertSettingsValues;
}
@Property
public Map<String, Object> getProviderProperties() {
return providerProperties;
}
public void setProviderProperties(Map<String, Object> providerProperties) {
this.providerProperties = providerProperties;
}
@Property
public Integer getKeepAliveInterval() {
return keepAliveInterval;
}
public void setKeepAliveInterval(int keepAliveInterval) {
this.keepAliveInterval = keepAliveInterval;
}
@Property
public Boolean isDefaultAutoCommit() {
return defaultAutoCommit;
}
public void setDefaultAutoCommit(boolean defaultAutoCommit) {
this.defaultAutoCommit = defaultAutoCommit;
}
@Nullable
public String getSelectedSecretId() {
return selectedSecretId;
}
public void setSelectedSecretId(String selectedSecretId) {
this.selectedSecretId = selectedSecretId;
}
@Property
public String getDefaultCatalogName() {
return defaultCatalogName;
}
public void setDefaultCatalogName(String defaultCatalogName) {
this.defaultCatalogName = defaultCatalogName;
}
@Property
public String getDefaultSchemaName() {
return defaultSchemaName;
}
public void setDefaultSchemaName(String defaultSchemaName) {
this.defaultSchemaName = defaultSchemaName;
}
@NotNull
public Map<String, String> getDefaultUserPreferences() {
return defaultUserPreferences;
}
public void setDefaultUserPreferences(@NotNull Map<String, String> defaultUserPreferences) {
this.defaultUserPreferences = defaultUserPreferences;
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebProjectImpl;
import io.cloudbeaver.model.app.BaseWebAppConfiguration;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.service.security.SMUtils;
import io.cloudbeaver.service.sql.WebDataFormat;
import io.cloudbeaver.utils.CBModelConstants;
@@ -39,15 +38,12 @@ import org.jkiss.dbeaver.model.connection.DBPDriverConfigurationType;
import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNative;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
import org.jkiss.dbeaver.model.navigator.DBNDatabaseNode;
import org.jkiss.dbeaver.model.navigator.DBNUtils;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
import org.jkiss.dbeaver.model.navigator.DBNDataSource;
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
import org.jkiss.dbeaver.model.preferences.DBPPropertySource;
import org.jkiss.dbeaver.model.rm.RMConstants;
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
import org.jkiss.dbeaver.model.runtime.DBRRunnableParametrized;
import org.jkiss.dbeaver.registry.DataSourcePreferenceStore;
import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor;
import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
@@ -63,6 +59,7 @@ import java.util.stream.Collectors;
public class WebConnectionInfo {
private static final Log log = Log.getLog(WebConnectionInfo.class);
public static final String SECURED_VALUE = "********";
private static final String FEATURE_HAS_TOOLS = "hasTools";
private static final String FEATURE_CONNECTED = "connected";
@@ -130,7 +127,7 @@ public class WebConnectionInfo {
if (canViewReadOnlyConnections()) {
return dataSourceContainer.getConnectionConfiguration().getHostName();
}
return CBConstants.SECURED_VALUE;
return SECURED_VALUE;
}
@Property
@@ -138,7 +135,7 @@ public class WebConnectionInfo {
if (canViewReadOnlyConnections()) {
return dataSourceContainer.getConnectionConfiguration().getHostPort();
}
return CBConstants.SECURED_VALUE;
return SECURED_VALUE;
}
@Property
@@ -151,7 +148,7 @@ public class WebConnectionInfo {
if (canViewReadOnlyConnections()) {
return dataSourceContainer.getConnectionConfiguration().getDatabaseName();
}
return CBConstants.SECURED_VALUE;
return SECURED_VALUE;
}
@Property
@@ -159,7 +156,7 @@ public class WebConnectionInfo {
if (canViewReadOnlyConnections()) {
return dataSourceContainer.getConnectionConfiguration().getUrl();
}
return CBConstants.SECURED_VALUE;
return SECURED_VALUE;
}
@Property
@@ -214,8 +211,7 @@ public class WebConnectionInfo {
@Property
public String getNodePath() {
DBNDatabaseNode dsNode = DBNUtils.getNodeByObject(dataSourceContainer);
return dsNode == null ? null : dsNode.getNodeUri();
return DBNDataSource.makeDataSourceItemPath(dataSourceContainer);
}
@Property
@@ -291,22 +287,6 @@ public class WebConnectionInfo {
return dataSourceContainer.getNavigatorSettings();
}
@Property
@NotNull
public DBNBrowseSettings getDefaultNavigatorSettings() {
return dataSourceContainer.getNavigatorSettings().getOriginalSettings();
}
@Property
@NotNull
public Map<String, String> getDefaultUserPreferences() {
DBPPreferenceStore preferenceStore = dataSourceContainer.getPreferenceStore();
if (preferenceStore instanceof DataSourcePreferenceStore dataSourcePreferenceStore) {
return dataSourcePreferenceStore.getProperties();
}
return Collections.emptyMap();
}
@Property
@NotNull
public List<WebDataFormat> getSupportedDataFormats() {
@@ -361,16 +341,6 @@ public class WebConnectionInfo {
if (CommonUtils.isEmpty(authModelId)) {
authModelId = AuthModelDatabaseNative.ID;
}
// Patch auth model
DBPAuthModelDescriptor authModel = DBWorkbench.getPlatform().getDataSourceProviderRegistry().getAuthModel(authModelId);
if (authModel != null) {
DBPAuthModelDescriptor amReplace = authModel.getReplacedBy(dataSourceContainer.getDriver());
if (amReplace != null && amReplace != authModel) {
return amReplace.getId();
}
}
return authModelId;
}
@@ -592,5 +562,4 @@ public class WebConnectionInfo {
public void setCredentialsSavedInSession(@Nullable Boolean credentialsSavedInSession) {
this.credentialsSavedInSession = credentialsSavedInSession;
}
}
@@ -1,7 +1,6 @@
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;
@@ -53,35 +52,35 @@ public class WebExpertSettingsProperties implements DBPObject {
public static class KeepAliveIntervalFieldValidator implements IPropertyValueValidator<WebExpertSettingsProperties, Object> {
@Override
public boolean isValidValue(@NotNull WebExpertSettingsProperties object, @Nullable Object value) throws IllegalArgumentException {
public boolean isValidValue(WebExpertSettingsProperties object, Object value) throws IllegalArgumentException {
return CommonUtils.toBoolean(!object.driver.isEmbedded(), true);
}
}
public static class AutoCommitFieldValidator implements IPropertyValueValidator<WebExpertSettingsProperties, Object> {
@Override
public boolean isValidValue(@NotNull WebExpertSettingsProperties object, @Nullable Object value) throws IllegalArgumentException {
public boolean isValidValue(WebExpertSettingsProperties object, 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(@NotNull WebExpertSettingsProperties object, @Nullable Object value) throws IllegalArgumentException {
public boolean isValidValue(WebExpertSettingsProperties object, 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(@NotNull WebExpertSettingsProperties object, @Nullable Object value) throws IllegalArgumentException {
public boolean isValidValue(WebExpertSettingsProperties object, 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(@NotNull WebExpertSettingsProperties object, @Nullable Object value) throws IllegalArgumentException {
public boolean isValidValue(WebExpertSettingsProperties object, Object value) throws IllegalArgumentException {
return CommonUtils.toBoolean(object.driver.getDriverParameter(DBPDriverConstants.PARAM_SUPPORTS_SCHEMA_SELECTION), true);
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
*/
package io.cloudbeaver.model;
import io.cloudbeaver.server.CBConstants;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration;
import org.jkiss.dbeaver.model.net.DBWHandlerType;
@@ -89,9 +88,7 @@ public class WebNetworkHandlerConfig {
DBPPropertyDescriptor[] descriptor = configuration.getHandlerDescriptor().getHandlerProperties();
for (DBPPropertyDescriptor p : descriptor) {
if (p.hasFeature(RegistryConstants.ATTR_PASSWORD)) {
secureProperties.computeIfPresent(p.getId(), (k, v) ->
CommonUtils.isEmpty(v) ? null : CBConstants.SECURED_VALUE
);
secureProperties.computeIfPresent(p.getId(), (k, v) -> CommonUtils.isEmpty(v) ? null : WebConnectionInfo.SECURED_VALUE);
}
}
return secureProperties;
@@ -16,8 +16,6 @@
*/
package io.cloudbeaver.model;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.net.DBWHandlerType;
import org.jkiss.utils.CommonUtils;
@@ -30,14 +28,12 @@ import java.util.Map;
*/
public class WebNetworkHandlerConfigInput {
@NotNull
private final Map<String, Object> cfg;
public WebNetworkHandlerConfigInput(@NotNull Map<String, Object> cfg) {
public WebNetworkHandlerConfigInput(Map<String, Object> cfg) {
this.cfg = cfg;
}
@Nullable
public DBWHandlerType getType() {
return CommonUtils.valueOf(DBWHandlerType.class, JSONUtils.getString(cfg, "type"), null);
}
@@ -46,7 +42,6 @@ public class WebNetworkHandlerConfigInput {
return JSONUtils.getString(cfg, "id");
}
@Nullable
public Boolean isEnabled() {
if (cfg.containsKey("enabled")) {
return JSONUtils.getBoolean(cfg, "enabled");
@@ -67,13 +62,11 @@ public class WebNetworkHandlerConfigInput {
return JSONUtils.getString(cfg, "password");
}
// Deprecated. Use secured properties
@Nullable
@Deprecated // use secured properties
public String getKey() {
return JSONUtils.getString(cfg, "key");
}
@Nullable
public Boolean isSavePassword() {
if (cfg.containsKey("savePassword")) {
return JSONUtils.getBoolean(cfg, "savePassword");
@@ -82,12 +75,10 @@ public class WebNetworkHandlerConfigInput {
}
}
@Nullable
public Map<String, Object> getProperties() {
return JSONUtils.getObjectOrNull(cfg, "properties");
}
@Nullable
public Map<String, String> getSecureProperties() {
var secureProperties = JSONUtils.getObjectOrNull(cfg, "secureProperties");
if (secureProperties == null) {
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* 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.
@@ -17,13 +17,11 @@
package io.cloudbeaver.model;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBConstants;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.*;
import org.jkiss.dbeaver.model.connection.DBPDriverConfigurationType;
import org.jkiss.dbeaver.model.impl.PropertyDescriptor;
import org.jkiss.dbeaver.model.impl.ProviderPropertyDescriptor;
import org.jkiss.dbeaver.model.meta.IPropertyValueListProvider;
import org.jkiss.dbeaver.model.meta.Property;
@@ -32,7 +30,6 @@ import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
import org.jkiss.dbeaver.model.preferences.DBPPropertySource;
import org.jkiss.dbeaver.registry.settings.ProductSettingDescriptor;
import org.jkiss.dbeaver.runtime.properties.ObjectPropertyDescriptor;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;
import java.lang.reflect.Array;
@@ -139,14 +136,13 @@ public class WebPropertyInfo {
@Property
public Object getValue() throws DBException {
Object value = propertySource == null ? null : propertySource.getPropertyValue(session.getProgressMonitor(), property.getId());
if (property instanceof ObjectPropertyDescriptor opd &&
(!showProtected && opd.isPassword() || opd.isHidden())
) {
return maskValue(value);
} else if (property instanceof PropertyDescriptor pd) {
String[] features = pd.getFeatures();
if (features != null && ArrayUtils.contains(features, DBConstants.PROP_FEATURE_PASSWORD)) {
return maskValue(value);
if (property instanceof ObjectPropertyDescriptor) {
ObjectPropertyDescriptor opd = (ObjectPropertyDescriptor) property;
if (!showProtected && opd.isPassword() || opd.isHidden()) {
if (value == null || value.toString().isEmpty()) {
return "";
}
return "******";
}
}
return value == null ? null : makePropertyValue(value);
@@ -247,11 +243,9 @@ public class WebPropertyInfo {
List<Object> result = new ArrayList<>();
int length = Array.getLength(value);
for (int i = 0; i < length; i++) {
result.add(makePropertyValue(Array.get(value, i)));
result.add(Array.get(value, i));
}
return result;
} else if (value instanceof Enum<?> enumValue) {
return new WebBasicEnumObjectInfo(enumValue.toString(), enumValue.name());
}
Class<?> dataType = property.getDataType();
if (dataType == Boolean.class || dataType == Boolean.TYPE) {
@@ -311,13 +305,4 @@ public class WebPropertyInfo {
READ_ONLY
}
}
@NotNull
private static String maskValue(@Nullable Object value) {
if (value == null || value.toString().isEmpty()) {
return "";
} else {
return CBConstants.SECURED_VALUE;
}
}
}
@@ -1,41 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.api.annotation;
import io.cloudbeaver.model.api.annotation.responses.ApiResponse;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Operation {
String summary() default "";
String description() default "";
String operationId() default "";
String method() default "";
Parameter[] parameters() default {};
ApiResponse[] responses() default {};
}
@@ -1,51 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.api.annotation;
import io.cloudbeaver.model.api.annotation.enums.Explode;
import io.cloudbeaver.model.api.annotation.enums.ParameterIn;
import io.cloudbeaver.model.api.annotation.enums.ParameterStyle;
import io.cloudbeaver.model.api.annotation.media.ArraySchema;
import io.cloudbeaver.model.api.annotation.media.Schema;
import java.lang.annotation.*;
@Repeatable(Parameters.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
public @interface Parameter {
String name() default "";
String description() default "";
boolean required() default false;
String example() default "";
ParameterIn in() default ParameterIn.DEFAULT;
Schema schema() default @Schema;
ArraySchema array() default @ArraySchema;
ParameterStyle style() default ParameterStyle.DEFAULT;
Explode explode() default Explode.DEFAULT;
boolean hidden() default false;
}
@@ -1,29 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.api.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
public @interface Parameters {
Parameter[] value();
}
@@ -1,34 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.api.annotation.enums;
public enum Explode {
TRUE("true"),
FALSE("false"),
DEFAULT("default");
private final String value;
Explode(final String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
@@ -1,36 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.api.annotation.enums;
public enum ParameterIn {
DEFAULT("default"),
QUERY("query"),
HEADER("header"),
PATH("path"),
COOKIE("cookie");
private final String value;
ParameterIn(final String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
@@ -1,39 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.api.annotation.enums;
public enum ParameterStyle {
MATRIX("matrix"),
LABEL("label"),
FORM("form"),
SIMPLE("simple"),
SPACEDELIMITED("spaceDelimited"),
PIPEDELIMITED("pipeDelimited"),
DEEPOBJECT("deepObject"),
DEFAULT("default");
private final String value;
ParameterStyle(final String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
@@ -1,29 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.api.annotation.media;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
public @interface ArraySchema {
Schema schema() default @Schema;
}
@@ -1,29 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.api.annotation.media;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface Content {
Schema schema() default @Schema;
}
@@ -1,37 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.api.annotation.media;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
public @interface Schema {
String description() default "";
String example() default "";
String type() default "";
Class<?> implementation() default Void.class;
boolean required() default false;
}
@@ -1,35 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.api.annotation.responses;
import io.cloudbeaver.model.api.annotation.media.Content;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface ApiResponse {
String responseCode() default "default";
String description() default "";
Content[] content() default {};
}
@@ -1,31 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.api.annotation.tags;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Tag {
String name() default "";
String description() default "";
}
@@ -20,11 +20,13 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
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.fs.DBFUtils;
import org.jkiss.dbeaver.registry.fs.FileSystemProviderRegistry;
import org.jkiss.utils.CommonUtils;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
/**
@@ -63,20 +65,30 @@ public abstract class BaseServerConfigurationController<T extends ServletServerC
if (CommonUtils.isEmpty(workspaceLocation)) {
return defaultWorkspaceLocation;
}
try {
Path externalFsPath = DBFUtils.getPathFromURI(workspaceLocation);
if (externalFsPath == null) {
log.warn("Failed to detect workspace path from URI: " + workspaceLocation +
" default workspace location will be used");
URI workspaceUri = URI.create(workspaceLocation);
if (workspaceUri.getScheme() == null) {
// default filesystem
return getHomeDirectory().resolve(workspaceLocation);
} else {
var externalFsProvider =
FileSystemProviderRegistry.getInstance().getFileSystemProviderBySchema(workspaceUri.getScheme());
if (externalFsProvider == null) {
log.error("File system not found for scheme: " + workspaceUri.getScheme() + " default workspace " +
"location will be used");
return defaultWorkspaceLocation;
}
log.info("Path from external filesystem used for workspace: " + externalFsPath);
return externalFsPath;
} catch (DBException e) {
log.error("Failed to detect workspace path from URI: " + workspaceLocation +
" default workspace location will be used", e);
return defaultWorkspaceLocation;
ClassLoader fsClassloader = externalFsProvider.getInstance().getClass().getClassLoader();
try (FileSystem externalFileSystem = FileSystems.newFileSystem(workspaceUri,
System.getenv(),
fsClassloader)) {
log.info("Path from external filesystem used for workspace: " + workspaceUri);
return externalFileSystem.provider().getPath(workspaceUri);
} catch (Exception e) {
log.error("Failed to initialize workspace path: " + workspaceUri + " default workspace " +
"location will be used", e);
}
}
return defaultWorkspaceLocation;
}
@Nullable
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* 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.
@@ -16,8 +16,8 @@
*/
package io.cloudbeaver.model.app;
import io.cloudbeaver.model.cli.CloudBeaverInstanceServer;
import io.cloudbeaver.model.log.SLF4JLogHandler;
import io.cloudbeaver.registry.WebFeatureRegistry;
import org.eclipse.core.runtime.Platform;
import org.eclipse.equinox.app.IApplicationContext;
import org.jkiss.code.NotNull;
@@ -25,13 +25,11 @@ import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBFileController;
import org.jkiss.dbeaver.model.app.DBPLockManagerProvider;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.auth.SMSessionContext;
import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.fs.lock.LockManager;
import org.jkiss.dbeaver.model.fs.lock.shared.SharedFileLockManager;
import org.jkiss.dbeaver.model.impl.app.ApplicationRegistry;
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
@@ -51,7 +49,7 @@ import java.util.Map;
/**
* Servlet application
*/
public abstract class BaseServletApplication extends BaseApplicationImpl implements ServletApplication, DBPLockManagerProvider {
public abstract class BaseServletApplication extends BaseApplicationImpl implements ServletApplication {
public static final String DEFAULT_CONFIG_FILE_PATH = "/etc/cloudbeaver.conf";
public static final String CUSTOM_CONFIG_FOLDER = "custom";
@@ -61,8 +59,7 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
private static final Log log = Log.getLog(BaseServletApplication.class);
private String instanceId;
@NotNull
private CloudBeaverInstanceServer instanceServer;
@Override
public RMController createResourceController(
@NotNull SMCredentialsProvider credentialsProvider,
@@ -77,16 +74,10 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
throw new IllegalStateException("File controller is not supported by " + getClass().getSimpleName());
}
@NotNull
@Nullable
@Override
public LockManager createLockManager(@NotNull String applicationId, @NotNull Path metadataFolder) throws DBException {
return new SharedFileLockManager(applicationId, metadataFolder);
}
@NotNull
@Override
public LockManager createLockManager() throws DBException {
return new SharedFileLockManager(getApplicationInstanceId());
public Path getDefaultWorkingFolder() {
return getServerConfigurationController().getWorkspacePath();
}
@Override
@@ -171,7 +162,6 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
* Method returns VoidSecretController instance.
* Advanced apps may implement it differently.
*/
@NotNull
@Override
public DBSSecretController getSecretController(
@NotNull SMCredentialsProvider credentialsProvider,
@@ -212,9 +202,12 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
@Override
public Object start(IApplicationContext context) {
initializeApplicationServices();
setWorkspacePath(getServerConfigurationController().getWorkspacePath());
try {
try {
this.instanceServer = new CloudBeaverInstanceServer();
} catch (Exception e) {
log.error("Error initializing instance server", e);
}
startServer();
} catch (Exception e) {
log.error(e.getMessage(), e);
@@ -225,7 +218,6 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
protected abstract void startServer() throws DBException;
@NotNull
@Override
public synchronized String getApplicationInstanceId() throws DBException {
if (instanceId == null) {
@@ -250,6 +242,12 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
return BaseWorkspaceImpl.readWorkspaceIdProperty();
}
@Override
public Path getWorkspaceDirectory() {
return getServerConfigurationController().getWorkspacePath();
}
public String getApplicationId() {
try {
return getApplicationInstanceId();
@@ -258,7 +256,6 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
}
}
@NotNull
@Override
public WSEventController getEventController() {
return null;
@@ -279,14 +276,14 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
}
}
@Nullable
@Override
public ApplicationInstanceController getInstanceServer() {
return instanceServer;
}
@Override
public boolean isAnonymousAccessEnabled() {
return getAppConfiguration().isAnonymousAccessEnabled();
}
@NotNull
@Override
public WebFeatureRegistry getFeatureRegistry() {
return WebFeatureRegistry.getInstance();
}
}
@@ -17,6 +17,7 @@
package io.cloudbeaver.model.app;
import io.cloudbeaver.DBWFeatureSet;
import io.cloudbeaver.registry.WebFeatureRegistry;
import io.cloudbeaver.utils.ServletAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.DBConstants;
@@ -114,7 +115,7 @@ public abstract class BaseWebAppConfiguration implements ServletAppConfiguration
public String[] getEnabledFeatures() {
if (enabledFeatures == null) {
// No config - enable all features (+backward compatibility)
return ServletAppUtils.getServletApplication().getFeatureRegistry().getWebFeatures()
return WebFeatureRegistry.getInstance().getWebFeatures()
.stream().map(DBWFeatureSet::getId).toArray(String[]::new);
}
return enabledFeatures;
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,9 +16,7 @@
*/
package io.cloudbeaver.model.app;
import io.cloudbeaver.registry.WebFeatureRegistry;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.DBFileController;
import org.jkiss.dbeaver.model.app.DBPApplication;
@@ -44,16 +42,14 @@ public interface ServletApplication extends DBPApplication {
return false;
}
@NotNull
ServletAppConfiguration getAppConfiguration();
@NotNull
ServletServerConfiguration getServerConfiguration();
@NotNull
Path getDataDirectory(boolean create);
@NotNull
Path getWorkspaceDirectory();
Path getHomeDirectory();
boolean isMultiNode();
@@ -62,35 +58,30 @@ public interface ServletApplication extends DBPApplication {
SMAdminController getAdminSecurityController(@NotNull SMCredentialsProvider credentialsProvider) throws DBException;
@NotNull
DBSSecretController getSecretController(
@NotNull SMCredentialsProvider credentialsProvider,
SMSessionContext smSessionContext
) throws DBException;
@NotNull
RMController createResourceController(
@NotNull SMCredentialsProvider credentialsProvider,
@NotNull DBPWorkspace workspace
) throws DBException;
@NotNull
DBFileController createFileController(@NotNull SMCredentialsProvider credentialsProvider);
@NotNull
String getServerURL();
default String getServicesURI() {
return "/";
}
@NotNull
default String getRootURI() {
return "";
}
@NotNull
String getApplicationInstanceId() throws DBException;
@NotNull
WSEventController getEventController();
/**
@@ -103,18 +94,12 @@ public interface ServletApplication extends DBPApplication {
/**
* Collector that contains information about system.
*/
@Nullable
ServletSystemInformationCollector<?> getSystemInformationCollector();
@NotNull
ServletSystemInformationCollector getSystemInformationCollector();
default void getStatusInfo(@NotNull Map<String, Object> infoMap) {
default void getStatusInfo(Map<String, Object> infoMap) {
}
boolean isAnonymousAccessEnabled();
/**
* Web feature registry
*/
@NotNull
WebFeatureRegistry getFeatureRegistry();
}
@@ -62,7 +62,6 @@ 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;
@@ -159,15 +158,7 @@ public class ServletSystemInformationCollector<T extends ServletApplication> imp
/**
* Collects info about internal databases.
*/
public synchronized void collectInternalDatabaseUseInformation() throws DBException {
if (internalDatabaseInfoCollected) {
return;
}
collectInternalDatabaseInfo();
this.internalDatabaseInfoCollected = true;
}
protected void collectInternalDatabaseInfo() throws DBException {
public void collectInternalDatabaseUseInformation() throws DBException {
this.smDatabaseInfo = application.getAdminSecurityController(new NoAuthCredentialsProvider())
.getInternalDatabaseInformation();
}
@@ -14,23 +14,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.service.sql.messages;
package io.cloudbeaver.model.cli;
import org.eclipse.osgi.util.NLS;
import org.jkiss.dbeaver.model.cli.ApplicationCommandLine;
import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
public class WebSQLMessages extends NLS {
static final String BUNDLE_NAME = "io.cloudbeaver.service.sql.messages.WebSQLResources"; //$NON-NLS-1$
public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationInstanceController> {
private static CloudBeaverCommandLine INSTANCE = null;
// TODO: add localizations
public static String model_web_ai_query_confirmation_title;
public static String model_web_ai_query_confirmation_message;
public static String model_web_dialog_sql_param_title;
static {
NLS.initializeMessages(BUNDLE_NAME, WebSQLMessages.class);
private CloudBeaverCommandLine() {
super();
}
private WebSQLMessages() {
public synchronized static CloudBeaverCommandLine getInstance() {
if (INSTANCE == null) {
INSTANCE = new CloudBeaverCommandLine();
}
return INSTANCE;
}
}
@@ -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,27 +25,20 @@ 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();
}
public 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 commandLine.executeCommandLineCommands(
return CloudBeaverCommandLine.getInstance().executeCommandLineCommands(
cmd,
this,
false,
false,
args
false
);
} catch (Exception e) {
return new CLIProcessResult(CLIProcessResult.PostAction.ERROR, "Error executing command: " + e.getMessage());
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@ import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import java.nio.file.Path;
public class WebFSUtils {
public class FSUtils {
@NotNull
public static String makeUniqueFsId(@NotNull DBFVirtualFileSystem fileSystem) {
return fileSystem.getType() + "://" + fileSystem.getId();
@@ -115,6 +115,13 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
return ((DBNResourceManagerRoot) getParentNode()).getResourceController();
}
@NotNull
@Deprecated
@Override
public String getNodeItemPath() {
return getParentNode().getNodeItemPath() + "/" + getName();
}
@Nullable
@Override
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
@@ -31,6 +31,8 @@ import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.rm.RMResource;
import org.jkiss.dbeaver.model.rm.RMResourceType;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.registry.ResourceTypeDescriptor;
import org.jkiss.dbeaver.registry.ResourceTypeRegistry;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.IOUtils;
@@ -42,7 +44,7 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
private final RMResource resource;
DBNResourceManagerResource(@NotNull DBNNode parentNode, @NotNull RMResource resource) {
DBNResourceManagerResource(DBNNode parentNode, RMResource resource) {
super(parentNode);
this.resource = resource;
}
@@ -69,7 +71,6 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
@Override
public DBPImage getNodeIcon() {
if (resource.isFolder()) {
/*
if (getParentNode() instanceof DBNResourceManagerResource) {
return DBIcon.TREE_FOLDER;
}
@@ -81,12 +82,11 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
if (folderResType != null) {
return folderResType.getFolderIcon();
}
*/
return DBIcon.TREE_FOLDER;
} else {
var fileExtension = IOUtils.getFileExtension(getNodeDisplayName());
if (!CommonUtils.isEmpty(fileExtension)) {
RMProject project = getRmProject();
RMProject project = getProjectNode();
if (project != null) {
for (RMResourceType rt : project.getResourceTypes()) {
if (ArrayUtils.contains(rt.getFileExtensions(), fileExtension)) {
@@ -99,8 +99,7 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
}
}
@Nullable
public RMProject getRmProject() {
private RMProject getProjectNode() {
for (DBNNode node = this; node != null; node = node.getParentNode()) {
if (node instanceof DBNResourceManagerProject) {
return ((DBNResourceManagerProject) node).getProject();
@@ -160,6 +159,13 @@ 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(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
@@ -115,6 +115,14 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
return projects;
}
@NotNull
@Deprecated
@Override
public String getNodeItemPath() {
// Path doesn't include project name
return NodePathType.ext.getPrefix() + getNodeDisplayName();
}
@Nullable
@Override
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
@@ -16,6 +16,7 @@
*/
package io.cloudbeaver.model.rm.local;
import io.cloudbeaver.BaseWebProjectImpl;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
@@ -26,9 +27,8 @@ import org.jkiss.dbeaver.model.DBPDataSourceFolder;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.fs.lock.LockManager;
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
import org.jkiss.dbeaver.model.rm.RMController;
import org.jkiss.dbeaver.model.rm.RMEvent;
import org.jkiss.dbeaver.model.rm.RMEventManager;
@@ -51,21 +51,22 @@ public abstract class BaseLocalResourceController implements RMController {
private static final Log log = Log.getLog(BaseLocalResourceController.class);
public static final String DEFAULT_CHANGE_ID = "0";
private static final String FILE_REGEX = "(?U)[\\w.$()@/\\\\ -]+";
private static final String PROJECT_REGEX = "(?U)[\\w.$()@ -]+"; // slash not allowed in project name
@NotNull
protected final DBPWorkspace workspace;
@NotNull
protected final LockManager lockController;
protected final FileLockController lockController;
protected BaseLocalResourceController(
@NotNull DBPWorkspace workspace,
@NotNull LockManager lockController
@NotNull FileLockController lockController
) {
this.workspace = workspace;
this.lockController = lockController;
}
@Nullable
@Override
public RMProject getProject(@NotNull String projectId, boolean readResources, boolean readProperties)
throws DBException {
@@ -84,7 +85,6 @@ public abstract class BaseLocalResourceController implements RMController {
return project;
}
@Nullable
@Override
public Object getProjectProperty(@NotNull String projectId, @NotNull String propName) throws DBException {
var project = getWebProject(projectId, false);
@@ -97,7 +97,7 @@ public abstract class BaseLocalResourceController implements RMController {
public void setProjectProperty(
@NotNull String projectId,
@NotNull String propName,
@Nullable Object propValue
@NotNull Object propValue
) throws DBException {
RMLocalProject webProject = getWebProject(projectId, false);
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
@@ -159,7 +159,7 @@ public abstract class BaseLocalResourceController implements RMController {
@NotNull String configuration,
@Nullable List<String> dataSourceIds
) throws DBException {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("updateProjectDataSources"))) {
try (var lock = lockController.lock(projectId, "updateProjectDataSources")) {
DBPProject project = getWebProject(projectId, false);
return doFileWriteOperation(
projectId, project.getMetadataFolder(false),
@@ -190,7 +190,7 @@ public abstract class BaseLocalResourceController implements RMController {
@NotNull String projectId,
@NotNull String[] dataSourceIds
) throws DBException {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteDataSources"))) {
try (var projectLock = lockController.lock(projectId, "deleteDatasources")) {
DBPProject project = getWebProject(projectId, false);
doFileWriteOperation(projectId, project.getMetadataFolder(false), () -> {
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
@@ -215,7 +215,7 @@ public abstract class BaseLocalResourceController implements RMController {
@NotNull String projectId,
@NotNull String folderPath
) throws DBException {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
try (var projectLock = lockController.lock(projectId, "createDatasourceFolder")) {
DBPProject project = getWebProject(projectId, false);
log.debug("Creating data source folder '" + folderPath + "' in project '" + projectId + "'");
doFileWriteOperation(projectId, project.getMetadataFolder(false),
@@ -226,10 +226,8 @@ public abstract class BaseLocalResourceController implements RMController {
GeneralUtils.validateResourceName(newName);
var parent = result.getParent();
var parentFolder = parent == null ? null : registry.getFolder(parent.toString().replace("\\", "/"));
registry.addFolder(parentFolder, newName);
if (registry instanceof DataSourcePersistentRegistry pr) {
pr.saveDataSources();
}
DBPDataSourceFolder newFolder = registry.addFolder(parentFolder, newName);
((DataSourcePersistentRegistry) registry).saveDataSources();
registry.checkForErrors();
return null;
}
@@ -243,19 +241,21 @@ public abstract class BaseLocalResourceController implements RMController {
@NotNull String[] folderPaths,
boolean dropContents
) throws DBException {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
try (var projectLock = lockController.lock(projectId, "createDatasourceFolder")) {
DBPProject project = getWebProject(projectId, false);
doFileWriteOperation(projectId, project.getMetadataFolder(false),
() -> {
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
for (String folderPath : folderPaths) {
DBPDataSourceFolder folder = registry.getFolder(folderPath);
log.debug("Deleting data source folder '" + folderPath + "' in project '" + projectId + "'");
registry.removeFolder(folder, dropContents);
}
if (registry instanceof DataSourcePersistentRegistry pr) {
pr.saveDataSources();
if (folder != null) {
log.debug("Deleting data source folder '" + folderPath + "' in project '" + projectId + "'");
registry.removeFolder(folder, dropContents);
} else {
log.warn("Can not find folder by path [" + folderPath + "] for deletion");
}
}
((DataSourcePersistentRegistry) registry).saveDataSources();
registry.checkForErrors();
return null;
}
@@ -269,7 +269,7 @@ public abstract class BaseLocalResourceController implements RMController {
@NotNull String oldPath,
@NotNull String newPath
) throws DBException {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
try (var projectLock = lockController.lock(projectId, "createDatasourceFolder")) {
DBPProject project = getWebProject(projectId, false);
log.debug("Moving data source folder from '" + oldPath + "' to '" + newPath + "' in project '" + projectId + "'");
doFileWriteOperation(projectId, project.getMetadataFolder(false),
@@ -284,19 +284,19 @@ public abstract class BaseLocalResourceController implements RMController {
}
}
protected abstract RMLocalProject getWebProject(@NotNull String projectId, boolean refresh) throws DBException;
protected abstract RMLocalProject getWebProject(String projectId, boolean refresh) throws DBException;
protected abstract <T> T doFileWriteOperation(@NotNull String projectId, @NotNull Path file, @NotNull RMFileOperation<T> operation)
protected abstract <T> T doFileWriteOperation(String projectId, Path file, RMFileOperation<T> operation)
throws DBException;
protected abstract <T> T doFileReadOperation(@NotNull String projectId, @NotNull Path file, @NotNull RMFileOperation<T> operation)
protected abstract <T> T doFileReadOperation(String projectId, Path file, RMFileOperation<T> operation)
throws DBException;
protected abstract <T> T doProjectOperation(@NotNull String projectId, @NotNull RMFileOperation<T> operation) throws DBException;
protected abstract <T> T doProjectOperation(String projectId, RMFileOperation<T> operation) throws DBException;
protected abstract RMProject makeProjectFromId(@NotNull String projectId, boolean loadPermissions) throws DBException;
protected abstract RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException;
protected void validateResourcePath(@NotNull String resourcePath) throws DBException {
protected void validateResourcePath(String resourcePath) throws DBException {
var fullPath = Paths.get(resourcePath);
for (Path path : fullPath) {
String fileName = IOUtils.getFileNameWithoutExtension(path);
@@ -314,6 +314,28 @@ public abstract class BaseLocalResourceController implements RMController {
}
}
protected class InternalWebProjectImpl extends BaseWebProjectImpl {
public InternalWebProjectImpl(
@NotNull SessionContextImpl sessionContext,
@NotNull RMProject rmProject,
@NotNull Path projectPath
) {
super(
BaseLocalResourceController.this.workspace,
BaseLocalResourceController.this,
sessionContext,
rmProject,
projectPath
);
}
@NotNull
@Override
protected DBPDataSourceRegistry createDataSourceRegistry() {
return new DataSourceRegistry(this);
}
}
protected void fireRmResourceAddEvent(@NotNull String projectId, @NotNull String resourcePath) throws DBException {
RMEventManager.fireEvent(
new RMEvent(RMEvent.Action.RESOURCE_ADD,
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* 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.
@@ -33,15 +33,13 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.SMCredentials;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.fs.lock.LockManager;
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
import org.jkiss.dbeaver.model.navigator.DBNLocalFolder;
import org.jkiss.dbeaver.model.rm.*;
import org.jkiss.dbeaver.model.security.SMAdminController;
import org.jkiss.dbeaver.model.security.SMController;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.sql.DBQuotaException;
import org.jkiss.dbeaver.model.websocket.event.MessageType;
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
@@ -53,7 +51,6 @@ import org.jkiss.dbeaver.registry.DataSourceParseResults;
import org.jkiss.dbeaver.registry.ResourceTypeDescriptor;
import org.jkiss.dbeaver.registry.ResourceTypeRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.IOUtils;
import org.jkiss.utils.Pair;
@@ -81,22 +78,20 @@ public class LocalResourceController extends BaseLocalResourceController {
private final Path userProjectsPath;
private final Path sharedProjectsPath;
private final String globalProjectName;
private Supplier<SMAdminController> smControllerSupplier;
private Supplier<SMController> smControllerSupplier;
protected final List<RMFileOperationHandler> fileHandlers;
private final Map<String, RMLocalProject> projectRegistries = new LinkedHashMap<>();
private final ProjectsMetadataInfo sharedProjectsMetadataInfo;
public LocalResourceController(
@NotNull DBPWorkspace workspace,
@NotNull SMCredentialsProvider credentialsProvider,
@NotNull Path rootPath,
@NotNull Path userProjectsPath,
@NotNull Path sharedProjectsPath,
@NotNull Supplier<SMAdminController> smControllerSupplier,
@NotNull LockManager lockController
DBPWorkspace workspace,
SMCredentialsProvider credentialsProvider,
Path rootPath,
Path userProjectsPath,
Path sharedProjectsPath,
Supplier<SMController> smControllerSupplier
) throws DBException {
super(workspace, lockController);
super(workspace, new FileLockController(ServletAppUtils.getServletApplication().getApplicationInstanceId()));
this.credentialsProvider = credentialsProvider;
this.rootPath = rootPath;
this.userProjectsPath = userProjectsPath;
@@ -105,34 +100,27 @@ public class LocalResourceController extends BaseLocalResourceController {
this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName();
this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers();
this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath, lockController);
}
@NotNull
protected SMAdminController getSecurityController() {
protected SMController getSecurityController() {
return smControllerSupplier.get();
}
@Nullable
private Path getGlobalProjectPath() {
return globalProjectName == null ? null : this.rootPath.resolve(this.globalProjectName);
}
@Nullable
private Path getPrivateProjectPath() {
SMCredentials activeUserCredentials = credentialsProvider.getActiveUserCredentials();
String userId = activeUserCredentials == null ? null : activeUserCredentials.getUserId();
return userId == null ? null : this.userProjectsPath.resolve(userId);
}
protected RMLocalProject getWebProject(@NotNull String projectId, boolean refresh) throws DBException {
protected RMLocalProject getWebProject(String projectId, boolean refresh) throws DBException {
synchronized (projectRegistries) {
RMLocalProject project = projectRegistries.get(projectId);
if (project == null || refresh) {
project = createWebProjectImpl(projectId, new SessionContextImpl(null));
if (project.getProjectType() == RMProjectType.SHARED) {
project.setProjectInfo(sharedProjectsMetadataInfo.getProjectInfo(projectId));
}
projectRegistries.put(projectId, project);
}
return project;
@@ -179,6 +167,11 @@ public class LocalResourceController extends BaseLocalResourceController {
projects.add(0, userProject);
}
}
if (ServletAppUtils.getServletApplication().isMultiNode()) {
for (RMProject rmProject : projects) {
handleProjectOpened(rmProject.getId());
}
}
projects.sort(Comparator.comparing(RMProject::getDisplayName));
return projects.toArray(new RMProject[0]);
@@ -284,7 +277,7 @@ public class LocalResourceController extends BaseLocalResourceController {
throw new DBException("Error creating shared project path", e);
}
}
GeneralUtils.validateResourceNameUnconditionally(name);
validateResourcePath(name);
validateProjectName(null, name);
var projectPath = sharedProjectsPath.resolve(name);
if (Files.exists(projectPath)) {
@@ -296,6 +289,9 @@ public class LocalResourceController extends BaseLocalResourceController {
try {
log.debug("Creating project '" + rmProject.getId() + "'");
Files.createDirectories(projectPath);
if (ServletAppUtils.getServletApplication().isMultiNode()) {
createResourceTypeFolders(projectPath);
}
fireRmProjectAddEvent(rmProject);
return rmProject;
} catch (IOException e) {
@@ -303,11 +299,10 @@ public class LocalResourceController extends BaseLocalResourceController {
}
}
@NotNull
@Override
public RMProject updateProject(@NotNull String projectId, @NotNull RMProjectInfo projectInfo) throws DBException {
validateProjectName(projectId, projectInfo.getName());
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("updateProject"))) {
try (var projectLock = lockController.lock(projectId, "updateProject")) {
RMLocalProject project = getWebProject(projectId, false);
Path targetPath = getProjectPath(projectId);
if (!Files.exists(targetPath)) {
@@ -317,7 +312,6 @@ public class LocalResourceController extends BaseLocalResourceController {
throw new DBException("Project '" + projectId + "' is not shared");
}
project.updateProject(projectInfo.getName(), projectInfo.getDescription());
sharedProjectsMetadataInfo.updateProjectInfo(projectId, projectInfo);
return WebRMUtils.createRmProjectFromWebProject(project);
}
}
@@ -334,28 +328,26 @@ public class LocalResourceController extends BaseLocalResourceController {
@Override
public void deleteProject(@NotNull String projectId) throws DBException {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteProject"))) {
try (var projectLock = lockController.lock(projectId, "deleteProject")) {
RMProject project = makeProjectFromId(projectId, false);
Path targetPath = getProjectPath(projectId);
if (!Files.exists(targetPath)) {
log.error(MessageFormat.format("Project folder ''{0}'' is not found", projectId));
log.debug(MessageFormat.format("Project folder ''{0}'' is not found", projectId));
return;
}
try {
log.debug("Deleting project '" + projectId + "' in persistence storage");
getSecurityController().deleteObject(projectId, projectId, SMObjectType.project);
log.debug("Deleting project '" + projectId + "' folder");
log.debug("Deleting project '" + projectId + "'");
IOUtils.deleteDirectory(targetPath);
getSecurityController().deleteAllObjectPermissions(projectId, SMObjectType.project);
synchronized (projectRegistries) {
projectRegistries.remove(projectId);
}
sharedProjectsMetadataInfo.updateProjectInfo(projectId, null);
} catch (IOException e) {
throw new DBException("Error deleting project '" + projectId + "'", e);
throw new DBException("Error deleting project '" + project.getName() + "'", e);
}
}
}
@Nullable
@Override
public RMProject getProject(@NotNull String projectId, boolean readResources, boolean readProperties) throws DBException {
RMProject project = makeProjectFromId(projectId, true);
@@ -415,15 +407,6 @@ public class LocalResourceController extends BaseLocalResourceController {
@Override
public void deleteProjectDataSources(@NotNull String projectId, @NotNull String[] dataSourceIds) throws DBException {
super.deleteProjectDataSources(projectId, dataSourceIds);
log.debug("Delete datasources '" + Arrays.toString(dataSourceIds) + "' from security controller");
for (String dsId : dataSourceIds) {
try {
getSecurityController().deleteObject(projectId, dsId, SMObjectType.datasource);
} catch (DBException e) {
log.error("Error deleting datasource '" + dsId + "' from database");
}
}
if (credentialsProvider.getActiveUserCredentials() != null && dataSourceIds.length > 0) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDataSourceEvent.delete(
@@ -631,8 +614,7 @@ public class LocalResourceController extends BaseLocalResourceController {
}
return doProjectOperation(projectId, () -> {
try {
boolean projectFolder = CommonUtils.isEmpty(folder);
Path folderPath = projectFolder ?
Path folderPath = CommonUtils.isEmpty(folder) ?
projectPath :
projectPath.resolve(folder);
folderPath = folderPath.normalize();
@@ -641,7 +623,7 @@ public class LocalResourceController extends BaseLocalResourceController {
throw new DBException("Invalid folder path");
}
createFolder(folderPath);
return readChildResources(projectId, folderPath, nameMask, projectFolder, readProperties, readHistory, recursive);
return readChildResources(projectId, folderPath, nameMask, readProperties, readHistory, recursive);
} catch (NoSuchFileException e) {
throw new DBException("Invalid resource folder " + folder);
} catch (IOException e) {
@@ -655,55 +637,29 @@ public class LocalResourceController extends BaseLocalResourceController {
@NotNull String projectId,
@NotNull Path folderPath,
@Nullable String nameMask,
boolean projectFolder,
boolean readProperties,
boolean readHistory,
boolean recursive
) throws IOException {
try (Stream<Path> files = Files.list(folderPath)) {
Set<RMResource> children = files.filter(path -> {
return files.filter(path -> {
String fileName = path.getFileName().toString();
return (nameMask == null || nameMask.equals(fileName)) && !fileName.startsWith(".");
}) // skip hidden files
.sorted(Comparator.comparing(path -> path.getFileName().toString(), String.CASE_INSENSITIVE_ORDER))
.map((Path path) -> makeResourceFromPath(projectId, path, nameMask, readProperties, readHistory, recursive))
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (ServletAppUtils.getServletApplication().isMultiNode() && projectFolder) {
addResourceTypeFolders(folderPath, children);
}
return children.stream()
.sorted(Comparator.comparing(RMResource::getName, String.CASE_INSENSITIVE_ORDER))
.toArray(RMResource[]::new);
}
}
private void addResourceTypeFolders(@NotNull Path projectPath, @NotNull Set<RMResource> children) {
Set<String> folderNames = children.stream()
.filter(RMResource::isFolder)
.map(RMResource::getName)
.collect(Collectors.toSet());
// fake resources for default resource type folders
for (ResourceTypeDescriptor type : ResourceTypeRegistry.getInstance().getResourceTypes()) {
String defaultRoot = type.getDefaultRoot(null);
if (CommonUtils.isEmpty(defaultRoot)) {
continue;
}
Path resourceFolder = projectPath.resolve(defaultRoot);
if (!folderNames.contains(resourceFolder.getFileName().toString())) {
RMResource resource = new RMResource(resourceFolder.getFileName().toString());
resource.setFolder(true);
children.add(resource);
}
}
}
@Override
public String createResource(
@NotNull String projectId,
@NotNull String resourcePath,
boolean isFolder
) throws DBException {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createResource"))) {
try (var ignoredLock = lockController.lock(projectId, "createResource")) {
validateResourcePath(resourcePath);
Path targetPath = getTargetPath(projectId, resourcePath);
if (Files.exists(targetPath)) {
@@ -735,7 +691,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@NotNull String oldResourcePath,
@NotNull String newResourcePath
) throws DBException {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("moveResource"))) {
try (var ignoredLock = lockController.lock(projectId, "moveResource")) {
var normalizedOldResourcePath = CommonUtils.normalizeResourcePath(oldResourcePath);
var normalizedNewResourcePath = CommonUtils.normalizeResourcePath(newResourcePath);
if (log.isDebugEnabled()) {
@@ -807,7 +763,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@Override
public void deleteResource(@NotNull String projectId, @NotNull String resourcePath, boolean recursive) throws DBException {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteResource"))) {
try (var ignoredLock = lockController.lock(projectId, "deleteResource")) {
if (log.isDebugEnabled()) {
log.debug("Removing resource from '" + resourcePath + "' in project '" + projectId + "'" + (recursive ? " recursive" : ""));
}
@@ -895,7 +851,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@NotNull byte[] data,
boolean forceOverwrite
) throws DBException {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("setResourceContents"))) {
try (var ignoredLock = lockController.lock(projectId, "setResourceContents")) {
validateResourcePath(resourcePath);
Number fileSizeLimit = ServletAppUtils.getServletApplication()
.getAppConfiguration()
@@ -940,7 +896,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@NotNull String propertyName,
@Nullable Object propertyValue
) throws DBException {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("resourcePropertyUpdate"))) {
try (var ignoredLock = lockController.lock(projectId, "resourcePropertyUpdate")) {
validateResourcePath(resourcePath);
RMLocalProject webProject = getWebProject(projectId, false);
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
@@ -961,7 +917,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@NotNull String resourcePath,
@NotNull Map<String, Object> properties
) throws DBException {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("resourcePropertyUpdate"))) {
try (var ignoredLock = lockController.lock(projectId, "resourcePropertyUpdate")) {
validateResourcePath(resourcePath);
RMLocalProject webProject = getWebProject(projectId, false);
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
@@ -998,7 +954,7 @@ public class LocalResourceController extends BaseLocalResourceController {
}
@Nullable
protected RMProject makeProjectFromId(@NotNull String projectId, boolean loadPermissions) throws DBException {
protected RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException {
var projectName = WebRMUtils.parseProjectName(projectId);
var projectPath = getProjectPath(projectId);
if (!Files.exists(projectPath)) {
@@ -1037,13 +993,29 @@ public class LocalResourceController extends BaseLocalResourceController {
.toArray(String[]::new);
RMLocalProject webProject = new RMLocalProject(workspace, new SessionContextImpl(null), path, type);
if (type == RMProjectType.SHARED) {
webProject.setProjectInfo(sharedProjectsMetadataInfo.getProjectInfo(webProject.getId()));
}
return createRmProjectFromWebProject(path, webProject, allProjectPermissions);
}
protected <T> T doProjectOperation(@NotNull String projectId, @NotNull RMFileOperation<T> operation) throws DBException {
private void createResourceTypeFolders(Path path) {
// FIXME: do not create folders by force!!!
var resourceTypes = ResourceTypeRegistry.getInstance().getResourceTypes();
for (var resourceType : resourceTypes) {
var defaultRoot = resourceType.getDefaultRoot(null);
if (defaultRoot == null) {
continue;
}
var typeFolder = path.resolve(defaultRoot);
try {
if (!Files.exists(typeFolder)) {
createFolder(typeFolder);
}
} catch (Exception e) {
log.error("Resource folder " + typeFolder + " is not created", e);
}
}
}
protected <T> T doProjectOperation(String projectId, RMFileOperation<T> operation) throws DBException {
for (RMFileOperationHandler fileHandler : fileHandlers) {
try {
fileHandler.projectOpened(projectId);
@@ -1061,7 +1033,7 @@ public class LocalResourceController extends BaseLocalResourceController {
return operation.doOperation();
}
protected <T> T doFileReadOperation(@NotNull String projectId, @NotNull Path file, @NotNull RMFileOperation<T> operation) throws DBException {
protected <T> T doFileReadOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
for (RMFileOperationHandler fileHandler : fileHandlers) {
try {
fileHandler.beforeFileRead(projectId, file);
@@ -1080,11 +1052,7 @@ public class LocalResourceController extends BaseLocalResourceController {
return operation.doOperation();
}
protected <T> T doFileWriteOperation(
@NotNull String projectId,
@NotNull Path file,
@NotNull RMFileOperation<T> operation
) throws DBException {
protected <T> T doFileWriteOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
for (RMFileOperationHandler fileHandler : fileHandlers) {
fileHandler.beforeFileChange(projectId, file);
}
@@ -1103,11 +1071,10 @@ public class LocalResourceController extends BaseLocalResourceController {
return result;
}
protected Path getProjectPath(@NotNull String projectId) throws DBException {
protected Path getProjectPath(String projectId) throws DBException {
RMProjectName project = WebRMUtils.parseProjectName(projectId);
RMProjectType type = project.getType();
String projectName = project.getName();
GeneralUtils.validateResourceNameUnconditionally(projectName);
switch (type) {
case GLOBAL:
if (!projectName.equals(globalProjectName)) {
@@ -1195,7 +1162,7 @@ public class LocalResourceController extends BaseLocalResourceController {
if (recursive && resource.isFolder()) {
try {
resource.setChildren(readChildResources(projectId, path, nameMask, false, readProperties, readHistory, true));
resource.setChildren(readChildResources(projectId, path, nameMask, readProperties, readHistory, true));
} catch (IOException e) {
log.error(e);
}
@@ -1209,14 +1176,16 @@ public class LocalResourceController extends BaseLocalResourceController {
return getProjectPath(projectId).toAbsolutePath().relativize(path).toString().replace('\\', IPath.SEPARATOR);
}
@NotNull
protected void handleProjectOpened(String projectId) throws DBException {
createResourceTypeFolders(getProjectPath(projectId));
}
public static Builder builder(
@NotNull SMCredentialsProvider credentialsProvider,
@NotNull DBPWorkspace workspace,
@NotNull LockManager lockController,
@NotNull Supplier<SMAdminController> smControllerSupplier
SMCredentialsProvider credentialsProvider,
DBPWorkspace workspace,
Supplier<SMController> smControllerSupplier
) {
return new Builder(workspace, credentialsProvider, lockController, smControllerSupplier);
return new Builder(workspace, credentialsProvider, smControllerSupplier);
}
@Override
@@ -1226,19 +1195,16 @@ public class LocalResourceController extends BaseLocalResourceController {
public static class Builder {
protected final SMCredentialsProvider credentialsProvider;
protected final Supplier<SMAdminController> smController;
protected final Supplier<SMController> smController;
protected final DBPWorkspace workspace;
protected Path rootPath;
protected Path userProjectsPath;
protected Path sharedProjectsPath;
protected LockManager lockController;
protected Builder(
@NotNull DBPWorkspace workspace,
@NotNull SMCredentialsProvider credentialsProvider,
@NotNull LockManager lockController,
@NotNull Supplier<SMAdminController> smControllerSupplier
DBPWorkspace workspace, SMCredentialsProvider credentialsProvider,
Supplier<SMController> smControllerSupplier
) {
this.workspace = workspace;
this.credentialsProvider = credentialsProvider;
@@ -1246,38 +1212,25 @@ public class LocalResourceController extends BaseLocalResourceController {
this.rootPath = RMUtils.getRootPath();
this.userProjectsPath = RMUtils.getUserProjectsPath();
this.sharedProjectsPath = RMUtils.getSharedProjectsPath();
this.lockController = lockController;
}
@NotNull
public Builder setRootPath(@NotNull Path rootPath) {
public Builder setRootPath(Path rootPath) {
this.rootPath = rootPath;
return this;
}
@NotNull
public Builder setUserProjectsPath(@NotNull Path userProjectsPath) {
public Builder setUserProjectsPath(Path userProjectsPath) {
this.userProjectsPath = userProjectsPath;
return this;
}
@NotNull
public Builder setSharedProjectsPath(@NotNull Path sharedProjectsPath) {
public Builder setSharedProjectsPath(Path sharedProjectsPath) {
this.sharedProjectsPath = sharedProjectsPath;
return this;
}
@NotNull
public LocalResourceController build() throws DBException {
return new LocalResourceController(
workspace,
credentialsProvider,
rootPath,
userProjectsPath,
sharedProjectsPath,
smController,
lockController
);
return new LocalResourceController(workspace, credentialsProvider, rootPath, userProjectsPath, sharedProjectsPath, smController);
}
}
@@ -1,152 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.rm.local;
import com.google.gson.reflect.TypeToken;
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.app.DBPProject;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.fs.lock.LockManager;
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
import org.jkiss.dbeaver.model.rm.RMProjectInfo;
import org.jkiss.dbeaver.model.rm.RMProjectType;
import org.jkiss.dbeaver.model.rm.RMUtils;
import org.jkiss.utils.CommonUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Stream;
public class ProjectsMetadataInfo {
private static final Log log = Log.getLog(ProjectsMetadataInfo.class);
private static final String PROJECTS_INFO_FILE_NAME = "projects-info.json";
private final Map<String, RMProjectInfo> projectsInfo = new LinkedHashMap<>();
private final Path projectsPath;
private final LockManager lockController;
public ProjectsMetadataInfo(@NotNull Path projectsPath, @NotNull LockManager lockController) throws DBException {
this.projectsPath = projectsPath;
this.lockController = lockController;
readProjectInfos(projectsPath);
}
public RMProjectInfo getProjectInfo(@NotNull String projectId) {
return projectsInfo.computeIfAbsent(projectId, key -> new RMProjectInfo());
}
public void updateProjectInfo(@NotNull String projectId, @Nullable RMProjectInfo projectInfo) {
if (projectInfo != null) {
projectsInfo.put(projectId, projectInfo);
} else {
projectsInfo.remove(projectId);
}
saveProjectsInfo();
}
private void readProjectInfos(@NotNull Path path) {
if (!Files.exists(path)) {
return;
}
Path projectInfoFile = path.resolve(PROJECTS_INFO_FILE_NAME);
if (!Files.exists(projectInfoFile)) {
loadAndMigrateProjectDataToCommonFile(path);
saveProjectsInfo();
return;
}
readProjectInfosFromFile(projectInfoFile);
}
private void readProjectInfosFromFile(@NotNull Path projectInfoFile) {
try {
log.info("Reading project metadata information");
String content = Files.readString(projectInfoFile, StandardCharsets.UTF_8);
Map<String, RMProjectInfo> loaded = JSONUtils.GSON.fromJson(
content,
TypeToken.getParameterized(Map.class, String.class, RMProjectInfo.class).getType()
);
projectsInfo.clear();
if (loaded != null) {
projectsInfo.putAll(loaded);
}
} catch (IOException e) {
log.error("Error reading existing " + PROJECTS_INFO_FILE_NAME, e);
}
}
private void loadAndMigrateProjectDataToCommonFile(@NotNull Path path) {
log.info("Migrating shared project information to the common place");
Map<String, RMProjectInfo> infos = new LinkedHashMap<>();
try (Stream<Path> stream = Files.list(path)) {
stream.filter(Files::isDirectory).forEach(projectDir -> {
RMProjectInfo info = getProjectInfoFromProjectSettings(projectDir);
String projectId = RMUtils.makeProjectIdFromPath(projectDir, RMProjectType.SHARED);
infos.put(projectId, info);
});
} catch (IOException e) {
log.error("Error listing shared projects path", e);
}
log.info("Migration for project information completed");
projectsInfo.clear();
projectsInfo.putAll(infos);
}
@NotNull
private RMProjectInfo getProjectInfoFromProjectSettings(@NotNull Path projectPath) {
String name = null;
String description = null;
Path settings = projectPath.resolve(DBPProject.METADATA_FOLDER).resolve(BaseProjectImpl.SETTINGS_STORAGE_FILE);
if (Files.exists(settings) && Files.isRegularFile(settings)) {
try {
String json = Files.readString(settings, StandardCharsets.UTF_8);
Map<String, Object> map = JSONUtils.GSON.fromJson(json, JSONUtils.MAP_TYPE_TOKEN);
name = JSONUtils.getString(map, BaseProjectImpl.PROP_PROJECT_NAME);
description = JSONUtils.getString(map, BaseProjectImpl.PROP_PROJECT_DESCRIPTION);
} catch (IOException e) {
log.warn("Failed to read project settings for " + projectPath + ": " + e.getMessage());
} catch (Exception e) {
log.warn("Failed to parse project settings for " + projectPath + ": " + e.getMessage());
}
}
RMProjectInfo info = new RMProjectInfo();
info.setName(CommonUtils.isEmpty(name) ? projectPath.getFileName().toString() : name);
info.setDescription(description);
return info;
}
private void saveProjectsInfo() {
try (var lock = lockController.lock(LockTarget.of(PROJECTS_INFO_FILE_NAME), LockOptions.of("saveProjectsInfo"))) {
log.info("Saving project information");
Files.writeString(projectsPath.resolve(PROJECTS_INFO_FILE_NAME), JSONUtils.GSON.toJson(projectsInfo));
} catch (IOException e) {
log.error("Error writing " + PROJECTS_INFO_FILE_NAME, e);
} catch (DBException e) {
log.error("Error locking file " + PROJECTS_INFO_FILE_NAME + ": " + e.getMessage());
}
}
}
@@ -18,10 +18,8 @@ package io.cloudbeaver.model.rm.local;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.SMSessionContext;
import org.jkiss.dbeaver.model.rm.RMProjectInfo;
import org.jkiss.dbeaver.model.rm.RMProjectType;
import org.jkiss.dbeaver.model.rm.RMUtils;
import org.jkiss.dbeaver.registry.project.LocalProjectImpl;
@@ -31,8 +29,6 @@ import java.nio.file.Path;
public class RMLocalProject extends LocalProjectImpl {
@NotNull
private final RMProjectType projectType;
@Nullable
private RMProjectInfo projectInfo;
public RMLocalProject(
@NotNull DBPWorkspace workspace,
@@ -55,39 +51,6 @@ public class RMLocalProject extends LocalProjectImpl {
return RMUtils.makeProjectIdFromPath(projectPath, projectType);
}
@Nullable
@Override
public String getDescription() {
if (projectInfo == null) {
return null;
}
return projectInfo.getDescription();
}
@NotNull
@Override
public String getName() {
if (projectInfo == null || projectInfo.getName() == null) {
return projectPath.getFileName().toString();
}
return projectInfo.getName();
}
@NotNull
public RMProjectType getProjectType() {
return projectType;
}
@Override
public void updateProject(@Nullable String newName, @Nullable String description) throws DBException {
this.projectInfo = new RMProjectInfo();
projectInfo.setName(newName);
projectInfo.setDescription(description);
}
public void setProjectInfo(@NotNull RMProjectInfo projectInfo) {
this.projectInfo = projectInfo;
}
public boolean canUpdateProjectName() {
return RMProjectType.SHARED.equals(projectType);
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import io.cloudbeaver.model.app.ServletApplication;
import io.cloudbeaver.model.app.ServletAuthApplication;
import io.cloudbeaver.websocket.CBWebSessionEventHandler;
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.auth.SMAuthInfo;
@@ -55,21 +56,14 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
@NotNull
protected final ServletApplication application;
protected volatile long lastAccessTime;
@NotNull
private final SessionType sessionType;
private final List<CBWebSessionEventHandler> sessionEventHandlers = new CopyOnWriteArrayList<>();
private WebSessionEventsFilter eventsFilter;
private final WebSessionWorkspace workspace;
public BaseWebSession(
@NotNull String id,
@NotNull ServletApplication application,
@NotNull SessionType sessionType
) throws DBException {
public BaseWebSession(@NotNull String id, @NotNull ServletApplication application) throws DBException {
this.id = id;
this.application = application;
this.sessionType = sessionType;
this.createTime = System.currentTimeMillis();
this.lastAccessTime = this.createTime;
this.workspace = createWebWorkspace();
@@ -109,7 +103,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
}
}
public abstract void addSessionError(@NotNull Throwable exception);
public abstract void addSessionError(Throwable exception);
public void addEventHandler(@NotNull CBWebSessionEventHandler handler) {
synchronized (sessionEventHandlers) {
@@ -123,21 +117,11 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
}
}
public void migrateEventHandlersTo(@NotNull BaseWebSession target) {
synchronized (sessionEventHandlers) {
for (CBWebSessionEventHandler handler : sessionEventHandlers) {
handler.migrateToSession(target);
target.addEventHandler(handler);
}
sessionEventHandlers.clear();
}
}
public boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
public synchronized boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
return userContext.refresh(smAuthInfo);
}
public void refreshUserData() {
public synchronized void refreshUserData() {
try {
userContext.refreshPermissions();
if (userContext.isAuthorizedInSecurityManager()) {
@@ -167,8 +151,8 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
return workspace.getAuthContext();
}
protected synchronized void clearSessionContext() {
this.workspace.getAuthContext().clearContext(false);
protected void clearSessionContext() {
this.workspace.getAuthContext().clear();
this.workspace.getAuthContext().addSession(this);
}
@@ -193,33 +177,27 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
return lastAccessTime;
}
@NotNull
public SessionType getSessionType() {
return sessionType;
}
public void touchSession() {
public synchronized void touchSession() {
this.lastAccessTime = System.currentTimeMillis();
}
@NotNull
public WebUserContext getUserContext() {
public synchronized WebUserContext getUserContext() {
return userContext;
}
@Override
public void close() {
cleanUpSession(true);
super.close();
cleanUpSession(true);
}
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
cleanUpSession(sendSessionExpiredEvent);
super.close();
}
private void cleanUpSession(boolean sendSessionExpiredEvent) {
application.getEventController().addEvent(new WSEventDeleteTempFile(getUserContext().getSmSessionId()));
application.getEventController().addEvent(new WSEventDeleteTempFile(getSessionId()));
synchronized (sessionEventHandlers) {
var sessionExpiredEvent = new WSSessionExpiredEvent();
for (CBWebSessionEventHandler sessionEventHandler : sessionEventHandlers) {
@@ -249,7 +227,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
this.eventsFilter = eventsFilter;
}
public boolean isProjectAccessible(@NotNull String projectId) {
public boolean isProjectAccessible(String projectId) {
return userContext.getAccessibleProjectIds().contains(projectId);
}
@@ -262,11 +240,11 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
}
public void removeSessionProject(@NotNull String projectId) throws DBException {
public void removeSessionProject(@Nullable String projectId) throws DBException {
userContext.getAccessibleProjectIds().remove(projectId);
}
public abstract void addSessionMessage(@NotNull WebServerMessage message);
public abstract void addSessionMessage(WebServerMessage message);
@Property
public boolean isValid() {
@@ -276,12 +254,8 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
@Property
public long getRemainingTime() {
if (application instanceof ServletAuthApplication authApplication) {
return getMaxSessionIdleTime(authApplication) + lastAccessTime - System.currentTimeMillis();
return authApplication.getMaxSessionIdleTime() + lastAccessTime - System.currentTimeMillis();
}
return Integer.MAX_VALUE;
}
protected long getMaxSessionIdleTime(@NotNull ServletAuthApplication authApplication) {
return authApplication.getMaxSessionIdleTime();
}
}
@@ -1,23 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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.session;
public enum SessionType {
WEB,
HEADLESS,
API_TOKEN
}
@@ -41,7 +41,6 @@ public class WebAuthInfo implements SMSessionPrincipal, WebUserAuthToken {
private final WebSession session;
private final WebUser user;
@NotNull
private final WebAuthProviderDescriptor authProvider;
private String authProviderConfigurationId;
@NotNull
@@ -72,7 +71,6 @@ public class WebAuthInfo implements SMSessionPrincipal, WebUserAuthToken {
this.loginTime = loginTime;
}
@NotNull
@Property
public OffsetDateTime getLoginTime() {
return loginTime;
@@ -93,19 +91,16 @@ public class WebAuthInfo implements SMSessionPrincipal, WebUserAuthToken {
return new WebUserOriginInfo(session, user, authProvider);
}
@NotNull
@Property
public String getUserId() {
return userIdentity.getId();
}
@NotNull
@Property
public String getDisplayName() {
return userIdentity.getDisplayName();
}
@NotNull
@Property
public String getAuthProvider() {
return authProvider.getId();
@@ -143,11 +138,13 @@ public class WebAuthInfo implements SMSessionPrincipal, WebUserAuthToken {
}
void closeAuth() {
try {
SMAuthProvider authProviderInstance = this.authProvider.getInstance();
authProviderInstance.closeSession(session, authSession);
} catch (Exception e) {
log.error(e);
if (authProvider != null && authSession != null) {
try {
SMAuthProvider authProviderInstance = this.authProvider.getInstance();
authProviderInstance.closeSession(session, authSession);
} catch (Exception e) {
log.error(e);
}
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,16 +31,16 @@ public class WebHeadlessSession extends BaseWebSession {
@NotNull String id,
@NotNull ServletAuthApplication application
) throws DBException {
super(id, application, SessionType.HEADLESS);
super(id, application);
}
@Override
public void addSessionError(@NotNull Throwable exception) {
public void addSessionError(Throwable exception) {
}
@Override
public void addSessionMessage(@NotNull WebServerMessage message) {
public void addSessionMessage(WebServerMessage message) {
}
@@ -17,7 +17,6 @@
package io.cloudbeaver.model.session;
import jakarta.servlet.http.HttpServletRequest;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.utils.HttpConstants;
@@ -32,16 +31,12 @@ public class WebHttpRequestInfo {
private final String lastRemoteAddress;
@Nullable
private final String lastRemoteUserAgent;
@NotNull
private final SessionType sessionType;
public WebHttpRequestInfo(HttpServletRequest request) {
this(
request.getSession() == null ? null : request.getSession().getId(),
request.getAttribute("locale"),
request.getRemoteAddr(),
request.getHeader(USER_AGENT)
);
this.id = request.getSession() == null ? null : request.getSession().getId();
this.locale = request.getAttribute("locale");
this.lastRemoteAddress = request.getRemoteAddr();
this.lastRemoteUserAgent = request.getHeader(USER_AGENT);
}
public WebHttpRequestInfo(
@@ -49,25 +44,13 @@ public class WebHttpRequestInfo {
@Nullable Object locale,
@Nullable String lastRemoteAddress,
@Nullable String lastRemoteUserAgent
) {
this(id, locale, lastRemoteAddress, lastRemoteUserAgent, SessionType.WEB);
}
public WebHttpRequestInfo(
@Nullable String id,
@Nullable Object locale,
@Nullable String lastRemoteAddress,
@Nullable String lastRemoteUserAgent,
@NotNull SessionType sessionType
) {
this.id = id;
this.locale = locale;
this.lastRemoteAddress = lastRemoteAddress;
this.lastRemoteUserAgent = lastRemoteUserAgent;
this.sessionType = sessionType;
}
@Nullable
public String getId() {
return id;
}
@@ -86,9 +69,4 @@ public class WebHttpRequestInfo {
public String getLastRemoteUserAgent() {
return lastRemoteUserAgent;
}
@NotNull
public SessionType getSessionType() {
return sessionType;
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* 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.
@@ -17,7 +17,10 @@
package io.cloudbeaver.model.session;
import io.cloudbeaver.*;
import io.cloudbeaver.model.*;
import io.cloudbeaver.model.CustomCancelableJob;
import io.cloudbeaver.model.WebAsyncTaskInfo;
import io.cloudbeaver.model.WebConnectionInfo;
import io.cloudbeaver.model.WebServerMessage;
import io.cloudbeaver.model.app.ServletApplication;
import io.cloudbeaver.model.app.ServletAuthApplication;
import io.cloudbeaver.model.session.monitor.TaskProgressMonitor;
@@ -27,6 +30,7 @@ import io.cloudbeaver.service.sql.WebSQLConstants;
import io.cloudbeaver.utils.CBModelConstants;
import io.cloudbeaver.utils.WebDataSourceUtils;
import io.cloudbeaver.utils.WebEventUtils;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.jkiss.code.NotNull;
@@ -34,7 +38,6 @@ import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBFileController;
import org.jkiss.dbeaver.model.DBPAdaptable;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBPEventListener;
import org.jkiss.dbeaver.model.access.DBAAuthCredentials;
@@ -42,6 +45,7 @@ import org.jkiss.dbeaver.model.access.DBACredentialsProvider;
import org.jkiss.dbeaver.model.auth.*;
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
import org.jkiss.dbeaver.model.exec.DBCException;
import org.jkiss.dbeaver.model.fs.DBFFileSystemManager;
import org.jkiss.dbeaver.model.meta.Association;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.navigator.DBNModel;
@@ -62,10 +66,8 @@ import java.lang.reflect.InvocationTargetException;
import java.nio.file.Path;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Web session.
@@ -73,16 +75,16 @@ import java.util.stream.Collectors;
*/
//TODO: split to authenticated and non authenticated context
public class WebSession extends BaseWebSession
implements SMSessionWithAuth, SMCredentialsProvider, DBACredentialsProvider, DBPAdaptable {
implements SMSessionWithAuth, SMCredentialsProvider, DBACredentialsProvider, IAdaptable {
private static final Log log = Log.getLog(WebSession.class);
public static final SMSessionType CB_SESSION_TYPE = new SMSessionType("CloudBeaver");
private static final String WEB_SESSION_AUTH_CONTEXT_TYPE = "web-session";
private static final String ATTR_LOCALE = "locale";
private static final AtomicInteger TASK_ID = new AtomicInteger();
public static String RUNTIME_PARAM_AUTH_INFOS = "auth-infos";
public static String RUNTIME_PARAM_CLIENT_ORIGIN = "client-origin";
private final AtomicInteger taskCount = new AtomicInteger();
private final String lastRemoteAddr;
@@ -90,34 +92,31 @@ public class WebSession extends BaseWebSession
private String locale;
private boolean cacheExpired;
private String clientOrigin;
protected WebSessionGlobalProjectImpl globalProject;
private final List<WebServerMessage> sessionMessages = new ArrayList<>();
private final Map<String, WebAsyncTaskInfo> asyncTasks = new HashMap<>();
private final Map<String, Function<Object, Object>> attributeDisposers = new HashMap<>();
// Map of auth tokens. Key is authentication provider
private final List<WebAuthInfo> authTokens = new ArrayList<>();
private DBNModel navigatorModel;
private final ReadWriteLock navigatorModelLock = new ReentrantReadWriteLock(true);
private final DBRProgressMonitor progressMonitor = new SessionProgressMonitor();
private final Map<String, DBWSessionHandler<WebSession>> sessionHandlers;
private final Map<String, DBWSessionHandler> sessionHandlers;
private final WebDataSourceConnectEventListener connectListener = new WebDataSourceConnectEventListener(this);
public WebSession(
@NotNull WebHttpRequestInfo requestInfo,
@NotNull ServletAuthApplication application,
@NotNull Map<String, DBWSessionHandler<WebSession>> sessionHandlers
@NotNull Map<String, DBWSessionHandler> sessionHandlers
) throws DBException {
this(
Objects.requireNonNull(requestInfo.getId()),
this(requestInfo.getId(),
CommonUtils.toString(requestInfo.getLocale()),
application,
sessionHandlers,
requestInfo.getLastRemoteAddress(),
requestInfo.getSessionType()
requestInfo.getLastRemoteAddress()
);
updateSessionParameters(requestInfo);
}
@@ -126,21 +125,10 @@ public class WebSession extends BaseWebSession
@NotNull String id,
@Nullable String locale,
@NotNull ServletApplication application,
@NotNull Map<String, DBWSessionHandler<WebSession>> sessionHandlers,
@Nullable String remoteAddr
@NotNull Map<String, DBWSessionHandler> sessionHandlers,
@NotNull String remoteAddr
) throws DBException {
this(id, locale, application, sessionHandlers, remoteAddr, SessionType.WEB);
}
protected WebSession(
@NotNull String id,
@Nullable String locale,
@NotNull ServletApplication application,
@NotNull Map<String, DBWSessionHandler<WebSession>> sessionHandlers,
@Nullable String remoteAddr,
@NotNull SessionType sessionType
) throws DBException {
super(id, application, sessionType);
super(id, application);
if (CommonUtils.isEmpty(remoteAddr)) {
throw new DBException("Remote address cannot be empty");
}
@@ -163,7 +151,7 @@ public class WebSession extends BaseWebSession
if (authTokens.isEmpty()) {
return null;
}
return authTokens.getFirst();
return authTokens.get(0);
}
}
@@ -200,12 +188,11 @@ public class WebSession extends BaseWebSession
this.cacheExpired = cacheExpired;
}
public WebUser getUser() {
public synchronized WebUser getUser() {
return this.userContext.getUser();
}
@NotNull
public Map<String, String> getUserMetaParameters() {
public synchronized Map<String, String> getUserMetaParameters() {
var user = getUser();
if (user == null) {
return Map.of();
@@ -217,20 +204,20 @@ public class WebSession extends BaseWebSession
return allMetaParams;
}
public String getUserId() {
public synchronized String getUserId() {
return userContext.getUserId();
}
public synchronized boolean hasPermission(String perm) {
Set<String> sessionPermissions = getSessionPermissions();
return sessionPermissions.contains(DBWConstants.PERMISSION_ADMIN) || sessionPermissions.contains(perm);
return getSessionPermissions().contains(DBWConstants.PERMISSION_ADMIN) ||
getSessionPermissions().contains(perm);
}
public boolean isAuthorizedInSecurityManager() {
public synchronized boolean isAuthorizedInSecurityManager() {
return userContext.isAuthorizedInSecurityManager();
}
public Set<String> getSessionPermissions() {
public synchronized Set<String> getSessionPermissions() {
if (userContext.getUserPermissions() == null) {
refreshSessionAuth();
}
@@ -238,30 +225,28 @@ public class WebSession extends BaseWebSession
}
@NotNull
public SMController getSecurityController() {
public synchronized SMController getSecurityController() {
return userContext.getSecurityController();
}
@NotNull
public SMAdminController getAdminSecurityController() throws DBException {
public synchronized SMAdminController getAdminSecurityController() throws DBException {
if (!hasPermission(DBWConstants.PERMISSION_ADMIN)) {
throw new DBException("Admin permissions required");
}
return userContext.getAdminSecurityController();
}
@NotNull
public RMController getRmController() {
public synchronized RMController getRmController() {
return userContext.getRmController();
}
@NotNull
public DBFileController getFileController() {
public synchronized DBFileController getFileController() {
return userContext.getFileController();
}
@Override
public void refreshUserData() {
public synchronized void refreshUserData() {
super.refreshUserData();
refreshSessionAuth();
@@ -269,58 +254,39 @@ public class WebSession extends BaseWebSession
}
// Note: for admin use only
public void resetUserState(boolean needResetUserCache) throws DBException {
public synchronized void resetUserState() throws DBException {
clearAuthTokens();
if (needResetUserCache) {
try {
resetSessionCache();
} catch (DBCException e) {
addSessionError(e);
log.error(e);
}
try {
resetSessionCache();
} catch (DBCException e) {
addSessionError(e);
log.error(e);
}
refreshUserData();
clearSessionContext();
}
public void resetUserState() throws DBException {
resetUserState(true);
}
@NotNull
public DBPEventListener getDataSourceConnectListener() {
return connectListener;
}
@NotNull
public String getClientOrigin() {
return clientOrigin;
}
public void updateClientOrigin(@NotNull String originFromRequest) {
this.clientOrigin = originFromRequest;
}
private void initNavigatorModel() {
navigatorModelLock.writeLock().lock();
try {
// Cleanup current data
if (this.navigatorModel != null) {
this.navigatorModel.dispose();
this.navigatorModel = null;
}
this.globalProject = null;
loadProjects();
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), getWorkspace());
this.navigatorModel.setModelAuthContext(getWorkspace().getAuthContext());
this.navigatorModel.initialize();
this.locale = Locale.getDefault().getLanguage();
} finally {
navigatorModelLock.writeLock().unlock();
// Cleanup current data
if (this.navigatorModel != null) {
this.navigatorModel.dispose();
this.navigatorModel = null;
}
this.globalProject = null;
loadProjects();
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), getWorkspace().getProjects());
this.navigatorModel.setModelAuthContext(getWorkspace().getAuthContext());
this.navigatorModel.initialize();
this.locale = Locale.getDefault().getLanguage();
}
private void loadProjects() {
@@ -343,7 +309,7 @@ public class WebSession extends BaseWebSession
anonymousProject.setInMemory(true);
}
if (workspace.getActiveProject() == null && !workspace.getProjects().isEmpty()) {
workspace.setActiveProject(workspace.getProjects().getFirst());
workspace.setActiveProject(workspace.getProjects().get(0));
}
} catch (DBException e) {
addSessionError(e);
@@ -351,14 +317,14 @@ public class WebSession extends BaseWebSession
}
}
@NotNull
private WebSessionProjectImpl createWebProject(@NotNull RMProject project) throws DBException {
private WebSessionProjectImpl createWebProject(RMProject project) throws DBException {
WebSessionProjectImpl sessionProject;
if (project.isGlobal()) {
sessionProject = createGlobalProject(project);
} else {
sessionProject = createSessionProject(project);
}
sessionProject.refreshProjectSettings();
// do not load data sources for anonymous project
if (project.getType() == RMProjectType.USER && userContext.getUser() == null) {
sessionProject.setInMemory(true);
@@ -367,10 +333,13 @@ public class WebSession extends BaseWebSession
if (!project.isShared() || application.isConfigurationMode()) {
getWorkspace().setActiveProject(sessionProject);
}
log.info(String.format(
"Project created: [ID=%s, Name=%s, Type=%s, Creator=%s]",
project.getId(), project.getName(), project.getType(), project.getCreator()
));
return sessionProject;
}
@NotNull
protected WebSessionProjectImpl createSessionProject(@NotNull RMProject project) throws DBException {
return new WebSessionProjectImpl(this, project, getProjectPath(project));
}
@@ -380,24 +349,32 @@ public class WebSession extends BaseWebSession
return RMUtils.getProjectPath(project);
}
@NotNull
protected WebSessionProjectImpl createGlobalProject(RMProject project) {
globalProject = new WebSessionGlobalProjectImpl(this, project);
globalProject.refreshAccessibleConnectionIds();
return globalProject;
}
private void resetNavigationModel() {
navigatorModelLock.writeLock().lock();
try {
getWorkspace().getProjects().forEach(WebSessionProjectImpl::dispose);
if (this.navigatorModel != null) {
this.navigatorModel.dispose();
this.navigatorModel = null;
private void resetSessionCache() throws DBCException {
// Clear attributes
synchronized (attributes) {
for (Map.Entry<String, Function<Object, Object>> attrDisposer : attributeDisposers.entrySet()) {
Object attrValue = attributes.get(attrDisposer.getKey());
attrDisposer.getValue().apply(attrValue);
}
} finally {
navigatorModelLock.writeLock().unlock();
attributeDisposers.clear();
// Remove all non-persistent attributes
attributes.entrySet().removeIf(
entry -> !(entry.getValue() instanceof PersistentAttribute));
}
}
private void resetNavigationModel() {
getWorkspace().getProjects().forEach(WebSessionProjectImpl::dispose);
if (this.navigatorModel != null) {
this.navigatorModel.dispose();
this.navigatorModel = null;
}
}
@@ -440,25 +417,15 @@ public class WebSession extends BaseWebSession
@Nullable
public DBNModel getNavigatorModel() {
navigatorModelLock.readLock().lock();
try {
return navigatorModel;
} finally {
navigatorModelLock.readLock().unlock();
}
return navigatorModel;
}
@NotNull
public DBNModel getNavigatorModelOrThrow() throws DBWebException {
navigatorModelLock.readLock().lock();
try {
if (navigatorModel != null) {
return navigatorModel;
}
throw new DBWebException("Navigator model is not found in session");
} finally {
navigatorModelLock.readLock().unlock();
if (navigatorModel != null) {
return navigatorModel;
}
throw new DBWebException("Navigator model is not found in session");
}
/**
* Returns and clears progress messages
@@ -472,8 +439,8 @@ public class WebSession extends BaseWebSession
}
}
public void updateInfo(boolean isOldHttpSessionUsed) {
log.trace("Update session lifetime " + getSessionId() + " for user " + getUserId());
public synchronized void updateInfo(boolean isOldHttpSessionUsed) {
log.debug("Update session lifetime " + getSessionId() + " for user " + getUserId());
touchSession();
if (isOldHttpSessionUsed) {
try {
@@ -504,6 +471,7 @@ public class WebSession extends BaseWebSession
public void close() {
try {
resetNavigationModel();
resetSessionCache();
} catch (Throwable e) {
log.error(e);
}
@@ -520,6 +488,7 @@ public class WebSession extends BaseWebSession
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
try {
resetNavigationModel();
resetSessionCache();
} catch (Throwable e) {
log.error(e);
}
@@ -534,7 +503,6 @@ public class WebSession extends BaseWebSession
super.close(clearTokens, sendSessionExpiredEvent);
}
@NotNull
private List<WebAuthInfo> clearAuthTokens() throws DBException {
ArrayList<WebAuthInfo> tokensCopy;
synchronized (authTokens) {
@@ -591,10 +559,6 @@ public class WebSession extends BaseWebSession
if (job instanceof CustomCancelableJob cancelableJob) {
cancelableJob.cancelJob(this, taskInfo);
}
CompletableFuture<?> future = getAttribute(getTaskConfirmationAttributeName(taskId));
if (future != null) {
future.cancel(false);
}
if (job != null) {
job.cancel();
}
@@ -604,7 +568,8 @@ public class WebSession extends BaseWebSession
public WebAsyncTaskInfo createAsyncTask(@NotNull String taskName) {
int taskId = TASK_ID.incrementAndGet();
return getAsyncTask(String.valueOf(taskId), taskName, true);
WebAsyncTaskInfo asyncTask = getAsyncTask(String.valueOf(taskId), taskName, true);
return asyncTask;
}
public List<WebAsyncTaskInfo> findTasksByJob(@NotNull Class<? extends AbstractJob> jobClass) {
@@ -619,18 +584,12 @@ public class WebSession extends BaseWebSession
}
}
@NotNull
public WebAsyncTaskInfo createAndRunAsyncTask(@NotNull String taskName, @NotNull WebAsyncTaskProcessor<?> runnable) {
WebAsyncTaskInfo asyncTask = createAsyncTask(taskName);
return runAsyncTask(asyncTask, runnable);
}
@NotNull
public WebAsyncTaskInfo runAsyncTask(@NotNull WebAsyncTaskInfo asyncTask, @NotNull WebAsyncTaskProcessor<?> runnable) {
AbstractJob job = new AbstractCancelableJob(asyncTask.getName()) {
@NotNull
AbstractJob job = new AbstractJob(taskName) {
@Override
protected IStatus run(@NotNull DBRProgressMonitor monitor) {
protected IStatus run(DBRProgressMonitor monitor) {
int curTaskCount = taskCount.incrementAndGet();
DBRProgressMonitor taskMonitor = new TaskProgressMonitor(monitor, WebSession.this, asyncTask);
@@ -666,11 +625,11 @@ public class WebSession extends BaseWebSession
return asyncTask;
}
public void addSessionError(@NotNull Throwable exception) {
public void addSessionError(Throwable exception) {
addSessionMessage(new WebServerMessage(exception));
}
public void addSessionMessage(@NotNull WebServerMessage message) {
public void addSessionMessage(WebServerMessage message) {
synchronized (sessionMessages) {
sessionMessages.add(message);
}
@@ -681,15 +640,14 @@ public class WebSession extends BaseWebSession
message.getMessage()));
}
public void addInfoMessage(@NotNull String message) {
public void addInfoMessage(String message) {
addSessionMessage(new WebServerMessage(MessageType.INFO, message));
}
public void addWarningMessage(@NotNull String message) {
public void addWarningMessage(String message) {
addSessionMessage(new WebServerMessage(MessageType.WARNING, message));
}
@NotNull
public List<WebServerMessage> readLog(Integer maxEntries, Boolean clearLog) {
synchronized (sessionMessages) {
List<WebServerMessage> messages = new ArrayList<>();
@@ -709,6 +667,42 @@ public class WebSession extends BaseWebSession
}
}
@Override
public <T> T getAttribute(String name) {
synchronized (attributes) {
Object value = attributes.get(name);
if (value instanceof PersistentAttribute persistentAttribute) {
value = persistentAttribute.value();
}
return (T) value;
}
}
public void setAttribute(String name, Object value, boolean persistent) {
synchronized (attributes) {
attributes.put(name, persistent ? new PersistentAttribute(value) : value);
}
}
public <T> T getAttribute(String name, Function<T, T> creator, Function<T, T> disposer) {
synchronized (attributes) {
Object value = attributes.get(name);
if (value instanceof PersistentAttribute persistentAttribute) {
value = persistentAttribute.value();
}
if (value == null) {
value = creator.apply(null);
if (value != null) {
attributes.put(name, value);
if (disposer != null) {
attributeDisposers.put(name, (Function<Object, Object>) disposer);
}
}
}
return (T) value;
}
}
@Property
public Map<String, Object> getActionParameters() {
WebActionParameters action = WebActionParameters.fromSession(this, true);
@@ -726,7 +720,7 @@ public class WebSession extends BaseWebSession
}
return null;
}
return authTokens.isEmpty() ? null : authTokens.getFirst();
return authTokens.isEmpty() ? null : authTokens.get(0);
}
}
@@ -737,7 +731,7 @@ public class WebSession extends BaseWebSession
}
}
@NotNull
public List<WebAuthInfo> getAllAuthInfo() {
synchronized (authTokens) {
return new ArrayList<>(authTokens);
@@ -769,7 +763,10 @@ public class WebSession extends BaseWebSession
if (oldAuthInfo != null) {
removeAuthInfo(oldAuthInfo);
}
getSessionContext().addSession(authInfo.getAuthSession());
SMSession authSession = authInfo.getAuthSession();
if (authSession != null) {
getSessionContext().addSession(authSession);
}
}
synchronized (authTokens) {
Collections.addAll(authTokens, tokens);
@@ -789,7 +786,7 @@ public class WebSession extends BaseWebSession
});
}
private void removeAuthInfo(@NotNull WebAuthInfo oldAuthInfo) {
private void removeAuthInfo(WebAuthInfo oldAuthInfo) {
oldAuthInfo.closeAuth();
synchronized (authTokens) {
authTokens.remove(oldAuthInfo);
@@ -815,7 +812,6 @@ public class WebSession extends BaseWebSession
return oldInfo;
}
@Nullable
public List<DBACredentialsProvider> getContextCredentialsProviders() {
return getAdapters(DBACredentialsProvider.class);
}
@@ -834,7 +830,6 @@ public class WebSession extends BaseWebSession
contextCredentialsProvider.provideAuthParameters(monitor, dataSourceContainer, configuration);
}
configuration.setRuntimeAttribute(RUNTIME_PARAM_AUTH_INFOS, getAllAuthInfo());
configuration.setRuntimeAttribute(RUNTIME_PARAM_CLIENT_ORIGIN, this.clientOrigin);
WebSessionProjectImpl project = getProjectById(dataSourceContainer.getProject().getId());
if (project != null) {
@@ -847,7 +842,7 @@ public class WebSession extends BaseWebSession
// uncommented because we had the problem with non-native auth models
// (for example, can't connect to DynamoDB if credentials are not saved)
DBAAuthCredentials credentials = configuration.getAuthModel().loadCredentials(dataSourceContainer, configuration);
WebDataSourceUtils.updateCredentialsFromProperties(this.progressMonitor, credentials, configuration.getAuthProperties());
WebDataSourceUtils.updateCredentialsFromProperties(credentials, configuration.getAuthProperties());
configuration.getAuthModel().provideCredentials(dataSourceContainer, configuration, credentials);
} catch (DBException e) {
@@ -865,7 +860,7 @@ public class WebSession extends BaseWebSession
// May be called to extract auth information from session
@Override
public <T> T getAdapter(@NotNull Class<T> adapter) {
public <T> T getAdapter(Class<T> adapter) {
synchronized (authTokens) {
for (WebAuthInfo authInfo : authTokens) {
if (isAuthInfoInstanceOf(authInfo, adapter)) {
@@ -887,7 +882,7 @@ public class WebSession extends BaseWebSession
}
private <T> boolean isAuthInfoInstanceOf(WebAuthInfo authInfo, Class<T> adapter) {
if (authInfo != null) {
if (authInfo != null && authInfo.getAuthSession() != null) {
return adapter.isInstance(authInfo.getAuthSession());
}
return false;
@@ -903,11 +898,11 @@ public class WebSession extends BaseWebSession
return parameters;
}
public void resetAuthToken() throws DBException {
public synchronized void resetAuthToken() throws DBException {
this.userContext.reset();
}
public boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
public synchronized boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
boolean contextChanged = super.updateSMSession(smAuthInfo);
if (contextChanged) {
refreshUserData();
@@ -926,7 +921,7 @@ public class WebSession extends BaseWebSession
}
@Nullable
public WebSessionProjectImpl getProjectById(@NotNull String projectId) {
public WebSessionProjectImpl getProjectById(@Nullable String projectId) {
return getWorkspace().getProjectById(projectId);
}
@@ -935,7 +930,6 @@ public class WebSession extends BaseWebSession
*
* @throws DBWebException if project with provided id is not found.
*/
@NotNull
public WebSessionProjectImpl getAccessibleProjectById(@Nullable String projectId) throws DBWebException {
WebSessionProjectImpl project = null;
if (projectId != null) {
@@ -947,7 +941,6 @@ public class WebSession extends BaseWebSession
return project;
}
@NotNull
public List<WebSessionProjectImpl> getAccessibleProjects() {
return getWorkspace().getProjects();
}
@@ -957,35 +950,26 @@ public class WebSession extends BaseWebSession
*/
public void addSessionProject(@NotNull WebSessionProjectImpl project) {
getWorkspace().addProject(project);
navigatorModelLock.readLock().lock();
try {
if (navigatorModel != null) {
navigatorModel.getRoot().addProject(project, false);
}
} finally {
navigatorModelLock.readLock().unlock();
if (navigatorModel != null) {
navigatorModel.getRoot().addProject(project, false);
}
}
/**
* Removes project from session cache and navigator tree.
*/
public void deleteSessionProject(@NotNull WebSessionProjectImpl project) {
RMProject rmProject = project.getRMProject();
log.info(String.format(
"Project deleted: [ID=%s, Name=%s, Type=%s, Creator=%s]",
rmProject.getId(), rmProject.getName(), rmProject.getType(), rmProject.getCreator()
));
project.dispose();
public void deleteSessionProject(@Nullable WebSessionProjectImpl project) {
if (project != null) {
RMProject rmProject = project.getRMProject();
log.info(String.format(
"Project deleted: [ID=%s, Name=%s, Type=%s, Creator=%s]",
rmProject.getId(), rmProject.getName(), rmProject.getType(), rmProject.getCreator()
));
project.dispose();
}
getWorkspace().removeProject(project);
navigatorModelLock.readLock().lock();
try {
if (navigatorModel != null) {
navigatorModel.getRoot().removeProject(project);
}
} finally {
navigatorModelLock.readLock().unlock();
if (navigatorModel != null) {
navigatorModel.getRoot().removeProject(project);
}
}
@@ -993,10 +977,6 @@ public class WebSession extends BaseWebSession
public void addSessionProject(@NotNull String projectId) throws DBException {
super.addSessionProject(projectId);
var rmProject = getRmController().getProject(projectId, false, false);
if (rmProject == null) {
log.error("RM project '" + projectId + "' not found");
return;
}
createWebProject(rmProject);
}
@@ -1011,7 +991,7 @@ public class WebSession extends BaseWebSession
}
@Override
public void removeSessionProject(@NotNull String projectId) throws DBException {
public void removeSessionProject(@Nullable String projectId) throws DBException {
super.removeSessionProject(projectId);
var project = getProjectById(projectId);
if (project == null) {
@@ -1020,6 +1000,15 @@ public class WebSession extends BaseWebSession
deleteSessionProject(project);
}
@NotNull
public DBFFileSystemManager getFileSystemManager(String projectId) throws DBException {
var project = getProjectById(projectId);
if (project == null) {
throw new DBException("Project not found: " + projectId);
}
return project.getFileSystemManager();
}
@NotNull
public WebSessionPreferenceStore getUserPreferenceStore() {
return getUserContext().getPreferenceStore();
@@ -1030,59 +1019,14 @@ public class WebSession extends BaseWebSession
return globalProject;
}
public void handleTaskConfirmation(
@NotNull String taskId,
boolean confirmed,
boolean skipConfirmations
) {
String attributeName = getTaskConfirmationAttributeName(taskId);
if (confirmed && skipConfirmations) {
setAttribute(WebSQLConstants.SKIP_TASK_CONFIRMATIONS_ATTR, Boolean.TRUE);
}
CompletableFuture<Boolean> confirmationFuture = getAttribute(attributeName);
if (confirmationFuture != null) {
confirmationFuture.complete(confirmed);
removeAttribute(attributeName);
} else {
log.error("Received unexpected confirmation event for taskId: " + taskId);
}
}
public void handleTaskConfirmationWithParameters(@NotNull String taskId, @NotNull Map<String, Object> parameters) {
String attributeName = getTaskConfirmationAttributeName(taskId);
CompletableFuture<Map<String, Object>> confirmationFuture = getAttribute(attributeName);
if (confirmationFuture != null) {
confirmationFuture.complete(parameters);
removeAttribute(attributeName);
} else {
log.error("Received unexpected confirmation event for taskId: " + taskId);
}
}
public void handleActionCancelledEvent(@NotNull String actionId) {
Object attributeValue = getAttribute(actionId);
if (!(attributeValue instanceof CompletableFuture<?> future)) {
log.info("Action to be cancelled not found, skipping: " + actionId);
return;
}
future.completeExceptionally(new DBWebException("Action cancelled by user: " + actionId));
removeAttribute(actionId);
}
@NotNull
private String getTaskConfirmationAttributeName(@NotNull String taskId) {
return WebSQLConstants.TASK_CONFIRMATION_ATTR_PREFIX + taskId;
}
private class SessionProgressMonitor extends BaseProgressMonitor {
@Override
public void beginTask(@NotNull String name, int totalWork) {
public void beginTask(String name, int totalWork) {
addInfoMessage(name);
}
@Override
public void subTask(@NotNull String name) {
public void subTask(String name) {
addInfoMessage(name);
}
}
@@ -1091,4 +1035,6 @@ public class WebSession extends BaseWebSession
return true;
}
private record PersistentAttribute(Object value) {
}
}
@@ -74,7 +74,7 @@ public class WebSessionAuthProcessor {
}
} catch (Exception e) {
if (resetUserStateOnError) {
webSession.resetUserState(false);
webSession.resetUserState();
}
throw new DBException(e.getMessage(), e);
}
@@ -20,7 +20,6 @@ import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.impl.preferences.AbstractUserPreferenceStore;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
import org.jkiss.utils.CommonUtils;
import java.io.IOException;
import java.util.Map;
@@ -86,13 +85,4 @@ public class WebSessionPreferenceStore extends AbstractUserPreferenceStore {
public void save() throws IOException {
throw new RuntimeException("Not implemented");
}
public boolean getUserPreferenceBoolean(@NotNull String name, boolean fallbackValue) {
String value = CommonUtils.toString(userPreferences.get(name));
if (value.isEmpty()) {
return fallbackValue;
} else {
return toBoolean(value);
}
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,17 +17,12 @@
package io.cloudbeaver.model.session;
import io.cloudbeaver.WebSessionProjectImpl;
import io.cloudbeaver.utils.ServletAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPAdaptable;
import org.jkiss.dbeaver.model.DBPImage;
import org.jkiss.dbeaver.model.app.DBPPlatform;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.fs.DBFFileSystemManager;
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
import org.jkiss.dbeaver.model.rm.RMUtils;
import org.jkiss.dbeaver.runtime.DBWorkbench;
@@ -41,13 +36,11 @@ import java.util.List;
* Web workspace
*/
public class WebSessionWorkspace implements DBPWorkspace {
private static final Log log = Log.getLog(WebSessionWorkspace.class);
private final BaseWebSession session;
private final SessionContextImpl workspaceAuthContext;
private final List<WebSessionProjectImpl> accessibleProjects = new ArrayList<>();
private WebSessionProjectImpl activeProject;
private DBFFileSystemManager fileSystemManager;
public WebSessionWorkspace(BaseWebSession session) {
this.session = session;
@@ -94,9 +87,7 @@ public class WebSessionWorkspace implements DBPWorkspace {
@NotNull
@Override
public List<WebSessionProjectImpl> getProjects() {
synchronized (accessibleProjects) {
return new ArrayList<>(accessibleProjects);
}
return accessibleProjects;
}
@Nullable
@@ -108,30 +99,26 @@ public class WebSessionWorkspace implements DBPWorkspace {
@Nullable
@Override
public WebSessionProjectImpl getProject(@NotNull String projectName) {
synchronized (accessibleProjects) {
for (WebSessionProjectImpl project : accessibleProjects) {
if (project.getName().equals(projectName)) {
return project;
}
for (WebSessionProjectImpl project : accessibleProjects) {
if (project.getName().equals(projectName)) {
return project;
}
}
log.error("Project name '" + projectName + "' not found in session workspace");
return null;
}
@Nullable
@Override
public WebSessionProjectImpl getProjectById(@NotNull String projectId) {
synchronized (accessibleProjects) {
for (WebSessionProjectImpl project : accessibleProjects) {
if (project.getId().equals(projectId)) {
return project;
}
if (projectId == null) {
return activeProject;
}
for (WebSessionProjectImpl project : accessibleProjects) {
if (project.getId().equals(projectId)) {
return project;
}
}
log.error("Project ID '" + projectId + "' not found in session workspace");
// FIXME: return null here
return activeProject;
return null;
}
@NotNull
@@ -156,74 +143,36 @@ public class WebSessionWorkspace implements DBPWorkspace {
return null;
}
@NotNull
@Override
public WebSessionProjectImpl createProject(@NotNull String name, @Nullable String description) throws DBException {
throw new DBException("Project creation is not supported in web session workspace");
}
@Override
public void deleteProject(@NotNull DBPProject project) throws DBException {
throw new DBException("Project removing is not supported in web session workspace");
}
@Override
public void renameProject(@NotNull DBPProject project, @NotNull String newName) throws DBException {
throw new DBException("Project renaming is not supported in web session workspace");
}
public void setActiveProject(@NotNull WebSessionProjectImpl activeProject) {
public void setActiveProject(WebSessionProjectImpl activeProject) {
this.activeProject = activeProject;
}
void addProject(@NotNull WebSessionProjectImpl project) {
synchronized (accessibleProjects) {
accessibleProjects.add(project);
}
void addProject(WebSessionProjectImpl project) {
accessibleProjects.add(project);
}
void removeProject(@NotNull WebSessionProjectImpl project) {
synchronized (accessibleProjects) {
accessibleProjects.remove(project);
}
void removeProject(WebSessionProjectImpl project) {
accessibleProjects.remove(project);
}
void clearProjects() {
synchronized (accessibleProjects) {
if (!this.accessibleProjects.isEmpty()) {
for (WebSessionProjectImpl project : accessibleProjects) {
project.dispose();
}
this.activeProject = null;
this.accessibleProjects.clear();
if (!this.accessibleProjects.isEmpty()) {
for (WebSessionProjectImpl project : accessibleProjects) {
project.dispose();
}
this.activeProject = null;
this.accessibleProjects.clear();
}
}
@Override
public boolean hasRealmPermission(@NotNull String permission) {
if (getWebSession() instanceof WebSession webSession) {
return webSession.getSessionPermissions().contains(permission);
}
return false;
}
@Override
public boolean supportsRealmFeature(@NotNull String feature) {
return ServletAppUtils.getServletApplication().getAppConfiguration().isFeatureEnabled(feature);
return false;
}
@NotNull
@Override
public synchronized DBFFileSystemManager getFileSystemManager() {
if (fileSystemManager == null) {
synchronized (this) {
if (fileSystemManager == null) {
fileSystemManager = new DBFFileSystemManager(session.getWorkspace());
}
}
}
return fileSystemManager;
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,9 +53,9 @@ public class WebUserContext implements SMCredentialsProvider {
private final ServletApplication application;
private final DBPWorkspace workspace;
private volatile WebUser user;
private volatile Set<String> userPermissions;
private volatile SMCredentials smCredentials = null;
private WebUser user;
private Set<String> userPermissions;
private SMCredentials smCredentials = null;
private String smSessionId;
private String refreshToken;
@@ -63,14 +63,11 @@ public class WebUserContext implements SMCredentialsProvider {
private SMAdminController adminSecurityController;
private DBSSecretController secretController;
private RMController rmController;
private final DBFileController fileController;
private final Set<String> accessibleProjectIds = new HashSet<>();
private DBFileController fileController;
private Set<String> accessibleProjectIds = new HashSet<>();
private final WebSessionPreferenceStore preferenceStore;
public WebUserContext(
@NotNull ServletApplication application,
@NotNull DBPWorkspace workspace
) throws DBException {
public WebUserContext(ServletApplication application, DBPWorkspace workspace) throws DBException {
this.application = application;
this.workspace = workspace;
this.securityController = application.createSecurityController(this);
@@ -87,7 +84,7 @@ public class WebUserContext implements SMCredentialsProvider {
* @return - true if context changed
* @throws DBException - if user already authorized and new token come from another user
*/
public boolean refresh(@NotNull SMAuthInfo smAuthInfo) throws DBException {
public synchronized boolean refresh(SMAuthInfo smAuthInfo) throws DBException {
if (smAuthInfo.getAuthPermissions() == null && !isAuthorizedInSecurityManager()) {
throw new DBCException("Required information about session permissions is missing");
}
@@ -175,56 +172,49 @@ public class WebUserContext implements SMCredentialsProvider {
}
@NotNull
public SMController getSecurityController() {
public synchronized SMController getSecurityController() {
return securityController;
}
@Nullable
@Override
public SMCredentials getActiveUserCredentials() {
public synchronized SMCredentials getActiveUserCredentials() {
return smCredentials;
}
public boolean isAuthorizedInSecurityManager() {
public synchronized boolean isAuthorizedInSecurityManager() {
return smCredentials != null;
}
public boolean isNonAnonymousUserAuthorizedInSM() {
public synchronized boolean isNonAnonymousUserAuthorizedInSM() {
return isAuthorizedInSecurityManager() && getUser() != null;
}
public boolean isAnonymousUserAuthorizedInSM() {
return isAuthorizedInSecurityManager() && getUser() == null;
}
@Nullable
public WebUser getUser() {
public synchronized WebUser getUser() {
return user;
}
@Nullable
public String getUserId() {
public synchronized String getUserId() {
return user == null ? null : user.getUserId();
}
protected void setUser(@Nullable WebUser user) {
protected synchronized void setUser(@Nullable WebUser user) {
this.user = user;
}
@NotNull
public SMAdminController getAdminSecurityController() {
public synchronized SMAdminController getAdminSecurityController() {
return adminSecurityController;
}
@Nullable
public Set<String> getUserPermissions() {
public synchronized Set<String> getUserPermissions() {
return userPermissions;
}
/**
* reread the current user's permissions
*/
public void refreshPermissions() throws DBException {
public synchronized void refreshPermissions() throws DBException {
if (isAuthorizedInSecurityManager()) {
log.debug("refresh permissions " + getUserId() + " " + getSmSessionId());
setUserPermissions(securityController.getTokenPermissions().getPermissions());
@@ -237,7 +227,6 @@ public class WebUserContext implements SMCredentialsProvider {
this.userPermissions = permissions;
}
@NotNull
public DBSSecretController getSecretController() throws DBException {
if (this.securityController == null) {
this.secretController = application.getSecretController(this, workspace.getAuthContext());
@@ -245,22 +234,18 @@ public class WebUserContext implements SMCredentialsProvider {
return secretController;
}
@Nullable
public String getSmSessionId() {
public synchronized String getSmSessionId() {
return smSessionId;
}
@Nullable
private Set<String> getDefaultPermissions() {
return application.isAnonymousAccessEnabled() ? null : Set.of();
}
@NotNull
public RMController getRmController() {
return rmController;
}
@NotNull
public DBFileController getFileController() {
return fileController;
}
@@ -274,7 +259,6 @@ public class WebUserContext implements SMCredentialsProvider {
this.refreshToken = refreshToken;
}
@NotNull
public WebSessionPreferenceStore getPreferenceStore() {
return preferenceStore;
}
@@ -40,14 +40,14 @@ public class TaskProgressMonitor extends ProxyProgressMonitor {
}
@Override
public void beginTask(@NotNull String name, int totalWork) {
public void beginTask(String name, int totalWork) {
super.beginTask(name, totalWork);
asyncTask.setStatus(name);
WebEventUtils.sendAsyncTaskEvent(webSession, asyncTask);
}
@Override
public void subTask(@NotNull String name) {
public void subTask(String name) {
super.subTask(name);
asyncTask.setStatus(name);
WebEventUtils.sendAsyncTaskEvent(webSession, asyncTask);
@@ -114,9 +114,4 @@ public class WebUser {
public String getDisableReason() {
return user.getDisableReason();
}
@Nullable
public Instant getLastLoginTime() {
return user.getLastLoginTime();
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* 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.
@@ -31,8 +31,6 @@ import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
import org.jkiss.dbeaver.model.security.SMAuthCredentialsProfile;
import org.jkiss.dbeaver.model.security.SMAuthProviderDescriptor;
import org.jkiss.dbeaver.model.security.SMSubjectType;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.dbeaver.utils.SystemVariablesResolver;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;
@@ -112,9 +110,7 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
}
public String getDescription() {
return GeneralUtils.replaceVariables(
cfg.getAttribute(WebRegistryConstant.ATTR_DESCRIPTION), SystemVariablesResolver.INSTANCE
);
return cfg.getAttribute(WebRegistryConstant.ATTR_DESCRIPTION);
}
public DBPImage getIcon() {
@@ -17,18 +17,12 @@
package io.cloudbeaver.registry;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.DBPConditionalProperty;
import org.jkiss.dbeaver.model.impl.LocalizedPropertyDescriptor;
import org.jkiss.dbeaver.utils.RuntimeUtils;
import org.osgi.framework.Bundle;
public class WebAuthProviderProperty extends LocalizedPropertyDescriptor implements DBPConditionalProperty {
private static final String WEB_AUTH_PROPERTY_PREFIX = "prop.auth.model.";
private final String[] requiredFeatures;
@Nullable
private final String type;
@@ -36,7 +30,6 @@ public class WebAuthProviderProperty extends LocalizedPropertyDescriptor impleme
private final String authProviderId;
private final String hideExpr;
private final String readOnlyExpr;
private final Bundle bundle;
public WebAuthProviderProperty(String category, IConfigurationElement config, String authProviderId) {
super(category, config);
@@ -46,7 +39,6 @@ public class WebAuthProviderProperty extends LocalizedPropertyDescriptor impleme
this.type = config.getAttribute("type");
this.hideExpr = config.getAttribute("hideExpr");
this.readOnlyExpr = config.getAttribute("readOnlyExpr");
this.bundle = extractBundle(config);
}
@NotNull
@@ -71,34 +63,12 @@ public class WebAuthProviderProperty extends LocalizedPropertyDescriptor impleme
return readOnlyExpr;
}
@NotNull
@Override
public String getLocalizedName(@NotNull String locale) {
return RuntimeUtils.getBundleLocalization(bundle, locale).getString(getPropertyId());
}
@Nullable
@Override
public String getLocalizedDescription(@NotNull String locale) {
return RuntimeUtils.getBundleLocalization(bundle, locale).getString(getPropertyId() + "." + ATTR_DESCRIPTION);
}
private String getPropertyId() {
public String getPropertyId() {
if (authProviderId != null) {
return WEB_AUTH_PROPERTY_PREFIX + authProviderId + "." + this.getId();
return "prop.auth.model." + authProviderId + "." + this.getId();
} else {
return WEB_AUTH_PROPERTY_PREFIX + this.getId();
return "prop.auth.model." + this.getId();
}
}
@NotNull
private Bundle extractBundle(@NotNull IConfigurationElement config) {
final Bundle bundle;
String bundleName = config.getContributor().getName();
bundle = Platform.getBundle(bundleName);
if (bundle == null) {
throw new IllegalStateException("Bundle '" + bundleName + "' not found");
}
return bundle;
}
}
@@ -38,7 +38,8 @@ public class WebFeatureDescriptor extends AbstractContextDescriptor implements D
private final DBPImage icon;
private final boolean enabledByDefault;
public WebFeatureDescriptor(@NotNull IConfigurationElement config) {
public WebFeatureDescriptor(IConfigurationElement config)
{
super(config);
this.id = config.getAttribute("id");
this.label = config.getAttribute("label");
@@ -76,7 +77,4 @@ public class WebFeatureDescriptor extends AbstractContextDescriptor implements D
return enabledByDefault;
}
public boolean isApplicable() {
return true;
}
}
@@ -20,12 +20,10 @@ import io.cloudbeaver.DBWFeatureSet;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class WebFeatureRegistry {
@@ -35,7 +33,7 @@ public class WebFeatureRegistry {
private static WebFeatureRegistry instance = null;
public static synchronized WebFeatureRegistry getInstance() {
public synchronized static WebFeatureRegistry getInstance() {
if (instance == null) {
instance = new WebFeatureRegistry();
instance.loadExtensions(Platform.getExtensionRegistry());
@@ -43,28 +41,23 @@ public class WebFeatureRegistry {
return instance;
}
private final List<WebFeatureDescriptor> webFeatures = new ArrayList<>();
private final List<DBWFeatureSet> webFeatures = new ArrayList<>();
private WebFeatureRegistry() {
}
protected void loadExtensions(@NotNull IExtensionRegistry registry) {
private void loadExtensions(IExtensionRegistry registry) {
IConfigurationElement[] extConfigs = registry.getConfigurationElementsFor(WebFeatureDescriptor.EXTENSION_ID);
for (IConfigurationElement ext : extConfigs) {
if (TAG_FEATURE.equals(ext.getName())) {
this.webFeatures.add(createFeatureDescriptor(ext));
this.webFeatures.add(
new WebFeatureDescriptor(ext));
}
}
}
@NotNull
protected WebFeatureDescriptor createFeatureDescriptor(@NotNull IConfigurationElement ext) {
return new WebFeatureDescriptor(ext);
}
@NotNull
public List<DBWFeatureSet> getWebFeatures() {
return webFeatures.stream()
.filter(WebFeatureDescriptor::isApplicable)
.collect(Collectors.toList());
return webFeatures;
}
}
@@ -41,7 +41,7 @@ public abstract class BaseServletPlatform extends BasePlatformImpl {
if (tempFolder == null) {
synchronized (this) {
if (tempFolder == null) {
initTempFolder();
initTempFolder(monitor);
}
}
}
@@ -59,8 +59,9 @@ public abstract class BaseServletPlatform extends BasePlatformImpl {
@NotNull
public abstract ServletApplication getApplication();
private void initTempFolder() {
private void initTempFolder(@NotNull DBRProgressMonitor monitor) {
// Make temp folder
monitor.subTask("Create temp folder");
String sysTempFolder = System.getProperty(StandardConstants.ENV_TMP_DIR);
if (CommonUtils.isNotEmpty(sysTempFolder)) {
tempFolder = Path.of(sysTempFolder).resolve(BASE_TEMP_DIR).resolve(DBWConstants.WORK_DATA_FOLDER_NAME);
@@ -68,11 +69,9 @@ public abstract class BaseServletPlatform extends BasePlatformImpl {
//we do not use workspace because it can be in external file system
tempFolder = getApplication().getHomeDirectory().resolve(DBWConstants.WORK_DATA_FOLDER_NAME);
}
log.debug("Temp folder: " + tempFolder);
}
public synchronized void dispose() {
super.dispose();
// Remove temp folder
if (tempFolder != null) {
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* 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.
@@ -28,34 +28,50 @@ public class CBConstants {
public static final String RUNTIME_PRODUCT_CONFIG_FILE_NAME = ".product.runtime.conf";
public static final String AUTO_CONFIG_FILE_NAME = ".cloudbeaver.auto.conf";
public static final String PARAM_SERVER_CONFIGURATION = "server";
public static final String PARAM_SERVER_PORT = "serverPort";
public static final String PARAM_SERVER_HOST = "serverHost";
public static final String PARAM_SERVER_NAME = "serverName";
public static final String PARAM_FORCE_HTTPS = "forceHttps";
public static final String PARAM_BIND_SESSION_TO_IP = "bindSessionToIp";
public static final String PARAM_SUPPORTED_HOSTS = "supportedHosts";
public static final String PARAM_SSL_CONFIGURATION_PATH = "sslConfigurationPath";
public static final String PARAM_CONTENT_ROOT = "contentRoot";
public static final String PARAM_SERVER_URL = "serverURL";
public static final String PARAM_ROOT_URI = "rootURI";
public static final String PARAM_SERVICES_URI = "serviceURI";
public static final String PARAM_DRIVERS_LOCATION = "driversLocation";
public static final String PARAM_WORKSPACE_LOCATION = "workspaceLocation";
//legacy path to a product.conf file
@Deprecated
public static final String PARAM_PRODUCT_CONFIGURATION = "productConfiguration";
public static final String PARAM_PRODUCT_SETTINGS = "productSettings";
public static final String PARAM_EXTERNAL_PROPERTIES = "externalProperties";
public static final String PARAM_STATIC_CONTENT = "staticContent";
public static final String PARAM_RESOURCE_QUOTAS = "resourceQuotas";
public static final String PARAM_RESOURCE_MANAGER_ENABLED = "resourceManagerEnabled";
public static final String PARAM_SECRET_MANAGER_ENABLED = "secretManagerEnabled";
public static final String PARAM_SHOW_READ_ONLY_CONN_INFO = "showReadOnlyConnectionInfo";
public static final String PARAM_CONN_GRANT_ANON_ACCESS = "grantConnectionsAccessToAnonymousTeam";
public static final String PARAM_AUTH_PROVIDERS = "authConfiguration";
public static final String PARAM_DB_CONFIGURATION = "database";
public static final String PARAM_SESSION_EXPIRE_PERIOD = "expireSessionAfterPeriod";
public static final String PARAM_DEVEL_MODE = "develMode";
public static final String PARAM_SECURITY_MANAGER = "enableSecurityManager";
public static final String PARAM_SM_CONFIGURATION = "sm";
public static final String PARAM_PASSWORD_POLICY_CONFIGURATION = "passwordPolicy";
public static final int DEFAULT_SERVER_PORT = 8080;
//public static final String DEFAULT_SERVER_NAME = "CloudBeaver Web Server";
public static final String DEFAULT_CONTENT_ROOT = "/var/www/cloudbeaver";
public static final String DEFAULT_ROOT_URI = "/";
public static final String DEFAULT_SERVICES_URI = "/api/";
public static final String DEFAULT_DEPLOY_LOCATION = "/opt/cloudbeaver";
public static final String DEFAULT_DRIVERS_LOCATION = DEFAULT_DEPLOY_LOCATION + "/drivers";
public static final String DEFAULT_WORKSPACE_LOCATION = DEFAULT_DEPLOY_LOCATION + "/workspace";
public static final String DEFAULT_PRODUCT_CONFIGURATION = "conf/product.conf";
public static final String DEFAULT_DATASOURCE_PERMISSIONS_CONFIGURATION = "data-sources-permissions.json";
public static final String DEFAULT_ADMIN_NAME = "cbadmin";
@@ -68,11 +84,14 @@ public class CBConstants {
public static final String VAR_HOST_DOCKER_INTERNAL = "host.docker.internal";
public static final String VAR_AUTO_CB_SERVER_NAME = "CB_SERVER_NAME";
public static final String VAR_AUTO_CB_SERVER_URL = "CB_SERVER_URL";
public static final String VAR_AUTO_CB_ADMIN_NAME = "CB_ADMIN_NAME";
public static final String VAR_AUTO_CB_ADMIN_PASSWORD = "CB_ADMIN_PASSWORD";
public static final String CB_SESSION_COOKIE_NAME = "cb-session-id";
public static final String APPLICATION_JSON = "application/json";
public static final String ADMIN_AUTO_GRANT = "auto-grant";
public static final String HOST_LOCALHOST = "localhost";
public static final String HOST_127_0_0_1 = "127.0.0.1";
@@ -80,8 +99,4 @@ public class CBConstants {
public static final String BIND_SESSION_ENABLE = "enable";
public static final String BIND_SESSION_DISABLE = "disable";
public static final String DEFAULT_CLOUD_PROJECT_NAME = "GlobalConfiguration";
public static final String SECURED_VALUE = "******";
public static final String PREF_DATA_EDITOR_EXPORT_DISABLED_OLD = "plugin.data-viewer.export.disabled";
public static final String PREF_DATA_EDITOR_IMPORT_DISABLED_OLD = "plugin.data-import.disabled";
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,14 +20,11 @@ import io.cloudbeaver.WebProjectImpl;
import io.cloudbeaver.model.app.ServletApplication;
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.app.DBPPlatform;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.auth.SMSession;
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.utils.CommonUtils;
import java.io.IOException;
@@ -54,18 +51,12 @@ public class ServerGlobalWorkspace extends BaseWorkspaceImpl {
@NotNull DBPPlatform platform,
@NotNull ServletApplication application
) {
super(platform, application.getWorkspacePath());
super(platform, application.getWorkspaceDirectory());
this.application = application;
}
@NotNull
@Override
protected SMSession acquireWorkspaceSession(@NotNull DBRProgressMonitor monitor) throws DBException {
return new ServerGlobalWorkspaceSession(this);
}
@Override
public void initializeProjects() throws DBException {
public void initializeProjects() {
initializeWorkspaceSession();
// Load global project
@@ -115,9 +106,4 @@ public class ServerGlobalWorkspace extends BaseWorkspaceImpl {
}
return null;
}
@Override
public boolean supportsRealmFeature(@NotNull String feature) {
return application.getAppConfiguration().isFeatureEnabled(feature);
}
}
@@ -1,36 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.server;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.impl.app.LocalWorkspaceSession;
import org.jkiss.dbeaver.model.secret.DBSSecretController;
public class ServerGlobalWorkspaceSession extends LocalWorkspaceSession {
public ServerGlobalWorkspaceSession(@NotNull DBPWorkspace workspace) {
super(workspace);
}
@Nullable
@Override
public DBSSecretController getSecretController() {
return null;
}
}
@@ -45,7 +45,4 @@ public class WebSQLConstants {
public static final String ATTR_SRID = "srid";
public static final String ATTR_PROPERTIES = "properties";
public static final String TASK_CONFIRMATION_ATTR_PREFIX = "session_task_confirmation_";
public static final String SKIP_TASK_CONFIRMATIONS_ATTR = "skip_session_task_confirmations";
public static final int TASK_CONFIRMATION_TIMEOUT_SECONDS = 60;
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
*/
package io.cloudbeaver.service.sql;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.data.DBDAttributeConstraint;
import org.jkiss.dbeaver.model.gis.DBGeometry;
import org.jkiss.utils.CommonUtils;
@@ -28,7 +27,6 @@ import java.util.Map;
*/
public class WebSQLDataFilterConstraint {
private String attributeName;
private Integer attributePosition;
private Integer orderPosition;
private Boolean orderAsc;
@@ -40,9 +38,8 @@ public class WebSQLDataFilterConstraint {
private WebSQLDataFilterConstraint() {
}
public WebSQLDataFilterConstraint(@NotNull Map<String, Object> map) {
this.attributeName = CommonUtils.toString(map.get("attributeName"));
this.attributePosition = CommonUtils.toInteger(map.get("attributePosition"), null);
public WebSQLDataFilterConstraint(Map<String, Object> map) {
this.attributePosition = CommonUtils.toInt(map.get("attributePosition"));
this.orderPosition = map.containsKey("orderPosition") ?
CommonUtils.toInt(map.get("orderPosition")) + 1 : // Use position + 1 because 0 means no ordering (because of legacy compatibility)
null;
@@ -67,10 +64,8 @@ public class WebSQLDataFilterConstraint {
}
@NotNull
public static WebSQLDataFilterConstraint from(@NotNull DBDAttributeConstraint constraint) {
public static WebSQLDataFilterConstraint from(DBDAttributeConstraint constraint) {
var webConstraint = new WebSQLDataFilterConstraint();
webConstraint.attributeName = constraint.getAttributeName();
webConstraint.orderPosition = constraint.getOrderPosition();
webConstraint.orderAsc = !constraint.isOrderDescending();
webConstraint.criteria = constraint.getCriteria();
@@ -81,10 +76,6 @@ public class WebSQLDataFilterConstraint {
return webConstraint;
}
public String getAttributeName() {
return attributeName;
}
public Integer getAttributePosition() {
return attributePosition;
}
@@ -1,23 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.service.sql;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
public record WebSQLQueryParameterRecord(@NotNull String name, @Nullable String value) {
}
@@ -21,7 +21,6 @@ import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.DBPDataSource;
import org.jkiss.dbeaver.model.DBUtils;
import org.jkiss.dbeaver.model.data.DBDAttributeBinding;
import org.jkiss.dbeaver.model.data.DBDDataFilter;
import org.jkiss.dbeaver.model.data.DBDRowIdentifier;
import org.jkiss.dbeaver.model.exec.trace.DBCTrace;
import org.jkiss.dbeaver.model.struct.*;
@@ -43,8 +42,6 @@ public class WebSQLResultsInfo {
// TODO: find a way to remove isSingleRow and use virtual keys for reading BLOB and string cell values.
private boolean isSingleRow;
private DBCTrace trace;
@Nullable
private DBDDataFilter dataFilter;
private String queryText;
public WebSQLResultsInfo(@NotNull DBSDataContainer dataContainer, @NotNull String id) {
@@ -154,13 +151,4 @@ public class WebSQLResultsInfo {
public void setSingleRow(boolean singleRow) {
isSingleRow = singleRow;
}
@Nullable
public DBDDataFilter getDataFilter() {
return dataFilter;
}
public void setDataFilter(@Nullable DBDDataFilter dataFilter) {
this.dataFilter = dataFilter;
}
}
@@ -18,20 +18,18 @@ package io.cloudbeaver.test;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.cloudbeaver.server.CBConstants;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.utils.HttpConstants;
import java.net.CookieManager;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
@@ -58,13 +56,10 @@ public class WebGQLClient {
@NotNull
private final HttpClient httpClient;
@NotNull
private final CookieManager cookieManager;
@NotNull
private final String apiUrl;
public WebGQLClient(@NotNull HttpClient httpClient, @NotNull CookieManager cookieManager, @NotNull String apiUrl) {
public WebGQLClient(@NotNull HttpClient httpClient, @NotNull String apiUrl) {
this.httpClient = httpClient;
this.cookieManager = cookieManager;
this.apiUrl = apiUrl;
}
@@ -75,18 +70,9 @@ public class WebGQLClient {
* @param variables GraphQL query variables
* @return GraphQL response
*/
@Nullable
public <T> T sendQuery(@NotNull String query, @Nullable Map<String, Object> variables) throws Exception {
return sendQueryWithHeaders(query, variables, Map.of());
}
@NotNull
public Map<String, Object> sendQueryAndGetHeaders(
@NotNull String query,
@Nullable Map<String, Object> variables,
@NotNull Map<String, String> requestHeaders
) throws Exception {
return executeGQLRequest(query, variables, requestHeaders);
public <T> T sendQuery(@NotNull String query, @Nullable Map<String, Object> variables) throws Exception {
return sendQueryWithHeaders(query, variables, List.of());
}
/**
@@ -101,36 +87,15 @@ public class WebGQLClient {
public <T> T sendQueryWithHeaders(
@NotNull String query,
@Nullable Map<String, Object> variables,
@NotNull Map<String, String> headers
) throws Exception {
Map<String, Object> parsed = executeGQLRequest(query, variables, headers);
// graphql response will be in "data" key
return (T) parsed.get("data");
}
@NotNull
private Map<String, Object> executeGQLRequest(
@NotNull String query,
@Nullable Map<String, Object> variables,
@NotNull Map<String, String> requestHeaders
) throws Exception {
return executeGQLRequest(query, variables, requestHeaders, "result");
}
@NotNull
public Map<String, Object> executeGQLRequest(
@NotNull String query,
@Nullable Map<String, Object> variables,
@NotNull Map<String, String> requestHeaders,
@NotNull String resultName
@NotNull List<String> headers
) throws Exception {
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
.uri(URI.create(apiUrl))
.POST(HttpRequest.BodyPublishers.ofString(makeGQLRequest(query, variables)))
.setHeader("TE-Client-Version", GeneralUtils.getMajorVersion())
.header(HttpConstants.HEADER_CONTENT_TYPE, HttpConstants.CONTENT_TYPE_JSON);
if (!requestHeaders.isEmpty()) {
requestHeaders.forEach(requestBuilder::header);
.header("Content-Type", "application/json");
if (!headers.isEmpty()) {
requestBuilder.headers(headers.toArray(String[]::new));
}
HttpRequest request = requestBuilder.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
@@ -142,10 +107,8 @@ public class WebGQLClient {
String message = JSONUtils.getString(JSONUtils.getObjectList(body, "errors").get(0), "message");
throw new DBException(message);
}
Map<String, Object> parsed = new LinkedHashMap<>();
parsed.put("data", JSONUtils.getObject(body, "data").get(resultName));
parsed.put("headers", response.headers().map());
return parsed;
// graphql response will be in "data" key
return (T) JSONUtils.getObject(body, "data").get("result");
}
@NotNull
@@ -158,13 +121,4 @@ public class WebGQLClient {
return gson.toJson(request, Map.class);
}
@NotNull
public String getSessionIdCookie() {
return cookieManager.getCookieStore().getCookies().stream()
.filter(cookie -> cookie.getName().equals(CBConstants.CB_SESSION_COOKIE_NAME))
.findFirst()
.get()
.getValue();
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* 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.
@@ -47,8 +47,6 @@ public class ServletAppUtils {
private static final String HEADER_REFERER = "Referer";
private static final String HEADER_FORWARDED_SCHEME = "X-Forwarded-Scheme";
private static final String HEADER_FORWARDED_PROTO = "X-Forwarded-Proto";
private static final String HEADER_FORWARDED_PORT = "X-Forwarded-Port";
private static final String HEADER_FORWARDED_HOST = "X-Forwarded-Host";
private static final Set<Integer> DEFAULT_PORTS = Set.of(
80,
@@ -193,12 +191,8 @@ public class ServletAppUtils {
@NotNull String origin
) throws DBException {
String finalOrigin = webAuthApplication.modifyOrigin(origin);
String serviceUriSegment = removeSideSlashes(webAuthApplication.getAuthServiceUriSegment());
String rootUri = removeSideSlashes(getServletApplication().getRootURI());
if (finalOrigin.endsWith("/" + rootUri) && serviceUriSegment.startsWith(rootUri + "/")) {
finalOrigin = finalOrigin.substring(0, finalOrigin.length() - rootUri.length());
}
StringBuilder authUriBuilder = new StringBuilder(removeSideSlashes(finalOrigin));
String serviceUriSegment = removeSideSlashes(webAuthApplication.getAuthServiceUriSegment());
if (CommonUtils.isNotEmpty(serviceUriSegment)) {
authUriBuilder.append("/").append(serviceUriSegment);
}
@@ -300,30 +294,19 @@ public class ServletAppUtils {
@NotNull
public static String getOriginFromRequest(@NotNull HttpServletRequest request) {
String origin = request.getHeader(HEADER_ORIGIN);
if (log.isTraceEnabled()) {
log.trace("Origin header: " + origin);
}
if (CommonUtils.isEmpty(origin)) {
origin = request.getHeader(HEADER_X_ORIGIN);
if (log.isTraceEnabled()) {
log.trace("X-Origin header: " + origin);
}
}
if (CommonUtils.isEmpty(origin)) {
origin = request.getHeader(HEADER_REFERER);
}
String forwardedScheme = request.getHeader(HEADER_FORWARDED_SCHEME);
if (CommonUtils.isEmpty(forwardedScheme)) {
forwardedScheme = request.getHeader(HEADER_FORWARDED_PROTO);
}
String forwardedHost = request.getHeader(HEADER_FORWARDED_HOST);
if (CommonUtils.isNotEmpty(forwardedScheme) && CommonUtils.isNotEmpty(forwardedHost)) {
origin = forwardedScheme + "://" + forwardedHost;
if (log.isTraceEnabled()) {
log.trace("forwarded origin: " + origin);
}
} else if (CommonUtils.isEmpty(origin)) {
}
if (CommonUtils.isEmpty(origin)) {
URI requestUrl = URI.create(request.getRequestURL().toString());
if (log.isTraceEnabled()) {
log.trace("Request URL: " + requestUrl);
}
origin = getRootUrlFromUri(requestUrl) + "/";
}
origin = removeSideSlashes(origin);
@@ -335,42 +318,13 @@ public class ServletAppUtils {
origin = removeSideSlashes(origin);
URI uri = URI.create(origin);
int port = uri.getPort();
if (CommonUtils.isNotEmpty(request.getHeader(HEADER_FORWARDED_PORT))) {
try {
port = Integer.parseInt(request.getHeader(HEADER_FORWARDED_PORT));
} catch (NumberFormatException e) {
log.error("Failed to parse port from header: " + request.getHeader(HEADER_FORWARDED_PORT), e);
}
}
String finalScheme = uri.getScheme();
String finalHost = uri.getHost();
int finalPort = port;
boolean changed = false;
if (CommonUtils.isNotEmpty(forwardedScheme) && !forwardedScheme.equals(finalScheme)) {
finalScheme = forwardedScheme;
changed = true;
}
if (CommonUtils.isNotEmpty(forwardedHost) && !forwardedHost.equals(finalHost)) {
finalHost = forwardedHost;
changed = true;
}
if (DEFAULT_PORTS.contains(port)) {
finalPort = -1;
changed = true;
}
if (log.isTraceEnabled()) {
log.trace("Origin URI: " + origin);
}
if (changed) {
if (DEFAULT_PORTS.contains(uri.getPort())) {
try {
origin = new URI(
finalScheme,
uri.getScheme(),
uri.getUserInfo(),
finalHost,
finalPort,
uri.getHost(),
-1,
uri.getPath(),
uri.getQuery(),
uri.getFragment()
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,10 @@
*/
package io.cloudbeaver.utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.Strictness;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebSessionProjectImpl;
@@ -30,6 +34,7 @@ import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBConstants;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.access.DBAAuthCredentials;
import org.jkiss.dbeaver.model.access.DBAAuthCredentialsWithComplexProperties;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
@@ -38,12 +43,10 @@ import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNativeCredentials;
import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration;
import org.jkiss.dbeaver.model.net.ssh.SSHConstants;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceDisconnectEvent;
import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor;
import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.dbeaver.utils.PropertySerializationUtils;
import org.jkiss.utils.CommonUtils;
import java.util.*;
@@ -162,24 +165,18 @@ public class WebDataSourceUtils {
}
public static boolean disconnectDataSource(
@NotNull WebSession webSession,
@NotNull DBPDataSourceContainer dataSource,
boolean sendDisconnectEvent
) {
public static boolean disconnectDataSource(@NotNull WebSession webSession, @NotNull DBPDataSourceContainer dataSource) {
if (dataSource.isConnected()) {
try {
dataSource.disconnect(webSession.getProgressMonitor());
if (sendDisconnectEvent) {
webSession.addSessionEvent(
new WSDataSourceDisconnectEvent(
dataSource.getProject().getId(),
dataSource.getId(),
WebEventUtils.getSmSessionId(webSession),
webSession.getUserId()
)
);
}
webSession.addSessionEvent(
new WSDataSourceDisconnectEvent(
dataSource.getProject().getId(),
dataSource.getId(),
webSession.getSessionId(),
webSession.getUserId()
)
);
return true;
} catch (DBException e) {
log.error("Error closing connection", e);
@@ -214,30 +211,31 @@ public class WebDataSourceUtils {
return webSession.getAccessibleProjectById(projectId).getWebConnectionInfo(connectionId);
}
public static void updateCredentialsFromProperties(
@NotNull DBRProgressMonitor progressMonitor,
@NotNull DBAAuthCredentials credentials,
@Nullable Map<String, ?> properties
) {
if (properties == null) {
return;
public static void updateCredentialsFromProperties(@NotNull DBAAuthCredentials credentials, @NotNull Map<String, ?> properties) {
InstanceCreator<DBAAuthCredentials> credTypeAdapter = type -> credentials;
Gson credGson = new GsonBuilder()
.setStrictness(Strictness.LENIENT)
.registerTypeAdapter(credentials.getClass(), credTypeAdapter)
.create();
if (credentials instanceof DBAAuthCredentialsWithComplexProperties complexProperties) {
complexProperties.updateCredentialsFromComplexProperties(properties);
}
PropertySerializationUtils.updateCredentialsFromProperties(progressMonitor, credentials, properties);
credGson.fromJson(credGson.toJsonTree(properties), credentials.getClass());
}
public static void saveAuthProperties(
@NotNull DBRProgressMonitor progressMonitor,
@NotNull DBPDataSourceContainer dataSourceContainer,
@NotNull DBPConnectionConfiguration configuration,
@Nullable Map<String, Object> authProperties,
boolean saveCredentials,
boolean sharedCredentials
) {
saveAuthProperties(progressMonitor, dataSourceContainer, configuration, authProperties, saveCredentials, sharedCredentials, false);
saveAuthProperties(dataSourceContainer, configuration, authProperties, saveCredentials, sharedCredentials, false);
}
public static void saveAuthProperties(
@NotNull DBRProgressMonitor progressMonitor,
@NotNull DBPDataSourceContainer dataSourceContainer,
@NotNull DBPConnectionConfiguration configuration,
@Nullable Map<String, Object> authProperties,
@@ -273,7 +271,7 @@ public class WebDataSourceUtils {
configuration.setAuthProperties(currentAuthProps);
}
if (!authProperties.isEmpty()) {
updateCredentialsFromProperties(progressMonitor, credentials, authProperties);
updateCredentialsFromProperties(credentials, authProperties);
}
configuration.getAuthModel().saveCredentials(dataSourceContainer, configuration, credentials);
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,21 +17,119 @@
package io.cloudbeaver.utils;
import io.cloudbeaver.model.WebAsyncTaskInfo;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.websocket.event.task.WSSessionTaskInfoEvent;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.security.SMSubjectType;
import org.jkiss.dbeaver.model.websocket.WSConstants;
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDatasourceFolderEvent;
import org.jkiss.dbeaver.model.websocket.event.permissions.WSSubjectPermissionEvent;
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceProperty;
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceUpdatedEvent;
import org.jkiss.dbeaver.model.websocket.event.session.WSSessionTaskInfoEvent;
import java.util.Objects;
import java.util.List;
public class WebEventUtils {
/**
* adds events of updated datasource to event controller
*
* @param project project of updated database
* @param session web session
* @param datasourceId id of datasource
* @param eventAction type of event
*/
public static void addDataSourceUpdatedEvent(
DBPProject project,
WebSession session,
String datasourceId,
WSConstants.EventAction eventAction,
WSDataSourceProperty property
) {
if (project == null) {
return;
}
WSEvent event = null;
switch (eventAction) {
case CREATE:
event = WSDataSourceEvent.create(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(datasourceId),
property
);
break;
case DELETE:
event = WSDataSourceEvent.delete(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(datasourceId),
property
);
break;
case UPDATE:
event = WSDataSourceEvent.update(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(datasourceId),
property
);
break;
}
if (event == null) {
return;
}
ServletAppUtils.getServletApplication().getEventController().addEvent(event);
}
public static void addNavigatorNodeUpdatedEvent(
DBPProject project,
WebSession session,
String nodePath,
WSConstants.EventAction eventAction
) {
if (project == null) {
return;
}
WSEvent event = null;
switch (eventAction) {
case CREATE:
event = WSDatasourceFolderEvent.create(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(nodePath)
);
break;
case DELETE:
event = WSDatasourceFolderEvent.delete(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(nodePath)
);
break;
case UPDATE:
event = WSDatasourceFolderEvent.update(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(nodePath)
);
break;
}
if (event == null) {
return;
}
ServletAppUtils.getServletApplication().getEventController().addEvent(event);
}
public static void addRmResourceUpdatedEvent(
String projectId,
@@ -55,7 +153,7 @@ public class WebEventUtils {
switch (eventAction) {
case CREATE:
event = WSResourceUpdatedEvent.create(
WebEventUtils.getSmSessionId(session),
session.getUserContext().getSmSessionId(),
session.getUserId(),
projectId,
resourcePath,
@@ -65,7 +163,7 @@ public class WebEventUtils {
break;
case DELETE:
event = WSResourceUpdatedEvent.delete(
WebEventUtils.getSmSessionId(session),
session.getUserContext().getSmSessionId(),
session.getUserId(),
projectId,
resourcePath,
@@ -75,7 +173,7 @@ public class WebEventUtils {
break;
case UPDATE:
event = WSResourceUpdatedEvent.update(
WebEventUtils.getSmSessionId(session),
session.getUserContext().getSmSessionId(),
session.getUserId(),
projectId,
resourcePath,
@@ -115,12 +213,4 @@ public class WebEventUtils {
ServletAppUtils.getServletApplication().getEventController().addEvent(event);
}
@Nullable
public static String getSmSessionId(@NotNull BaseWebSession webSession) {
return webSession.getUserContext().getSmSessionId();
}
public static boolean isSmSessionIdEquals(@NotNull BaseWebSession webSession, @Nullable String smSessionId) {
return Objects.equals(getSmSessionId(webSession), smSessionId);
}
}
@@ -17,11 +17,8 @@
package io.cloudbeaver.utils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.utils.GeneralUtils;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
@@ -42,30 +39,4 @@ public class WebTestUtils {
return false;
}
}
public static void setPrivateField(
@NotNull Object target,
@NotNull String fieldName,
@Nullable Object value
) throws Exception {
Class<?> cls = target.getClass();
Field f = null;
while (cls != null) {
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
if (field.getName().equals(fieldName)) {
f = field;
break;
}
}
cls = cls.getSuperclass();
}
if (f == null) {
throw new NoSuchFieldException(fieldName);
}
boolean isAccessible = f.canAccess(target);
f.setAccessible(true);
f.set(target, value);
f.setAccessible(isAccessible);
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +16,6 @@
*/
package io.cloudbeaver.websocket;
import io.cloudbeaver.model.session.BaseWebSession;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
@@ -26,6 +24,4 @@ public interface CBWebSessionEventHandler {
void close();
void migrateToSession(@NotNull BaseWebSession newSession);
}

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