Compare commits

..
1368 changed files with 12226 additions and 25976 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,6 +14,7 @@
"redhat.vscode-yaml",
"streetsidesoftware.code-spell-checker",
"streetsidesoftware.code-spell-checker-russian",
"syler.sass-indented",
"github.copilot-chat",
"yzhang.markdown-all-in-one",
"GraphQL.vscode-graphql-syntax",
@@ -21,4 +22,4 @@
"esbenp.prettier-vscode",
"devnaumov.licensify"
]
}
}
+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"
}
+55 -91
View File
@@ -5,16 +5,13 @@
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,67 @@ 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
### 26.1.1 2026-06-22
### 25.3.3 2026-01-19
### Changes since 26.1.0:
- For security reasons, session expiration leads to a complete logout for a user and clears all data from the screen;
- Added "Use local formatting" setting to Data Editor. Users can choose how to display numbers and dates: using the OS locale, a custom locale, or keeping values unformatted. This formatting applies only to displayed values. Data in the database remains unchanged;
- Added the shortcut Ctrl/Cmd + . to cancel operations in Data Editor;
- CloudBeaver now supports long polling as a fallback when WebSockets are unavailable or blocked, ensuring reliable communication for metadata updates and SQL execution;
- Fixed a keyboard navigation issue for panels to keep the focus inside;
- Clickhouse: Spatial data support was added.
- 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).
### 25.3.2 2026-01-05
### 26.1.0 2026-06-01
- The ability to pin columns has been added to the Data Editor, allowing users to keep key columns (e.g., IDs, names) visible while scrolling through wide tables;
- Added a new setting in the SQL Editor to highlight spaces, tabs, and other whitespace characters to help users read, debug, and maintain their scripts. It is turned off by default and can be configured in personal preferences;
- Redesigned the connection configuration page. Form fields and sections have been reorganized to provide more space for input and reduce visual clutter;
- Clickhouse: driver has been updated to version 0.9.5;
- Oracle: Added a new "Set Username to OS_USER" option in the Misc section of Oracle connection settings. When turned on, the tool automatically uses the current database username as the operating system user identifier in the session metadata.
### Changes since 26.0.0:
### 25.3.1 2025-12-22
- 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.
- 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.
- 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.
- 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.
- 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.
- Implemented an ability to add parameters and variables to queries in the SQL Editor. This feature allows for the reuse of queries by changing parameters at the execution stage. It is enabled by default and can be configured in personal preferences;
- Added support for mapping users to CloudBeaver teams based on LDAP memberOf group membership;
- Added support for previous browser versions, but not older than three years.
### 25.3.0 2025-12-01
### Changes since 25.2.0:
- Administration:
- Permission configuration for connections has been simplified, providing a clearer appearance and improved performance;
- LDAP authentication now supports case-insensitive Distinguished Names (DNs), allowing login with mixed-case DNS.
- Security:
- Fixed a security issue in the Spatial Viewer for the HTML content to prevent a third-party script execution;
- Fixed the critical vulnerability (CVE-2025-61927) in the Happy DOM library. The library was updated to version 20.0.2.
- SQL Editor:
- A confirmation prompt has been added to the SQL Editor to prevent accidental execution of potentially dangerous queries. You can customize this behavior in Preferences > SQL Editor;
- A search field has been added to the catalog/schema selector on the main toolbar for easy navigation;
- Alphabetical order has been added to the autocomplete values in the SQL Editor to simplify object search;
- Added support for temporary tables in semantic analysis.
- Data Editor:
- The ability to review the database query after applying filters and sorting in the Data Editor, Grouping panel, and Result sets has been added. The new Show SQL button is placed on the Filter panel;
- The value panel in the Data Editor has been redesigned to give users more space for viewing and editing data. Tab buttons for different data type visualizations are now aligned on a single level, creating a cleaner layout;
- Added visualization for column reordering in the Data Editor;
- Fixed an issue in the Data Editor where resizing one column in a wide table caused previously resized columns to revert to their original width. Users can resize multiple columns as intended.
- General:
- A reorder tabs option has been added to the application, allowing users to set editor tabs in custom positions;
- Keyboard navigation has been improved across the application. Additional shortcuts are available in the main info panel (top-right corner) to help discover them faster. Relevant keyboard shortcuts are also shown in the tooltips of Data Editor buttons;
- Boolean values have been replaced with intuitive checkboxes instead of the true/false text in the metadata editor, making editing easier and faster;
- The database navigator now automatically hides empty subfolders in shared projects, reducing visual clutter;
- Databases and drivers:
- ClickHouse: query canceling support has been added;
- DuckDB: fixed an issue where catalog selection was lost upon disconnection;
- PostgreSQL: fixed support for GIS data types;
- SQL Server: the driver has been updated to version 13.2.1;
- The "Show all databases" setting has been added on the connection page for MySQL and MariaDB.
## 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.
+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
@@ -3,8 +3,8 @@ 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-Version: 1.0.3.qualifier
Bundle-Release-Date: 20260202
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.model.cli;visibility:=reexport
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.model.cli</artifactId>
<version>1.0.13-SNAPSHOT</version>
<version>1.0.3-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -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.
@@ -18,36 +18,39 @@ 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 org.jkiss.dbeaver.model.cli.*;
import org.jkiss.dbeaver.model.cli.model.CommandLineAuthenticator;
import picocli.CommandLine;
import java.util.List;
public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationInstanceController> {
@Nullable
private final CommandLineAuthenticator authenticator;
private final CloudBeaverMixin mixin;
public CloudBeaverCommandLine() {
public CloudBeaverCommandLine(@Nullable CommandLineAuthenticator authenticator) {
super();
this.authenticator = authenticator;
this.mixin = new CloudBeaverMixin();
}
public CloudBeaverCommandLine(
@Nullable CommandLineAuthenticator authenticator,
@NotNull CloudBeaverMixin mixin
) {
super();
this.authenticator = authenticator;
this.mixin = mixin;
}
@Override
protected CloudBeaverTopLevelCommand createTopLevelCommand(
@Nullable ApplicationInstanceController applicationInstanceController,
@NotNull CLIContextImpl context,
@NotNull CommandLineContext context,
@NotNull CLIRunMeta runMeta
) {
if (authenticator != null) {
context.setContextParameter(CLIConstants.CONTEXT_PARAM_AUTHENTICATOR, authenticator);
}
return new CloudBeaverTopLevelCommand(applicationInstanceController, context, runMeta);
}
@@ -55,11 +58,10 @@ public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationIn
@Override
protected CommandLine initCommandLine(
@Nullable ApplicationInstanceController applicationInstanceController,
@NotNull CLIContextImpl context,
@NotNull CLIRunMeta runMeta,
@NotNull List<CLICommandDescriptor> commandsToExecute
@NotNull CommandLineContext context,
@NotNull CLIRunMeta runMeta
) {
var cmd = super.initCommandLine(applicationInstanceController, context, runMeta, commandsToExecute);
var cmd = super.initCommandLine(applicationInstanceController, context, runMeta);
cmd.addMixin("cloudbeaver", mixin);
return cmd;
}
@@ -29,7 +29,7 @@ public class CloudBeaverInstanceServer extends ApplicationInstanceServer<Applica
public CloudBeaverInstanceServer() throws IOException {
super(ApplicationInstanceController.class);
this.commandLine = new CloudBeaverCommandLine();
this.commandLine = new CloudBeaverCommandLine(null);
}
public CloudBeaverInstanceServer(@NotNull CloudBeaverCommandLine commandLine) throws IOException {
@@ -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,14 +16,12 @@
*/
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",
@@ -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.
@@ -19,15 +19,13 @@ 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.CommandLineContext;
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,
@@ -37,7 +35,7 @@ public class CloudBeaverTopLevelCommand extends AbstractTopLevelCommand {
protected CloudBeaverTopLevelCommand(
@Nullable ApplicationInstanceController controller,
@NotNull CLIContextImpl context,
@NotNull CommandLineContext context,
@NotNull CLIRunMeta meta
) {
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Model
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
Bundle-Version: 1.0.104.qualifier
Bundle-Release-Date: 20260706
Bundle-Version: 1.0.94.qualifier
Bundle-Release-Date: 20260202
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
@@ -17,6 +17,7 @@ Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
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,
@@ -28,11 +29,6 @@ Export-Package: io.cloudbeaver,
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,
@@ -25,11 +25,6 @@
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"/>
@@ -51,8 +46,6 @@
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.94-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.
@@ -27,7 +27,6 @@ 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> {
@@ -94,9 +93,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.
@@ -29,11 +29,11 @@ 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;
@@ -218,7 +218,6 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
*
* @return connections from cache.
*/
@NotNull
public List<WebConnectionInfo> getConnections() {
if (!registryIsLoaded) {
addDataSourcesToCache();
@@ -252,14 +251,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);
}
@@ -345,7 +344,7 @@ 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);
WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer);
DBPDataSourceRegistry registry = getDataSourceRegistry();
registry.removeDataSource(dataSourceContainer);
removeConnection(dataSourceContainer);
@@ -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;
@@ -65,8 +64,6 @@ public class WebConnectionConfig {
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));
@@ -372,13 +367,4 @@ public class WebConnectionConfig {
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
@@ -297,16 +293,6 @@ public class WebConnectionInfo {
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() {
@@ -592,5 +578,4 @@ public class WebConnectionInfo {
public void setCredentialsSavedInSession(@Nullable Boolean credentialsSavedInSession) {
this.credentialsSavedInSession = credentialsSavedInSession;
}
}
@@ -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);
@@ -311,13 +307,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 "";
}
@@ -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.
@@ -25,13 +25,10 @@ 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.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 +48,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";
@@ -62,7 +59,6 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
private String instanceId;
@NotNull
@Override
public RMController createResourceController(
@NotNull SMCredentialsProvider credentialsProvider,
@@ -77,16 +73,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
@@ -212,8 +202,6 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
@Override
public Object start(IApplicationContext context) {
initializeApplicationServices();
setWorkspacePath(getServerConfigurationController().getWorkspacePath());
try {
startServer();
} catch (Exception e) {
@@ -250,6 +238,13 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
return BaseWorkspaceImpl.readWorkspaceIdProperty();
}
@NotNull
@Override
public Path getWorkspaceDirectory() {
return getServerConfigurationController().getWorkspacePath();
}
public String getApplicationId() {
try {
return getApplicationInstanceId();
@@ -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,6 +53,9 @@ public interface ServletApplication extends DBPApplication {
@NotNull
Path getDataDirectory(boolean create);
@NotNull
Path getWorkspaceDirectory();
@NotNull
Path getHomeDirectory();
@@ -77,6 +80,9 @@ public interface ServletApplication extends DBPApplication {
@NotNull
DBFileController createFileController(@NotNull SMCredentialsProvider credentialsProvider);
@Nullable
String getServerURL();
@NotNull
default String getServicesURI() {
return "/";
@@ -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 {
@@ -26,9 +26,7 @@ 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.rm.RMController;
import org.jkiss.dbeaver.model.rm.RMEvent;
import org.jkiss.dbeaver.model.rm.RMEventManager;
@@ -55,11 +53,11 @@ public abstract class BaseLocalResourceController implements RMController {
@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;
@@ -159,7 +157,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 ignoredLock = lockController.lock(projectId, "updateProjectDataSources")) {
DBPProject project = getWebProject(projectId, false);
return doFileWriteOperation(
projectId, project.getMetadataFolder(false),
@@ -190,7 +188,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 ignoredLock = lockController.lock(projectId, "deleteDataSources")) {
DBPProject project = getWebProject(projectId, false);
doFileWriteOperation(projectId, project.getMetadataFolder(false), () -> {
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
@@ -215,7 +213,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 ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
DBPProject project = getWebProject(projectId, false);
log.debug("Creating data source folder '" + folderPath + "' in project '" + projectId + "'");
doFileWriteOperation(projectId, project.getMetadataFolder(false),
@@ -243,7 +241,7 @@ 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 ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
DBPProject project = getWebProject(projectId, false);
doFileWriteOperation(projectId, project.getMetadataFolder(false),
() -> {
@@ -269,7 +267,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 ignoredLock = 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),
@@ -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.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;
@@ -93,10 +90,9 @@ public class LocalResourceController extends BaseLocalResourceController {
@NotNull Path rootPath,
@NotNull Path userProjectsPath,
@NotNull Path sharedProjectsPath,
@NotNull Supplier<SMAdminController> smControllerSupplier,
@NotNull LockManager lockController
@NotNull Supplier<SMAdminController> smControllerSupplier
) throws DBException {
super(workspace, lockController);
super(workspace, new FileLockController(ServletAppUtils.getServletApplication().getApplicationInstanceId()));
this.credentialsProvider = credentialsProvider;
this.rootPath = rootPath;
this.userProjectsPath = userProjectsPath;
@@ -105,7 +101,7 @@ public class LocalResourceController extends BaseLocalResourceController {
this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName();
this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers();
this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath, lockController);
this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath);
}
@NotNull
@@ -284,7 +280,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)) {
@@ -307,7 +303,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@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 ignoredLock = lockController.lock(projectId, "updateProject")) {
RMLocalProject project = getWebProject(projectId, false);
Path targetPath = getProjectPath(projectId);
if (!Files.exists(targetPath)) {
@@ -334,7 +330,7 @@ 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 ignoredLock = lockController.lock(projectId, "deleteProject")) {
Path targetPath = getProjectPath(projectId);
if (!Files.exists(targetPath)) {
log.error(MessageFormat.format("Project folder ''{0}'' is not found", projectId));
@@ -703,7 +699,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@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 +731,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 +803,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 +891,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 +936,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 +957,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(),
@@ -1107,7 +1103,6 @@ public class LocalResourceController extends BaseLocalResourceController {
RMProjectName project = WebRMUtils.parseProjectName(projectId);
RMProjectType type = project.getType();
String projectName = project.getName();
GeneralUtils.validateResourceNameUnconditionally(projectName);
switch (type) {
case GLOBAL:
if (!projectName.equals(globalProjectName)) {
@@ -1209,14 +1204,12 @@ public class LocalResourceController extends BaseLocalResourceController {
return getProjectPath(projectId).toAbsolutePath().relativize(path).toString().replace('\\', IPath.SEPARATOR);
}
@NotNull
public static Builder builder(
@NotNull SMCredentialsProvider credentialsProvider,
@NotNull DBPWorkspace workspace,
@NotNull LockManager lockController,
@NotNull Supplier<SMAdminController> smControllerSupplier
SMCredentialsProvider credentialsProvider,
DBPWorkspace workspace,
Supplier<SMAdminController> smControllerSupplier
) {
return new Builder(workspace, credentialsProvider, lockController, smControllerSupplier);
return new Builder(workspace, credentialsProvider, smControllerSupplier);
}
@Override
@@ -1232,13 +1225,10 @@ public class LocalResourceController extends BaseLocalResourceController {
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<SMAdminController> smControllerSupplier
) {
this.workspace = workspace;
this.credentialsProvider = credentialsProvider;
@@ -1246,38 +1236,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);
}
}
@@ -17,15 +17,14 @@
package io.cloudbeaver.model.rm.local;
import com.google.gson.reflect.TypeToken;
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.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.fs.lock.FileLockController;
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
import org.jkiss.dbeaver.model.rm.RMProjectInfo;
import org.jkiss.dbeaver.model.rm.RMProjectType;
@@ -46,12 +45,12 @@ public class ProjectsMetadataInfo {
private final Map<String, RMProjectInfo> projectsInfo = new LinkedHashMap<>();
private final Path projectsPath;
private final LockManager lockController;
private final FileLockController lockController;
public ProjectsMetadataInfo(@NotNull Path projectsPath, @NotNull LockManager lockController) throws DBException {
public ProjectsMetadataInfo(@NotNull Path projectsPath) throws DBException {
this.projectsPath = projectsPath;
this.lockController = lockController;
this.lockController = new FileLockController(ServletAppUtils.getServletApplication().getApplicationInstanceId());
readProjectInfos(projectsPath);
}
@@ -140,7 +139,7 @@ public class ProjectsMetadataInfo {
}
private void saveProjectsInfo() {
try (var lock = lockController.lock(LockTarget.of(PROJECTS_INFO_FILE_NAME), LockOptions.of("saveProjectsInfo"))) {
try (var lock = lockController.lock(PROJECTS_INFO_FILE_NAME, "saveProjectsInfo")) {
log.info("Saving project information");
Files.writeString(projectsPath.resolve(PROJECTS_INFO_FILE_NAME), JSONUtils.GSON.toJson(projectsInfo));
} catch (IOException e) {
@@ -149,4 +148,4 @@ public class ProjectsMetadataInfo {
log.error("Error locking file " + PROJECTS_INFO_FILE_NAME + ": " + e.getMessage());
}
}
}
}
@@ -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() {
@@ -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
}
@@ -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,22 +44,11 @@ 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
@@ -86,9 +70,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.
@@ -57,6 +57,8 @@ import org.jkiss.dbeaver.model.websocket.event.MessageType;
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.function.ThrowableConsumer;
import org.jkiss.utils.function.ThrowableFunction;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Path;
@@ -64,8 +66,6 @@ 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.stream.Collectors;
/**
* Web session.
@@ -96,12 +96,12 @@ public class WebSession extends BaseWebSession
private final List<WebServerMessage> sessionMessages = new ArrayList<>();
private final Map<String, WebAsyncTaskInfo> asyncTasks = new HashMap<>();
private final Map<String, ThrowableConsumer<Object, Exception>> 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 WebDataSourceConnectEventListener connectListener = new WebDataSourceConnectEventListener(this);
@@ -116,8 +116,7 @@ public class WebSession extends BaseWebSession
CommonUtils.toString(requestInfo.getLocale()),
application,
sessionHandlers,
requestInfo.getLastRemoteAddress(),
requestInfo.getSessionType()
requestInfo.getLastRemoteAddress()
);
updateSessionParameters(requestInfo);
}
@@ -129,18 +128,7 @@ public class WebSession extends BaseWebSession
@NotNull Map<String, DBWSessionHandler<WebSession>> sessionHandlers,
@Nullable 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");
}
@@ -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,7 +254,7 @@ public class WebSession extends BaseWebSession
}
// Note: for admin use only
public void resetUserState(boolean needResetUserCache) throws DBException {
public synchronized void resetUserState(boolean needResetUserCache) throws DBException {
clearAuthTokens();
if (needResetUserCache) {
try {
@@ -283,7 +268,7 @@ public class WebSession extends BaseWebSession
clearSessionContext();
}
public void resetUserState() throws DBException {
public synchronized void resetUserState() throws DBException {
resetUserState(true);
}
@@ -292,35 +277,26 @@ public class WebSession extends BaseWebSession
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() {
@@ -351,8 +327,7 @@ 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);
@@ -370,7 +345,6 @@ public class WebSession extends BaseWebSession
return sessionProject;
}
@NotNull
protected WebSessionProjectImpl createSessionProject(@NotNull RMProject project) throws DBException {
return new WebSessionProjectImpl(this, project, getProjectPath(project));
}
@@ -380,24 +354,37 @@ 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);
private void resetSessionCache() throws DBCException {
// Clear attributes
synchronized (attributes) {
for (Map.Entry<String, ThrowableConsumer<Object, Exception>> attrDisposer : attributeDisposers.entrySet()) {
Object attrValue = attributes.get(attrDisposer.getKey());
if (this.navigatorModel != null) {
this.navigatorModel.dispose();
this.navigatorModel = null;
try {
attrDisposer.getValue().accept(attrValue);
} catch (Exception e) {
log.error("Error disposing attribute '" + attrDisposer.getKey() + "'", e);
}
}
} 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 +427,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,7 +449,7 @@ public class WebSession extends BaseWebSession
}
}
public void updateInfo(boolean isOldHttpSessionUsed) {
public synchronized void updateInfo(boolean isOldHttpSessionUsed) {
log.trace("Update session lifetime " + getSessionId() + " for user " + getUserId());
touchSession();
if (isOldHttpSessionUsed) {
@@ -504,6 +481,7 @@ public class WebSession extends BaseWebSession
public void close() {
try {
resetNavigationModel();
resetSessionCache();
} catch (Throwable e) {
log.error(e);
}
@@ -520,6 +498,7 @@ public class WebSession extends BaseWebSession
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
try {
resetNavigationModel();
resetSessionCache();
} catch (Throwable e) {
log.error(e);
}
@@ -534,7 +513,6 @@ public class WebSession extends BaseWebSession
super.close(clearTokens, sendSessionExpiredEvent);
}
@NotNull
private List<WebAuthInfo> clearAuthTokens() throws DBException {
ArrayList<WebAuthInfo> tokensCopy;
synchronized (authTokens) {
@@ -619,13 +597,11 @@ 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
@@ -666,11 +642,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 +657,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 +684,46 @@ 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, E extends Exception> T getAttribute(
String name,
ThrowableFunction<String, T, E> creator,
ThrowableConsumer<T, E> disposer
) throws E {
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, (ThrowableConsumer<Object, Exception>) disposer);
}
}
}
return (T) value;
}
}
@Property
public Map<String, Object> getActionParameters() {
WebActionParameters action = WebActionParameters.fromSession(this, true);
@@ -737,7 +752,7 @@ public class WebSession extends BaseWebSession
}
}
@NotNull
public List<WebAuthInfo> getAllAuthInfo() {
synchronized (authTokens) {
return new ArrayList<>(authTokens);
@@ -789,7 +804,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 +830,6 @@ public class WebSession extends BaseWebSession
return oldInfo;
}
@Nullable
public List<DBACredentialsProvider> getContextCredentialsProviders() {
return getAdapters(DBACredentialsProvider.class);
}
@@ -903,11 +917,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 +940,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 +949,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 +960,6 @@ public class WebSession extends BaseWebSession
return project;
}
@NotNull
public List<WebSessionProjectImpl> getAccessibleProjects() {
return getWorkspace().getProjects();
}
@@ -957,35 +969,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 +996,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 +1010,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) {
@@ -1049,6 +1048,7 @@ public class WebSession extends BaseWebSession
}
}
public void handleTaskConfirmationWithParameters(@NotNull String taskId, @NotNull Map<String, Object> parameters) {
String attributeName = getTaskConfirmationAttributeName(taskId);
CompletableFuture<Map<String, Object>> confirmationFuture = getAttribute(attributeName);
@@ -1060,16 +1060,6 @@ public class WebSession extends BaseWebSession
}
}
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;
@@ -1091,4 +1081,6 @@ public class WebSession extends BaseWebSession
return true;
}
private record PersistentAttribute(Object 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;
}
public WebSessionProjectImpl getProjectById(@Nullable String projectId) {
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;
@@ -87,7 +87,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(@NotNull SMAuthInfo smAuthInfo) throws DBException {
if (smAuthInfo.getAuthPermissions() == null && !isAuthorizedInSecurityManager()) {
throw new DBCException("Required information about session permissions is missing");
}
@@ -175,56 +175,53 @@ 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() {
public synchronized 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 +234,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,8 +241,7 @@ public class WebUserContext implements SMCredentialsProvider {
return secretController;
}
@Nullable
public String getSmSessionId() {
public synchronized String getSmSessionId() {
return smSessionId;
}
@@ -260,7 +255,6 @@ public class WebUserContext implements SMCredentialsProvider {
return rmController;
}
@NotNull
public DBFileController getFileController() {
return fileController;
}
@@ -274,7 +268,6 @@ public class WebUserContext implements SMCredentialsProvider {
this.refreshToken = refreshToken;
}
@NotNull
public WebSessionPreferenceStore getPreferenceStore() {
return preferenceStore;
}
@@ -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() {
@@ -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;
}
}
@@ -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) {
}
@@ -24,7 +24,6 @@ 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;
@@ -113,22 +112,12 @@ public class WebGQLClient {
@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
) 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);
.header("Content-Type", "application/json");
if (!requestHeaders.isEmpty()) {
requestHeaders.forEach(requestBuilder::header);
}
@@ -143,7 +132,7 @@ public class WebGQLClient {
throw new DBException(message);
}
Map<String, Object> parsed = new LinkedHashMap<>();
parsed.put("data", JSONUtils.getObject(body, "data").get(resultName));
parsed.put("data", JSONUtils.getObject(body, "data").get("result"));
parsed.put("headers", response.headers().map());
return parsed;
}
@@ -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()
@@ -162,24 +162,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);
@@ -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 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);
}
}
@@ -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);
}
@@ -1,40 +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.websocket.event.client;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.websocket.WSConstants;
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
public class WSSessionCancelActionClientEvent extends WSClientEvent {
public static final String ID = "cb_client_cancel_action";
@NotNull
private final String actionId;
public WSSessionCancelActionClientEvent(
@NotNull String actionId
) {
super(ID, WSConstants.TOPIC_SESSION_ACTION);
this.actionId = actionId;
}
@NotNull
public String getActionId() {
return actionId;
}
}
@@ -16,24 +16,23 @@
*/
package io.cloudbeaver.websocket.event.task;
import io.cloudbeaver.service.sql.WebSQLQueryParameterRecord;
import org.jkiss.code.NotNull;
import java.util.List;
import java.util.Map;
public class WSSessionTaskQueryParamsConfirmationEvent extends WSAbstractSessionTaskEvent {
private static final String ID = "cb_session_task_query_params_confirmation_request";
@NotNull
private final String query;
@NotNull
private final List<WebSQLQueryParameterRecord> parameters;
private final Map<String, Object> parameters;
public WSSessionTaskQueryParamsConfirmationEvent(
@NotNull String taskId,
@NotNull String title,
@NotNull String message,
@NotNull String query,
@NotNull List<WebSQLQueryParameterRecord> parameters
@NotNull Map<String, Object> parameters
) {
super(ID, taskId, title, message);
this.query = query;
@@ -46,7 +45,7 @@ public class WSSessionTaskQueryParamsConfirmationEvent extends WSAbstractSession
}
@NotNull
public List<WebSQLQueryParameterRecord> getParameters() {
public Map<String, Object> getParameters() {
return parameters;
}
}
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Community Product
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
Bundle-Version: 26.1.2.qualifier
Bundle-Release-Date: 20260706
Bundle-Version: 25.3.4.qualifier
Bundle-Release-Date: 20260202
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce
@@ -10,8 +10,9 @@
</extension>
<extension point="org.eclipse.core.runtime.products" id="product">
<product application="io.cloudbeaver.product.ce.application" name="CloudBeaver Community">
<property name="appName" value="CloudBeaver Community"/>
<product application="io.cloudbeaver.product.ce.application" description="CloudBeaver CE Web UI Application"
name="CloudBeaver CE Server">
<property name="appName" value="CloudBeaver CE Server"/>
<property name="versionUpdateURL" value="https://dbeaver.com/product/cloudbeaver-ce-version.json"/>
</product>
</extension>
@@ -20,7 +21,8 @@
<application
id="cloudbeaver-ce"
family="CB"
name="CloudBeaver Community"
name="CloudBeaver CE Server"
description="CloudBeaver CE Web UI"
server="true"/>
</extension>
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.product.ce</artifactId>
<version>26.1.2-SNAPSHOT</version>
<version>25.3.4-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Cloudbeaver Base JDBC drivers
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
Bundle-Version: 1.0.149.qualifier
Bundle-Release-Date: 20260706
Bundle-Version: 1.0.139.qualifier
Bundle-Release-Date: 20260202
Bundle-Vendor: DBeaver Corp
Bundle-ActivationPolicy: lazy
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
@@ -14,9 +14,7 @@
<resource name="drivers/jaybird"/>
<resource name="drivers/h2"/>
<resource name="drivers/h2_v2"/>
<resource name="drivers/h2_v3"/>
<resource name="drivers/sqlite/xerial"/>
<resource name="drivers/libsql"/>
<resource name="drivers/mssql/new"/>
<resource name="drivers/trino"/>
<resource name="drivers/kyuubi"/>
@@ -37,9 +35,7 @@
<bundle id="drivers.firebird" label="Firebird drivers"/>
<bundle id="drivers.h2" label="H2 drivers"/>
<bundle id="drivers.h2_v2" label="H2 v2 drivers"/>
<bundle id="drivers.h2_v3" label="H2 v3 drivers"/>
<bundle id="drivers.sqlite.xerial" label="SQLite drivers"/>
<bundle id="drivers.libsql" label="LibSQL drivers"/>
<bundle id="drivers.mssql.new" label="SQL Server drivers"/>
<bundle id="drivers.trino" label="Trino drivers"/>
<bundle id="drivers.kyuubi" label="Apache Kyuubi drivers"/>
@@ -59,7 +55,6 @@
<driver id="clickhouse:com_clickhouse"/>
<driver id="h2:h2_embedded"/>
<driver id="h2:h2_embedded_v2"/>
<driver id="h2:h2_embedded_v3"/>
<driver id="sqlite:sqlite_jdbc"/>
<driver id="sqlite:libsql_jdbc"/>
<driver id="sqlserver:microsoft"/>
@@ -9,6 +9,6 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
<version>1.0.149-SNAPSHOT</version>
<version>1.0.139-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -1,10 +1,10 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: CloudBeaver Community
Bundle-Name: Cloudbeaver CE Server
Bundle-SymbolicName: io.cloudbeaver.server.ce;singleton:=true
Bundle-Version: 26.1.2.qualifier
Bundle-Release-Date: 20260706
Bundle-Version: 25.3.4.qualifier
Bundle-Release-Date: 20260202
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.server.ce</artifactId>
<version>26.1.2-SNAPSHOT</version>
<version>25.3.4-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -34,7 +34,7 @@ public class CBWebServerConfig extends WebServerConfig {
@Property
public String getServerURL() {
return ""; // backward compatibility
return CommonUtils.notEmpty(cbApp.getServerConfiguration().getServerURL());
}
@Property
@@ -31,6 +31,7 @@ public class CBServerConfig implements WebServerConfiguration {
private static final Log log = Log.getLog(CBServerConfig.class);
protected String serverURL;
@NotNull
protected List<String> supportedHosts = new ArrayList<>();
protected boolean forceHttps;
@@ -61,6 +62,10 @@ public class CBServerConfig implements WebServerConfiguration {
this.securityManagerConfiguration = createSecurityManagerConfiguration();
}
public String getServerURL() {
return serverURL;
}
public int getServerPort() {
return serverPort;
}
@@ -103,6 +108,10 @@ public class CBServerConfig implements WebServerConfiguration {
return staticContent;
}
public void setServerURL(String serverURL) {
this.serverURL = serverURL;
}
public void setServerPort(int serverPort) {
this.serverPort = serverPort;
}
@@ -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,7 +16,6 @@
*/
package io.cloudbeaver.server;
import graphql.execution.instrumentation.Instrumentation;
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.auth.NoAuthCredentialsProvider;
import io.cloudbeaver.model.CBWebServerConfig;
@@ -30,7 +29,6 @@ import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.registry.WebDriverRegistry;
import io.cloudbeaver.registry.WebServiceRegistry;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
import io.cloudbeaver.server.jetty.CBJettyServer;
import io.cloudbeaver.service.DBWServiceInitializer;
import io.cloudbeaver.service.DBWServiceServerConfigurator;
@@ -47,14 +45,14 @@ import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBConstants;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.app.DBPPlatform;
import org.jkiss.dbeaver.model.auth.SMAuthConfiguration;
import org.jkiss.dbeaver.model.auth.AuthInfo;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
import org.jkiss.dbeaver.model.security.SMAdminController;
import org.jkiss.dbeaver.model.security.SMConstants;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.websocket.event.WSEventController;
import org.jkiss.dbeaver.model.websocket.event.WSServerConfigurationChangedEvent;
import org.jkiss.dbeaver.runtime.DBWorkbench;
@@ -121,6 +119,11 @@ public abstract class CBApplication<T extends CBServerConfig>
this.homeDirectory = new File(initHomeFolder());
}
@Nullable
public String getServerURL() {
return getServerConfiguration().getServerURL();
}
// Port this server listens on. If set the 0 a random port is assigned which may be obtained with getLocalPort()
@Override
public int getServerPort() {
@@ -228,11 +231,11 @@ public abstract class CBApplication<T extends CBServerConfig>
Location instanceLoc = Platform.getInstanceLocation();
try {
if (!instanceLoc.isSet()) { // always false?
URL wsLocationURL = getWorkspacePath().toUri().toURL();
URL wsLocationURL = getWorkspaceDirectory().toUri().toURL();
instanceLoc.set(wsLocationURL, true);
}
} catch (Exception e) {
log.error("Error setting workspace location to " + getWorkspacePath().toAbsolutePath(), e);
log.error("Error setting workspace location to " + getWorkspaceDirectory().toAbsolutePath(), e);
return;
}
this.systemInformationCollector = createSystemInformationCollector();
@@ -256,6 +259,9 @@ public abstract class CBApplication<T extends CBServerConfig>
//log.debug("\tProduct details: " + application.getInfoDetails());
log.debug("\tListen port: " + getServerPort() + (CommonUtils.isEmpty(getServerHost()) ? " on all interfaces" : " on " + getServerHost()));
log.debug("\tBase URI: " + getServicesURI());
if (!isConfigurationMode()) {
log.debug("\tGlobal access server URL: " + getServerConfiguration().getServerURL());
}
if (isDevelMode()) {
log.debug("\tDevelopment mode");
} else {
@@ -350,6 +356,7 @@ public abstract class CBApplication<T extends CBServerConfig>
*/
protected void performAutoConfiguration(Path configPath) {
String autoServerName = System.getenv(CBConstants.VAR_AUTO_CB_SERVER_NAME);
String autoServerURL = System.getenv(CBConstants.VAR_AUTO_CB_SERVER_URL);
String autoAdminName = System.getenv(CBConstants.VAR_AUTO_CB_ADMIN_NAME);
String autoAdminPassword = System.getenv(CBConstants.VAR_AUTO_CB_ADMIN_PASSWORD);
@@ -364,6 +371,7 @@ public abstract class CBApplication<T extends CBServerConfig>
autoProps.load(is);
autoServerName = autoProps.getProperty(CBConstants.VAR_AUTO_CB_SERVER_NAME);
autoServerURL = autoProps.getProperty(CBConstants.VAR_AUTO_CB_SERVER_URL);
autoAdminName = autoProps.getProperty(CBConstants.VAR_AUTO_CB_ADMIN_NAME);
autoAdminPassword = autoProps.getProperty(CBConstants.VAR_AUTO_CB_ADMIN_PASSWORD);
} catch (IOException e) {
@@ -381,6 +389,7 @@ public abstract class CBApplication<T extends CBServerConfig>
}
CBServerConfig serverConfig = new CBServerConfig();
serverConfig.setServerName(autoServerName);
serverConfig.setServerURL(autoServerURL);
serverConfig.setMaxSessionIdleTime(getMaxSessionIdleTime());
try {
finishConfiguration(
@@ -443,7 +452,7 @@ public abstract class CBApplication<T extends CBServerConfig>
@NotNull
public Path getDataDirectory(boolean create) {
Path dataDir = getWorkspacePath().resolve(CBConstants.RUNTIME_DATA_DIR_NAME);
Path dataDir = getWorkspaceDirectory().resolve(CBConstants.RUNTIME_DATA_DIR_NAME);
if (create && !Files.exists(dataDir)) {
try {
Files.createDirectories(dataDir);
@@ -534,7 +543,7 @@ public abstract class CBApplication<T extends CBServerConfig>
public synchronized void finishConfiguration(
@NotNull String adminName,
@Nullable String adminPassword,
@NotNull List<SMAuthConfiguration> authInfoList,
@NotNull List<AuthInfo> authInfoList,
@NotNull CBServerConfig serverConfig,
@NotNull CBAppConfig appConfig,
@Nullable SMCredentialsProvider credentialsProvider
@@ -591,7 +600,7 @@ public abstract class CBApplication<T extends CBServerConfig>
protected abstract void finishSecurityServiceConfiguration(
@NotNull String adminName,
@Nullable String adminPassword,
@NotNull List<SMAuthConfiguration> authInfoList
@NotNull List<AuthInfo> authInfoList
) throws DBException;
public synchronized void flushConfiguration(SMCredentialsProvider webSession) throws DBException {
@@ -687,11 +696,6 @@ public abstract class CBApplication<T extends CBServerConfig>
return new CBSessionManager(this);
}
@NotNull
public GraphQLEndpoint createGraphQLEndpoint(@NotNull Instrumentation instrumentation) {
return new GraphQLEndpoint(instrumentation);
}
@NotNull
public WebDriverRegistry getDriverRegistry() {
return WebDriverRegistry.getInstance();
@@ -743,7 +747,7 @@ public abstract class CBApplication<T extends CBServerConfig>
sendConfigChangedEvent(credentialsProvider);
}
protected void sendConfigChangedEvent(@Nullable SMCredentialsProvider credentialsProvider) {
protected void sendConfigChangedEvent(SMCredentialsProvider credentialsProvider) {
String sessionId = null;
if (credentialsProvider != null && credentialsProvider.getActiveUserCredentials() != null) {
sessionId = credentialsProvider.getActiveUserCredentials().getSmSessionId();
@@ -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.
@@ -27,7 +27,7 @@ import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBFileController;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.SMAuthConfiguration;
import org.jkiss.dbeaver.model.auth.AuthInfo;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.rm.RMController;
import org.jkiss.dbeaver.model.security.SMAdminController;
@@ -75,16 +75,13 @@ public class CBApplicationCE extends CBApplication<CBServerConfig> {
);
}
@NotNull
@Override
public RMController createResourceController(
@NotNull SMCredentialsProvider credentialsProvider,
@NotNull DBPWorkspace workspace
) throws DBException {
var lockManager = createLockManager();
return LocalResourceController
.builder(credentialsProvider, workspace, lockManager, this::getSecurityController)
.build();
public RMController createResourceController(@NotNull SMCredentialsProvider credentialsProvider,
@NotNull DBPWorkspace workspace) throws DBException {
return LocalResourceController.builder(credentialsProvider, workspace, this::getSecurityController).build();
}
@NotNull
@@ -101,15 +98,10 @@ public class CBApplicationCE extends CBApplication<CBServerConfig> {
protected void finishSecurityServiceConfiguration(
@NotNull String adminName,
@Nullable String adminPassword,
@NotNull List<SMAuthConfiguration> authInfoList
@NotNull List<AuthInfo> authInfoList
) throws DBException {
if (securityController instanceof CBEmbeddedSecurityController<?> embeddedSecurityController) {
embeddedSecurityController.finishConfiguration(adminName, adminPassword, authInfoList);
}
}
@Override
public boolean isCommunity() {
return true;
}
}
@@ -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.
@@ -21,7 +21,6 @@ import io.cloudbeaver.server.jobs.SessionStateJob;
import io.cloudbeaver.server.jobs.WebDataSourceMonitorJob;
import io.cloudbeaver.server.jobs.WebSessionMonitorJob;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.app.DBPRegistryListener;
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
@@ -52,7 +51,7 @@ public class CBPlatform extends BaseWebPlatform {
}
@Override
protected synchronized void initialize() throws DBException {
protected synchronized void initialize() {
long startTime = System.currentTimeMillis();
log.info("Initialize web platform " + getClass().getSimpleName());
this.preferenceStore = new WebServerPreferenceStore(WebPlatformActivator.getInstance().getPreferences());
@@ -56,9 +56,6 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
private static final Log log = Log.getLog(CBServerConfigurationController.class);
private static final String NETWORK_MODE_ENV = "NETWORK_MODE";
private static final String NETWORK_MODE_VALUE_HOST = "host";
// Configurations
@NotNull
private final T serverConfiguration;
@@ -170,9 +167,12 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
public T parseServerConfiguration() {
var config = getServerConfiguration();
if (NETWORK_MODE_VALUE_HOST.equalsIgnoreCase(System.getenv(NETWORK_MODE_ENV))) {
String hostName = getLocalHostAddress();
config.setServerHost(hostName);
if (config.getServerURL() == null) {
String hostName = config.getServerHost();
if (CommonUtils.isEmpty(hostName)) {
hostName = getLocalHostAddress();
}
config.setServerURL("http://" + hostName + ":" + config.getServerPort());
}
config.setContentRoot(ServletAppUtils.getRelativePath(config.getContentRoot(), homeDirectory));
@@ -383,6 +383,12 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
}
}
public synchronized void updateServerUrl(@NotNull SMCredentialsProvider credentialsProvider,
@Nullable String newPublicUrl) throws DBException {
getServerConfiguration().setServerURL(newPublicUrl);
}
protected Map<String, Object> collectConfigurationProperties(
@NotNull CBServerConfig serverConfig,
@NotNull CBAppConfig appConfig
@@ -517,6 +523,10 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
CBConstants.PARAM_SERVER_NAME,
serverConfig.getServerName());
}
if (!CommonUtils.isEmpty(serverConfig.getServerURL())) {
copyConfigValue(
originServerConfig, serverConfigProperties, CBConstants.PARAM_SERVER_URL, serverConfig.getServerURL());
}
if (serverConfig.getMaxSessionIdleTime() > 0) {
copyConfigValue(
originServerConfig,
@@ -24,7 +24,6 @@ import io.cloudbeaver.server.WebAppUtils;
import io.cloudbeaver.service.security.SMUtils;
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.security.SMAdminController;
@@ -34,6 +33,7 @@ import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.model.websocket.event.permissions.WSObjectPermissionEvent;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -43,11 +43,10 @@ import java.util.stream.Collectors;
public class WSObjectPermissionUpdatedEventHandler extends WSDefaultEventHandler<WSObjectPermissionEvent> {
private static final Log log = Log.getLog(WSObjectPermissionUpdatedEventHandler.class);
@Nullable
@Override
protected Consumer<BaseWebSession> getEventConsumer(@NotNull WSObjectPermissionEvent event) {
public void handleEvent(@NotNull WSObjectPermissionEvent event) {
String objectId = event.getObjectId();
return switch (event.getSmObjectType()) {
Consumer<BaseWebSession> runnable = switch (event.getSmObjectType()) {
case project:
yield getUpdateUserProjectsInfoConsumer(event, objectId);
case datasource:
@@ -62,6 +61,23 @@ public class WSObjectPermissionUpdatedEventHandler extends WSDefaultEventHandler
yield null;
}
};
if (runnable == null) {
return;
}
log.debug(event.getTopicId() + " event handled");
Collection<BaseWebSession> allSessions = CBApplication.getInstance().getSessionManager().getAllActiveSessions();
for (var activeUserSession : allSessions) {
if (!isAcceptableInSession(activeUserSession, event)) {
log.debug("Cannot handle %s event '%s' in session %s".formatted(
event.getTopicId(),
event.getId(),
activeUserSession.getSessionId()
));
continue;
}
log.debug("%s event '%s' handled".formatted(event.getTopicId(), event.getId()));
runnable.accept(activeUserSession);
}
}
@NotNull
@@ -24,7 +24,6 @@ import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.filters.ServerConfigurationTimeLimitFilter;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
import io.cloudbeaver.server.servlets.CBImageServlet;
import io.cloudbeaver.server.servlets.CBManifestServlet;
import io.cloudbeaver.server.servlets.CBStaticServlet;
import io.cloudbeaver.server.servlets.WebStatusServlet;
import io.cloudbeaver.server.websockets.CBEventsLongPollingServlet;
@@ -33,8 +32,8 @@ import io.cloudbeaver.server.websockets.CBWebSocketServerConfigurator;
import io.cloudbeaver.service.DBWServiceBindingServlet;
import io.cloudbeaver.service.DBWServiceBindingWebSocket;
import jakarta.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.ee11.servlet.*;
import org.eclipse.jetty.ee11.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer;
import org.eclipse.jetty.ee10.servlet.*;
import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.eclipse.jetty.xml.XmlConfiguration;
@@ -120,15 +119,12 @@ public class CBJettyServer {
ServletHolder imagesServletHolder = new ServletHolder("images", new CBImageServlet());
servletContextHandler.addServlet(imagesServletHolder, serverConfiguration.getServicesURI() + "images/*");
ServletHolder manifestServletHolder = new ServletHolder("manifest", new CBManifestServlet(contentRootPath));
servletContextHandler.addServlet(manifestServletHolder, "/manifest.webmanifest");
servletContextHandler.addServlet(new ServletHolder("status", new WebStatusServlet()), "/status");
ServletHolder eventsServletHolder = new ServletHolder("events", new CBEventsLongPollingServlet());
servletContextHandler.addServlet(eventsServletHolder, serverConfiguration.getServicesURI() + "events/*");
GraphQLEndpoint endpoint = application.createGraphQLEndpoint(new ServerConfigurationTimeLimitFilter(application));
GraphQLEndpoint endpoint = new GraphQLEndpoint(new ServerConfigurationTimeLimitFilter(application));
application.addApplicationContextValue(GraphQL.class.getName(), endpoint.getGraphQL());
String gqlServletPath = serverConfiguration.getServicesURI() + "gql/*";
servletContextHandler.addServlet(
@@ -261,4 +257,4 @@ public class CBJettyServer {
cbSessionHandler.setSecureCookies(application.getServerConfiguration().isForceHttps());
}
}
}
}
@@ -39,6 +39,7 @@ public class CBServerContextListener implements ServletContextListener {
//scf.setDomain(domain);
//scf.setMaxAge(CB_SESSION_LIFE_TIME);
cookieConfig.setPath(CBApplication.getInstance().getRootURI());
// cookieConfig.setSecure(application.getServerURL().startsWith("https"));
cookieConfig.setHttpOnly(true);
cookieConfig.setName(CBConstants.CB_SESSION_COOKIE_NAME);
}
@@ -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,7 +33,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class RequestHostFilter implements Filter {
private static final Log log = Log.getLog(RequestHostFilter.class);
@@ -75,19 +74,9 @@ public class RequestHostFilter implements Filter {
chain.doFilter(request, response);
return;
}
if (CommonUtils.isNotEmpty(originUri.getHost())) {
requestAllowed = InetAddresses.isInetAddress(originUri.getHost());
} else {
log.debug(
"Request origin host is null, request URI - " + originUri +
", request path - " + httpRequest.getServletPath() +
", request url - " + httpRequest.getRequestURL()
);
}
boolean isIpAddress = InetAddresses.isInetAddress(originUri.getHost());
String servletPath = httpRequest.getServletPath();
requestAllowed = isIpAddress;
if (!requestAllowed) {
if (CommonUtils.isNotEmpty(servletPath)) {
for (String excludedPath : excludedPaths) {
@@ -156,10 +145,10 @@ public class RequestHostFilter implements Filter {
if (!availableHosts.contains(requestHost)) {
for (String errorPath : errorPaths) {
if (httpRequest.getServletPath().contains(errorPath)) {
log.warn("Request host '" + requestHost + "' is not allowed. Available hosts: " + availableHosts);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().write("Request host is not allowed. Available hosts: " + availableHosts);
response.getWriter().write(
"Request host '" + requestHost + "' is not allowed. Available hosts: " + availableHosts
);
return false;
}
}
@@ -1,119 +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.servlets;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.server.CBApplication;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpHeader;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.utils.MimeTypes;
import org.jkiss.utils.IOUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@WebServlet(urlPatterns = "/manifest.webmanifest")
public class CBManifestServlet extends HttpServlet {
private static final Log log = Log.getLog(CBManifestServlet.class);
private static final String MANIFEST_FILE = "manifest.webmanifest";
// Fix: Create Gson instance with HTML escaping disabled to prevent \u003d escaping
private static final Gson GSON = new GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping() // This prevents escaping = to \u003d
.create();
@NotNull
private final Path contentRoot;
public CBManifestServlet(@NotNull Path contentRoot) {
this.contentRoot = contentRoot;
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Path manifestPath = contentRoot.resolve(MANIFEST_FILE).normalize();
if (!manifestPath.startsWith(contentRoot) || !Files.exists(manifestPath)) {
log.error("Manifest file not found or path is invalid: " + manifestPath);
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
try {
// Read the original manifest file
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (InputStream fis = Files.newInputStream(manifestPath)) {
IOUtils.copyStream(fis, baos);
}
String manifestContent = baos.toString(StandardCharsets.UTF_8);
// Parse the JSON and update dynamic fields
CBServerConfig serverConfig = CBApplication.getInstance().getServerConfiguration();
String rootURI = serverConfig.getRootURI();
// Ensure rootURI ends with / for proper scope and path handling
String normalizedRootURI = rootURI;
if (!normalizedRootURI.endsWith("/")) {
normalizedRootURI += "/";
}
// Use JsonParser to parse the existing manifest
JsonObject manifest = JsonParser.parseString(manifestContent).getAsJsonObject();
// Update scope to match the rootURI
manifest.addProperty("scope", normalizedRootURI);
// Update start_url and id to match the rootURI
String startUrl = normalizedRootURI + "?source=pwa";
manifest.addProperty("start_url", startUrl);
manifest.addProperty("id", startUrl);
// Convert back to JSON string using the Gson instance with HTML escaping disabled
String updatedManifest = GSON.toJson(manifest);
byte[] manifestBytes = updatedManifest.getBytes(StandardCharsets.UTF_8);
// Set appropriate headers
response.setHeader(HttpHeader.CONTENT_TYPE.toString(), "application/manifest+json");
response.setHeader(HttpHeader.CACHE_CONTROL.toString(), "no-cache, no-store, must-revalidate");
response.setHeader(HttpHeader.EXPIRES.toString(), "0");
response.setContentLength(manifestBytes.length);
// Write the response
response.getOutputStream().write(manifestBytes);
} catch (Exception e) {
log.error("Error processing manifest file", e);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
}
@@ -34,7 +34,7 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.ee11.servlet.DefaultServlet;
import org.eclipse.jetty.ee10.servlet.DefaultServlet;
import org.eclipse.jetty.http.HttpHeader;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
@@ -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,9 +24,9 @@ import io.cloudbeaver.registry.WebSessionHandlerDescriptor;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.WebAppSessionManager;
import io.cloudbeaver.server.events.WSWebUtils;
import io.cloudbeaver.service.DBWSessionHandler;
import io.cloudbeaver.utils.ServletAppUtils;
import io.cloudbeaver.utils.WebEventUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
@@ -51,7 +51,7 @@ public class CBSessionManager implements WebAppSessionManager {
private static final Log log = Log.getLog(CBSessionManager.class);
private final CBApplication<?> application;
protected final Map<String, BaseWebSession> sessionMap = new HashMap<>();
private final Map<String, BaseWebSession> sessionMap = new HashMap<>();
public CBSessionManager(CBApplication<?> application) {
this.application = application;
@@ -60,7 +60,6 @@ public class CBSessionManager implements WebAppSessionManager {
/**
* Closes Web Session, associated to HttpSession from {@code request}
*/
@Nullable
@Override
public BaseWebSession closeSession(@NotNull HttpServletRequest request) {
HttpSession session = request.getSession();
@@ -70,13 +69,11 @@ public class CBSessionManager implements WebAppSessionManager {
return null;
}
@Nullable
@Override
public BaseWebSession closeSession(@NotNull String sessionId) {
return closeSession(sessionId, true);
}
@Nullable
@Override
public BaseWebSession closeSession(@NotNull String sessionId, boolean sendSessionExpiredEvent) {
BaseWebSession webSession;
@@ -202,48 +199,12 @@ public class CBSessionManager implements WebAppSessionManager {
return httpSession.getId();
}
/**
* Invalidates the current HTTP session, creates a new one, and binds a new {@link WebSession} to it.
*/
@NotNull
public WebSession rotateSession(
@NotNull HttpServletRequest request,
@NotNull WebSession oldWebSession
) throws DBWebException {
HttpSession oldHttpSession = request.getSession(false);
if (oldHttpSession != null) {
oldHttpSession.invalidate();
}
String newSessionId = request.getSession(true).getId();
String locale = oldWebSession.getLocale();
String remoteAddr = oldWebSession.getLastRemoteAddr();
String remoteUserAgent = oldWebSession.getLastRemoteUserAgent();
var requestInfo = new WebHttpRequestInfo(newSessionId, locale, remoteAddr, remoteUserAgent);
WebSession newWebSession;
try {
newWebSession = createWebSessionImpl(requestInfo);
} catch (DBException e) {
throw new DBWebException(e);
}
oldWebSession.migrateEventHandlersTo(newWebSession);
String oldSessionId = oldWebSession.getSessionId();
synchronized (sessionMap) {
sessionMap.remove(oldSessionId);
sessionMap.put(newSessionId, newWebSession);
}
oldWebSession.close(false, false);
log.debug("Session rotated '" + oldSessionId + "' -> '" + newSessionId + "'");
return newWebSession;
}
/**
* Returns not expired session from cache, or restore it.
*
* @return WebSession object or null, if session expired or invalid
*/
@NotNull
@Nullable
public WebSession getOrRestoreWebSession(@NotNull WebHttpRequestInfo requestInfo) {
final var sessionId = requestInfo.getId();
if (sessionId == null) {
@@ -322,19 +283,19 @@ public class CBSessionManager implements WebAppSessionManager {
@Override
@Nullable
public WebSession findWebSession(@NotNull HttpServletRequest request) {
public WebSession findWebSession(HttpServletRequest request) {
String sessionId = getSessionId(request);
WebSession webSession;
synchronized (sessionMap) {
var session = sessionMap.get(sessionId);
webSession = (session instanceof WebSession) ? (WebSession) session : null;
if (session instanceof WebSession) {
return (WebSession) session;
}
return null;
}
return webSession;
}
@Nullable
@Override
public WebSession findWebSession(@NotNull HttpServletRequest request, boolean errorOnNoFound) throws DBWebException {
public WebSession findWebSession(HttpServletRequest request, boolean errorOnNoFound) throws DBWebException {
WebSession webSession = findWebSession(request);
if (webSession != null) {
return webSession;
@@ -362,7 +323,6 @@ public class CBSessionManager implements WebAppSessionManager {
}
}
@NotNull
@Override
public Collection<BaseWebSession> getAllActiveSessions() {
synchronized (sessionMap) {
@@ -419,63 +379,6 @@ public class CBSessionManager implements WebAppSessionManager {
}
}
@Nullable
public WebSession getWebSession(
@Nullable String smAccessToken,
@NotNull WebHttpRequestInfo requestInfo,
boolean create
) throws DBException {
if (CommonUtils.isEmpty(smAccessToken)) {
return null;
}
synchronized (sessionMap) {
var tempCredProvider = new SMTokenCredentialProvider(smAccessToken);
SMAuthPermissions authPermissions = application.createSecurityController(tempCredProvider).getTokenPermissions();
var sessionId = requestInfo.getId() != null ? requestInfo.getId()
: authPermissions.getSessionId();
var existSession = sessionMap.get(sessionId);
if (existSession instanceof WebSession webSession) {
var creds = webSession.getUserContext().getActiveUserCredentials();
if (creds == null || !smAccessToken.equals(creds.getSmAccessToken())) {
if (webSession.getUserContext().refresh(
smAccessToken,
null,
authPermissions
)) {
webSession.refreshUserData();
}
}
return webSession;
}
if (existSession != null) {
//session exist but it not web session
return null;
}
if (!create) {
return null;
}
if (requestInfo.getId() == null) {
requestInfo = new WebHttpRequestInfo(
sessionId,
requestInfo.getLocale(),
requestInfo.getLastRemoteAddress(),
requestInfo.getLastRemoteUserAgent()
);
}
var webSession = createWebSessionImpl(requestInfo);
webSession.getUserContext().refresh(
smAccessToken,
null,
authPermissions
);
webSession.refreshUserData();
sessionMap.put(sessionId, webSession);
return webSession;
}
}
/**
* Send session state with remaining alive time to all cached session
*/
@@ -541,7 +444,7 @@ public class CBSessionManager implements WebAppSessionManager {
for (Iterator<BaseWebSession> iterator = sessionMap.values().iterator(); iterator.hasNext(); ) {
var session = iterator.next();
iterator.remove();
session.close(false, !WebEventUtils.isSmSessionIdEquals(session, initiatorSessionId));
session.close(false, !WSWebUtils.isSessionIdEquals(session, initiatorSessionId));
}
}
}
@@ -567,5 +470,4 @@ public class CBSessionManager implements WebAppSessionManager {
log.debug("> Expire session '" + session.getSessionId() + "'");
session.close();
}
}
@@ -3,13 +3,17 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Server
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
Bundle-Version: 26.1.2.qualifier
Bundle-Release-Date: 20260706
Bundle-Version: 25.3.4.qualifier
Bundle-Release-Date: 20260202
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.model.sql;visibility:=reexport,
Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
org.apache.commons.jexl,
org.jkiss.utils;visibility:=reexport,
org.jkiss.dbeaver.model.sql;visibility:=reexport,
org.jkiss.dbeaver.data.gis,
org.jkiss.bundle.jetty.server;visibility:=reexport,
com.google.gson;visibility:=reexport,
org.jkiss.bundle.graphql;visibility:=reexport,
org.jkiss.bundle.apache.dbcp,
org.jkiss.dbeaver.net.ssh,
@@ -39,7 +39,7 @@
</extension>
<extension point="org.jkiss.dbeaver.ws.event.handler">
<eventHandler class="io.cloudbeaver.server.events.WSServerConfigurationChangedEventHandler">
<eventHandler class="io.cloudbeaver.server.events.WSDefaultEventHandler">
<topic id="cb_config"/>
</eventHandler>
<eventHandler class="io.cloudbeaver.server.events.WSDataSourceUpdatedEventHandlerImpl">

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