mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
47
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d22898da1 | ||
|
|
8eb012670d | ||
|
|
413d229028 | ||
|
|
b0fbd41b85 | ||
|
|
660c7d9e1f | ||
|
|
0f79ef21e7 | ||
|
|
d9b5a9a20d | ||
|
|
b200148765 | ||
|
|
18ecc829d3 | ||
|
|
58f51ee5f6 | ||
|
|
0f7c8fe69f | ||
|
|
776a91767d | ||
|
|
9f4f543eb3 | ||
|
|
e4e009e1ec | ||
|
|
895d27eb84 | ||
|
|
a0cbaa89f6 | ||
|
|
32d1ef9a70 | ||
|
|
8362946990 | ||
|
|
d3d71b79f0 | ||
|
|
c344210b8b | ||
|
|
f6aea971ef | ||
|
|
3d9c4391d1 | ||
|
|
19284d33e7 | ||
|
|
dc1e865545 | ||
|
|
4d62ddb5fc | ||
|
|
14df3b3120 | ||
|
|
30f5bd3bb3 | ||
|
|
04a4c19331 | ||
|
|
5bc6ea3731 | ||
|
|
d93d73bb44 | ||
|
|
6cdc6b2ee0 | ||
|
|
96ba18da83 | ||
|
|
a16c138bba | ||
|
|
f728590b90 | ||
|
|
ba7268bb8f | ||
|
|
1b7c81fa24 | ||
|
|
98f8380962 | ||
|
|
2d40781960 | ||
|
|
166efa634f | ||
|
|
fad07f3d73 | ||
|
|
4ca4976184 | ||
|
|
ed4f5e45ed | ||
|
|
a333e5841a | ||
|
|
2c3afd61f0 | ||
|
|
a817dcd92f | ||
|
|
c14e8f5ce3 | ||
|
|
cc3cf21890 |
@@ -6,6 +6,7 @@ on:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- ready_for_review
|
||||
push:
|
||||
branches: [devel]
|
||||
|
||||
@@ -24,20 +25,15 @@ 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
|
||||
secrets: inherit
|
||||
|
||||
lint-server:
|
||||
name: Server
|
||||
uses: dbeaver/dbeaver-common/.github/workflows/java-checkstyle.yml@devel
|
||||
secrets: inherit
|
||||
# lint-server:
|
||||
# name: Server
|
||||
# uses: dbeaver/dbeaver-common/.github/workflows/java-checkstyle.yml@devel
|
||||
# secrets: inherit
|
||||
|
||||
lint-frontend:
|
||||
name: Frontend
|
||||
|
||||
Vendored
+1
-2
@@ -14,7 +14,6 @@
|
||||
"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",
|
||||
@@ -22,4 +21,4 @@
|
||||
"esbenp.prettier-vscode",
|
||||
"devnaumov.licensify"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,16 @@
|
||||
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)
|
||||
@@ -19,181 +22,90 @@ See our [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
|
||||
|
||||
## Demo server
|
||||
|
||||
You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
|
||||
You can see a live demo of CloudBeaver server here: https://demo.cloudbeaver.io
|
||||
|
||||
[Database access instructions](https://github.com/dbeaver/cloudbeaver/wiki/Demo-Server)
|
||||
|
||||
## Changelog
|
||||
|
||||
### 26.0.5 2026-05-18
|
||||
### 26.1.0 2026-06-01
|
||||
|
||||
### Changes since 26.0.0:
|
||||
|
||||
- Administration:
|
||||
- Added support for custom startup arguments when running CloudBeaver in a container. Administrators can specify configuration file locations to adapt the application to their environment. (thanks to @MalteHei)
|
||||
- Updated the manifest.webmanifest file to generate dynamically based on the CLOUDBEAVER_ROOT_URI environment variable. (thanks to @houssemexo26)
|
||||
- Authorization:
|
||||
- Added HTTP header authentication support for GraphQL queries. Users can provide API tokens via Authorization or X-Api-Key headers, allowing external data tools to authenticate without modifying request bodies.
|
||||
- Data Editor:
|
||||
- Added a DDL generation option to the Data Editor and Result Set. Users can view table schemas directly while editing or reviewing records.
|
||||
- Fixed the copying of Boolean values in the clipboard in the Data Editor.
|
||||
- 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 more settings to the Generated SQL window, including the ability to select fully qualified names for query or use compact SQL formatting.
|
||||
- Added the ability to open generated queries right from the Generate SQL window.
|
||||
- General:
|
||||
- Updated the manifest.webmanifest file to dynamically generate the environment variable according to the CLOUDBEAVER_ROOT_URI. (thanks to @houssemexo26 )
|
||||
- Databases:
|
||||
- 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.
|
||||
- Snowflake: Updated driver to version 4.1.0.
|
||||
- SQL Server: Fixed the issue with connecting to the database 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 high vulnerability (CVE-2026-42035) in the axios library. The library was updated to version 1.15.1.
|
||||
- Fixed the medium vulnerability (CVE-2026-41305) in the postcss library. The library was updated to version 8.5.14.
|
||||
|
||||
### 26.0.4 2026-05-04
|
||||
|
||||
- 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 separate cells, not put in a single cell.
|
||||
- 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.
|
||||
- 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 the ability to view and edit scripts without connecting to the database.
|
||||
- Added more settings to the Generated SQL window, including the ability to select fully qualified names for query or use compact SQL formatting.
|
||||
- Added the ability to open in the SQL Editor generated queries right from the Generate SQL window.
|
||||
- Accessibility:
|
||||
- Fixed contrast for elements across different application parts in the light and dark themes to meet WCAG requirements.
|
||||
- General:
|
||||
- Removed the "Folders" setting from the available settings for the Navigator Tree.
|
||||
- Databases:
|
||||
- DuckDB: Fixed map rendering for GEOMETRY types with CRS parameters.
|
||||
- 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.
|
||||
- SQL Server: Fixed DDL generation for external tables.
|
||||
- Security:
|
||||
- Updated session handling to prompt the application to generate a new session identifier for each login.
|
||||
- Fixed the high vulnerability (CVE-2026-3505) in the bcpg-jdk18on library. The library was updated to version 1.84.0.
|
||||
|
||||
### 26.0.3 2026-04-20
|
||||
|
||||
- Data Editor:
|
||||
- Added key column mark for tables with unique keys in the data grid.
|
||||
- Added the ability to paste a copied cell into multiple cells and to set multiple cells to NULL.
|
||||
- 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:
|
||||
- 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 to improve application 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:
|
||||
- SQL Editor: Fixed keyword autocompletion for partially typed keywords. There are no extra letters for them after completion.
|
||||
- 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)
|
||||
- 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:
|
||||
- ClickHouse: Fixed the display of Array(JSON) types in the data grid.
|
||||
- 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.
|
||||
|
||||
### 26.0.2 2026-04-06
|
||||
|
||||
- UI improvements:
|
||||
- Marked icons for non-saved (temporary) scripts with the hourglass symbol to add visual distinction from the saved ones.
|
||||
- Added the tooltip about quoting rules for the Bind variables parameters dialog.
|
||||
- Improved keyboard navigation for context menu and buttons for Data Editor, SQL Editor, and Navigator tree.
|
||||
- Authentication:
|
||||
- Fixed the bug with the authentication form. Now the same password is required for the password change operation.
|
||||
- Fixed the display of authentication fields for SQL Server with Active Directory - MSI. The unnecessary Password field was removed.
|
||||
- Databases and drivers:
|
||||
- DuckDB: Fixed geometry type display for database versions > 1.5. (thanks @EastLord).
|
||||
- H2 database:
|
||||
- Added new embedded driver version 2.4.2.
|
||||
- Updated server driver to the version 2.4.2.
|
||||
- Security:
|
||||
- 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-2026-33228) in the flatted library. The library was updated to version 3.4.2.
|
||||
|
||||
### 26.0.1 2026-03-23
|
||||
|
||||
- Data Editor:
|
||||
- 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.
|
||||
- Added support for adding, duplicating, deleting, or setting values to NULL in multiple highlighted rows via the Data Editor context menu.
|
||||
- SQL query generation in the Data Editor is now available for columns containing large object data.
|
||||
- Improved column drag-and-drop behavior in the Data Editor. Users could drag columns from any area within the header and drop them onto any part of the target column or table body.
|
||||
- SQL Editor:
|
||||
- Added fuzzy search to SQL Editor auto-completion for object names. The feature allowed users to find tables and containers even with typos or incorrect letter ordering by matching strings that start with, contain, or resemble the input.
|
||||
- Fixed data editing for SQL scripts with variables.
|
||||
- General:
|
||||
- Added a close button to the right tab when it is the only open tab (ex. Scripts). Users no longer needed to use the main menu to close a single tab.
|
||||
- Replaced the generic "Something went wrong" message with a specific network error notification when the application cannot connect to the server.
|
||||
- Null or empty values can now be assigned to the Driver properties for all drivers.
|
||||
- Fixed an issue where re-selecting an existing SSH key file during connection edits caused problems.
|
||||
- Fixed the ability to switch tabs in the Data editor using the keyboard.
|
||||
- Fixed error notification spelling. Thanks to @Malcolm-B-Breaks.
|
||||
- ClickHouse:
|
||||
- Updated driver version to 0.9.7.
|
||||
- Fixed IP address display in the Data Editor by removing the leading slash.
|
||||
- PostgreSQL: Fixed an error when connecting via URL if the database name contains a hyphen.
|
||||
- Security:
|
||||
- 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.
|
||||
|
||||
### 26.0.0 2026-03-02
|
||||
|
||||
### Changes since 25.3.0:
|
||||
|
||||
- Security:
|
||||
- Enforced complete logout and screen data clearance upon session expiration;
|
||||
- Fixed the high vulnerability (CVE-2026-25639) in the axios library. The library was updated to version 1.13.5.
|
||||
- Administration:
|
||||
- Added support for mapping users to CloudBeaver teams based on LDAP memberOf group membership;
|
||||
- Change the User list settings in the Administration part to show both active and inactive users by default.
|
||||
- SQL Editor:
|
||||
- Added support for parameters and variables in queries. This feature allows queries to be reused by changing parameters at execution time. Enabled by default and configurable in personal preferences;
|
||||
- Added SQL preview to the Bind parameters/variables dialog to review queries with changed values on the fly;
|
||||
- Enabled Tab key for autocompletion in the SQL Editor alongside the Enter key;
|
||||
- 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;
|
||||
- Dangerous query confirmation is now shown for all DROP statements, not just for tables.
|
||||
- Data Editor:
|
||||
- Added ability to automatically generate INSERT, SELECT, DELETE, and UPDATE statements for the selected values;
|
||||
- Added undo and redo functionality for cell edits, row operations, and other data modifications. Retains the last 50 actions across the Data Editor, result sets, and related panels;
|
||||
- Added "Use local formatting" setting. 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 column pinning to keep key columns (e.g., IDs, names) visible while horizontal scrolling through wide tables;
|
||||
- Added status indicator icon in the top-left corner with tooltips explaining table editability. Indicates presence of primary keys, read-only connection settings, or read-only columns;
|
||||
- Added shortcut Ctrl/Cmd + . to cancel operations in Data Editor;
|
||||
- Fixed application freeze in canceling fetch size requests for large tables.
|
||||
- Navigator tree:
|
||||
- Added the ability to duplicate connection configuration in the project navigation tree. The "Clone connection" feature is available in the context menu;
|
||||
- Added the ability for users to configure the Simple or Advanced view in the Navigation tree for all connections or for each connection separately;
|
||||
- Added the ability to show table objects, such as columns or keys, in the Navigation tree. The setting is disabled by default and can be turned on in the Navigator settings panel;
|
||||
- Added the ability to rename connections via context menu in the Navigation Tree;
|
||||
- Added Connection Info tab to display basic information about the current connection for all users.
|
||||
- General:
|
||||
- Added support for long polling as a fallback when WebSockets are unavailable or blocked. Ensures reliable communication for metadata updates and SQL execution;
|
||||
- Extended browser support to versions up to three years old;
|
||||
- Redesigned the connection configuration page. Reorganized form fields and sections to provide more input space and reduce visual clutter;
|
||||
- Expanded pointer target areas for icons in the Navigator, editors, and tabs according to the accessibility standards;
|
||||
- Fixed a keyboard navigation issue for panels to keep the focus inside;
|
||||
- Renamed "Database Native" authentication type to "Username/password" in the connection dialog;
|
||||
- Fixed issue where the missing pg_dump utility caused errors during initialization or deployment updates when PostgreSQL was selected as the inner database.
|
||||
- Databases:
|
||||
- ClickHouse:
|
||||
- Updated driver to version 0.9.5
|
||||
- Added spatial data support
|
||||
- Fixed an issue with displaying arrays of UUID, IPv4/IPv6, and Map types
|
||||
- Fixed JSON data type reading
|
||||
- Resolved an issue with CTE expressions
|
||||
- DuckDB:
|
||||
- Updated driver to version 1.4.4.0;
|
||||
- Added support for the dollar-quoted string syntax for the SQL Editor
|
||||
- Oracle: Added a new "Set Username to OS_USER" option in the Misc section of Oracle connection settings. Automatically uses the current database username as the operating system user identifier in session metadata when enabled;
|
||||
- PostgreSQL: Added DDL display support for PostgreSQL policies.
|
||||
|
||||
[We are happy to reward](https://dbeaver.com/help-dbeaver/) our most active contributors every major sprint.
|
||||
- Fixed the medium vulnerability (CVE-2026-41305) in the postcss library. The library was updated to version 8.5.14.
|
||||
|
||||
+1
-13
@@ -1,18 +1,6 @@
|
||||
# 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 devops@dbeaver.com.
|
||||
You will receive a response from us within 48 hours.
|
||||
Please report (suspected) security vulnerabilities to <security@dbeaver.com>.
|
||||
If the issue is confirmed, we will release a patch as soon as possible, depending on complexity, but historically, within a few days.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* 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();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
{
|
||||
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: [
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
{
|
||||
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: []
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,9 @@ 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
|
||||
|
||||
@@ -42,6 +42,9 @@ 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
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
FROM ubuntu:24.04
|
||||
# Ubuntu 24.04 (Noble Numbat)
|
||||
FROM ubuntu:noble
|
||||
|
||||
MAINTAINER DBeaver Corp, devops@dbeaver.com
|
||||
|
||||
@@ -31,7 +32,7 @@ RUN set -eux; \
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
ENV JAVA_HOME=/opt/java/openjdk
|
||||
COPY --from=eclipse-temurin:21 $JAVA_HOME $JAVA_HOME
|
||||
COPY --from=eclipse-temurin:25-jdk-noble $JAVA_HOME $JAVA_HOME
|
||||
ENV PATH="${JAVA_HOME}/bin:${PATH}"
|
||||
|
||||
RUN set -eux; \
|
||||
|
||||
@@ -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.11.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Version: 1.0.13.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.model.cli;visibility:=reexport
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model.cli</artifactId>
|
||||
<version>1.0.11-SNAPSHOT</version>
|
||||
<version>1.0.13-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -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.102.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Version: 1.0.104.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model</artifactId>
|
||||
<version>1.0.102-SNAPSHOT</version>
|
||||
<version>1.0.104-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -17,6 +17,7 @@
|
||||
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;
|
||||
@@ -46,7 +47,7 @@ public class WebDataSourceConnectEventListener implements DBPEventListener {
|
||||
new WSDataSourceConnectEvent(
|
||||
container.getProject().getId(),
|
||||
container.getId(),
|
||||
webSession.getSessionId(),
|
||||
WebEventUtils.getSmSessionId(webSession),
|
||||
webSession.getUserId()
|
||||
)
|
||||
);
|
||||
|
||||
+7
-17
@@ -30,6 +30,7 @@ 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;
|
||||
@@ -258,28 +259,12 @@ 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 List<DBWNetworkProfile> getNetworkProfiles() {
|
||||
public DBWNetworkProfileManager 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) {
|
||||
@@ -377,6 +362,11 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
|
||||
return dataSourceRegistry.getPreferenceStore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeDataSources() {
|
||||
dataSourceRegistry.initializeDataSources();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSourceParseResults loadDataSources(
|
||||
@NotNull List<DBPDataSourceConfigurationStorage> storages,
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -39,7 +39,8 @@ 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.DBNDataSource;
|
||||
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.preferences.DBPPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertySource;
|
||||
@@ -213,7 +214,8 @@ public class WebConnectionInfo {
|
||||
|
||||
@Property
|
||||
public String getNodePath() {
|
||||
return DBNDataSource.makeDataSourceItemPath(dataSourceContainer);
|
||||
DBNDatabaseNode dsNode = DBNUtils.getNodeByObject(dataSourceContainer);
|
||||
return dsNode == null ? null : dsNode.getNodeUri();
|
||||
}
|
||||
|
||||
@Property
|
||||
|
||||
-7
@@ -115,13 +115,6 @@ 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 {
|
||||
|
||||
-7
@@ -160,13 +160,6 @@ 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 {
|
||||
|
||||
-8
@@ -115,14 +115,6 @@ 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 {
|
||||
|
||||
+4
-2
@@ -33,6 +33,7 @@ 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;
|
||||
@@ -41,7 +42,6 @@ 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,6 +53,7 @@ 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;
|
||||
@@ -283,7 +284,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
throw new DBException("Error creating shared project path", e);
|
||||
}
|
||||
}
|
||||
validateResourcePath(name);
|
||||
GeneralUtils.validateResourceNameUnconditionally(name);
|
||||
validateProjectName(null, name);
|
||||
var projectPath = sharedProjectsPath.resolve(name);
|
||||
if (Files.exists(projectPath)) {
|
||||
@@ -1106,6 +1107,7 @@ 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)) {
|
||||
|
||||
+1
-1
@@ -209,7 +209,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
}
|
||||
|
||||
private void cleanUpSession(boolean sendSessionExpiredEvent) {
|
||||
application.getEventController().addEvent(new WSEventDeleteTempFile(getSessionId()));
|
||||
application.getEventController().addEvent(new WSEventDeleteTempFile(getUserContext().getSmSessionId()));
|
||||
synchronized (sessionEventHandlers) {
|
||||
var sessionExpiredEvent = new WSSessionExpiredEvent();
|
||||
for (CBWebSessionEventHandler sessionEventHandler : sessionEventHandlers) {
|
||||
|
||||
+1
-1
@@ -313,7 +313,7 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
loadProjects();
|
||||
|
||||
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), getWorkspace().getProjects());
|
||||
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), getWorkspace());
|
||||
this.navigatorModel.setModelAuthContext(getWorkspace().getAuthContext());
|
||||
this.navigatorModel.initialize();
|
||||
|
||||
|
||||
+15
@@ -27,6 +27,7 @@ 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;
|
||||
@@ -46,6 +47,7 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
private final SessionContextImpl workspaceAuthContext;
|
||||
private final List<WebSessionProjectImpl> accessibleProjects = new ArrayList<>();
|
||||
private WebSessionProjectImpl activeProject;
|
||||
private DBFFileSystemManager fileSystemManager;
|
||||
|
||||
public WebSessionWorkspace(BaseWebSession session) {
|
||||
this.session = session;
|
||||
@@ -211,4 +213,17 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
return ServletAppUtils.getServletApplication().getAppConfiguration().isFeatureEnabled(feature);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public synchronized DBFFileSystemManager getFileSystemManager() {
|
||||
if (fileSystemManager == null) {
|
||||
synchronized (this) {
|
||||
if (fileSystemManager == null) {
|
||||
fileSystemManager = new DBFFileSystemManager(session.getWorkspace());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fileSystemManager;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -245,7 +245,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
return secretController;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Nullable
|
||||
public String getSmSessionId() {
|
||||
return smSessionId;
|
||||
}
|
||||
|
||||
@@ -114,4 +114,9 @@ public class WebUser {
|
||||
public String getDisableReason() {
|
||||
return user.getDisableReason();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Instant getLastLoginTime() {
|
||||
return user.getLastLoginTime();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,4 +82,6 @@ public class CBConstants {
|
||||
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";
|
||||
}
|
||||
|
||||
@@ -319,8 +319,7 @@ public class ServletAppUtils {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("forwarded origin: " + origin);
|
||||
}
|
||||
}
|
||||
if (CommonUtils.isEmpty(origin)) {
|
||||
} else if (CommonUtils.isEmpty(origin)) {
|
||||
URI requestUrl = URI.create(request.getRequestURL().toString());
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Request URL: " + requestUrl);
|
||||
@@ -344,13 +343,34 @@ public class ServletAppUtils {
|
||||
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) {
|
||||
try {
|
||||
origin = new URI(
|
||||
uri.getScheme(),
|
||||
finalScheme,
|
||||
uri.getUserInfo(),
|
||||
uri.getHost(),
|
||||
-1,
|
||||
finalHost,
|
||||
finalPort,
|
||||
uri.getPath(),
|
||||
uri.getQuery(),
|
||||
uri.getFragment()
|
||||
@@ -359,9 +379,6 @@ public class ServletAppUtils {
|
||||
log.error("Failed to create URI without port", e);
|
||||
}
|
||||
}
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Origin URI: " + origin);
|
||||
}
|
||||
|
||||
return origin;
|
||||
}
|
||||
|
||||
+1
-1
@@ -175,7 +175,7 @@ public class WebDataSourceUtils {
|
||||
new WSDataSourceDisconnectEvent(
|
||||
dataSource.getProject().getId(),
|
||||
dataSource.getId(),
|
||||
webSession.getSessionId(),
|
||||
WebEventUtils.getSmSessionId(webSession),
|
||||
webSession.getUserId()
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -17,119 +17,21 @@
|
||||
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.List;
|
||||
import java.util.Objects;
|
||||
|
||||
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,
|
||||
@@ -153,7 +55,7 @@ public class WebEventUtils {
|
||||
switch (eventAction) {
|
||||
case CREATE:
|
||||
event = WSResourceUpdatedEvent.create(
|
||||
session.getUserContext().getSmSessionId(),
|
||||
WebEventUtils.getSmSessionId(session),
|
||||
session.getUserId(),
|
||||
projectId,
|
||||
resourcePath,
|
||||
@@ -163,7 +65,7 @@ public class WebEventUtils {
|
||||
break;
|
||||
case DELETE:
|
||||
event = WSResourceUpdatedEvent.delete(
|
||||
session.getUserContext().getSmSessionId(),
|
||||
WebEventUtils.getSmSessionId(session),
|
||||
session.getUserId(),
|
||||
projectId,
|
||||
resourcePath,
|
||||
@@ -173,7 +75,7 @@ public class WebEventUtils {
|
||||
break;
|
||||
case UPDATE:
|
||||
event = WSResourceUpdatedEvent.update(
|
||||
session.getUserContext().getSmSessionId(),
|
||||
WebEventUtils.getSmSessionId(session),
|
||||
session.getUserId(),
|
||||
projectId,
|
||||
resourcePath,
|
||||
@@ -213,4 +115,12 @@ 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);
|
||||
}
|
||||
}
|
||||
@@ -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.0.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Version: 26.1.2.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce</artifactId>
|
||||
<version>26.1.0-SNAPSHOT</version>
|
||||
<version>26.1.2-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.147.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Version: 1.0.149.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
|
||||
<version>1.0.147-SNAPSHOT</version>
|
||||
<version>1.0.149-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: CloudBeaver Community
|
||||
Bundle-SymbolicName: io.cloudbeaver.server.ce;singleton:=true
|
||||
Bundle-Version: 26.1.0.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Version: 26.1.2.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
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.0-SNAPSHOT</version>
|
||||
<version>26.1.2-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+5
-5
@@ -47,14 +47,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.AuthInfo;
|
||||
import org.jkiss.dbeaver.model.auth.SMAuthConfiguration;
|
||||
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;
|
||||
@@ -534,7 +534,7 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
public synchronized void finishConfiguration(
|
||||
@NotNull String adminName,
|
||||
@Nullable String adminPassword,
|
||||
@NotNull List<AuthInfo> authInfoList,
|
||||
@NotNull List<SMAuthConfiguration> authInfoList,
|
||||
@NotNull CBServerConfig serverConfig,
|
||||
@NotNull CBAppConfig appConfig,
|
||||
@Nullable SMCredentialsProvider credentialsProvider
|
||||
@@ -591,7 +591,7 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
protected abstract void finishSecurityServiceConfiguration(
|
||||
@NotNull String adminName,
|
||||
@Nullable String adminPassword,
|
||||
@NotNull List<AuthInfo> authInfoList
|
||||
@NotNull List<SMAuthConfiguration> authInfoList
|
||||
) throws DBException;
|
||||
|
||||
public synchronized void flushConfiguration(SMCredentialsProvider webSession) throws DBException {
|
||||
@@ -743,7 +743,7 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
sendConfigChangedEvent(credentialsProvider);
|
||||
}
|
||||
|
||||
protected void sendConfigChangedEvent(SMCredentialsProvider credentialsProvider) {
|
||||
protected void sendConfigChangedEvent(@Nullable SMCredentialsProvider credentialsProvider) {
|
||||
String sessionId = null;
|
||||
if (credentialsProvider != null && credentialsProvider.getActiveUserCredentials() != null) {
|
||||
sessionId = credentialsProvider.getActiveUserCredentials().getSmSessionId();
|
||||
|
||||
+2
-2
@@ -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.AuthInfo;
|
||||
import org.jkiss.dbeaver.model.auth.SMAuthConfiguration;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.security.SMAdminController;
|
||||
@@ -101,7 +101,7 @@ public class CBApplicationCE extends CBApplication<CBServerConfig> {
|
||||
protected void finishSecurityServiceConfiguration(
|
||||
@NotNull String adminName,
|
||||
@Nullable String adminPassword,
|
||||
@NotNull List<AuthInfo> authInfoList
|
||||
@NotNull List<SMAuthConfiguration> authInfoList
|
||||
) throws DBException {
|
||||
if (securityController instanceof CBEmbeddedSecurityController<?> embeddedSecurityController) {
|
||||
embeddedSecurityController.finishConfiguration(adminName, adminPassword, authInfoList);
|
||||
|
||||
+4
-20
@@ -24,6 +24,7 @@ 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;
|
||||
@@ -33,7 +34,6 @@ 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,10 +43,11 @@ import java.util.stream.Collectors;
|
||||
public class WSObjectPermissionUpdatedEventHandler extends WSDefaultEventHandler<WSObjectPermissionEvent> {
|
||||
private static final Log log = Log.getLog(WSObjectPermissionUpdatedEventHandler.class);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public void handleEvent(@NotNull WSObjectPermissionEvent event) {
|
||||
protected Consumer<BaseWebSession> getEventConsumer(@NotNull WSObjectPermissionEvent event) {
|
||||
String objectId = event.getObjectId();
|
||||
Consumer<BaseWebSession> runnable = switch (event.getSmObjectType()) {
|
||||
return switch (event.getSmObjectType()) {
|
||||
case project:
|
||||
yield getUpdateUserProjectsInfoConsumer(event, objectId);
|
||||
case datasource:
|
||||
@@ -61,23 +62,6 @@ 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
|
||||
|
||||
+3
-3
@@ -156,10 +156,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.getWriter().write(
|
||||
"Request host '" + requestHost + "' is not allowed. Available hosts: " + availableHosts
|
||||
);
|
||||
response.setContentType("text/plain;charset=UTF-8");
|
||||
response.getWriter().write("Request host is not allowed. Available hosts: " + availableHosts);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -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;
|
||||
@@ -60,6 +60,7 @@ 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();
|
||||
@@ -69,11 +70,13 @@ 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;
|
||||
@@ -204,7 +207,7 @@ public class CBSessionManager implements WebAppSessionManager {
|
||||
*
|
||||
* @return WebSession object or null, if session expired or invalid
|
||||
*/
|
||||
@Nullable
|
||||
@NotNull
|
||||
public WebSession getOrRestoreWebSession(@NotNull WebHttpRequestInfo requestInfo) {
|
||||
final var sessionId = requestInfo.getId();
|
||||
if (sessionId == null) {
|
||||
@@ -283,7 +286,7 @@ public class CBSessionManager implements WebAppSessionManager {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public WebSession findWebSession(HttpServletRequest request) {
|
||||
public WebSession findWebSession(@NotNull HttpServletRequest request) {
|
||||
String sessionId = getSessionId(request);
|
||||
synchronized (sessionMap) {
|
||||
var session = sessionMap.get(sessionId);
|
||||
@@ -294,8 +297,9 @@ public class CBSessionManager implements WebAppSessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WebSession findWebSession(HttpServletRequest request, boolean errorOnNoFound) throws DBWebException {
|
||||
public WebSession findWebSession(@NotNull HttpServletRequest request, boolean errorOnNoFound) throws DBWebException {
|
||||
WebSession webSession = findWebSession(request);
|
||||
if (webSession != null) {
|
||||
return webSession;
|
||||
@@ -323,6 +327,7 @@ public class CBSessionManager implements WebAppSessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<BaseWebSession> getAllActiveSessions() {
|
||||
synchronized (sessionMap) {
|
||||
@@ -501,7 +506,7 @@ public class CBSessionManager implements WebAppSessionManager {
|
||||
for (Iterator<BaseWebSession> iterator = sessionMap.values().iterator(); iterator.hasNext(); ) {
|
||||
var session = iterator.next();
|
||||
iterator.remove();
|
||||
session.close(false, !WSWebUtils.isSessionIdEquals(session, initiatorSessionId));
|
||||
session.close(false, !WebEventUtils.isSmSessionIdEquals(session, initiatorSessionId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Server
|
||||
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
|
||||
Bundle-Version: 26.1.0.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Version: 26.1.2.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.model.sql;visibility:=reexport,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server</artifactId>
|
||||
<version>26.1.0-SNAPSHOT</version>
|
||||
<version>26.1.2-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -787,6 +787,9 @@ extend type Query {
|
||||
|
||||
"Reads session log entries"
|
||||
readSessionLog(maxEntries: Int, clearEntries: Boolean): [ LogEntry! ]!
|
||||
|
||||
"Returns connection driver properties based on configuration"
|
||||
listConnectionDriverProperties( projectId: ID!, config: ConnectionConfig! ): [ObjectPropertyInfo!]!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
|
||||
@@ -55,8 +55,6 @@ type DatabaseObjectInfo {
|
||||
}
|
||||
|
||||
type NavigatorNodeInfo {
|
||||
"Node ID - generally a full path to the node from root of tree"
|
||||
id: ID!
|
||||
"Node URI - a unique path to a node including all parent nodes"
|
||||
uri: ID! @since(version: "23.3.1")
|
||||
"Node human readable name"
|
||||
|
||||
@@ -72,6 +72,17 @@ input SQLDataFilter {
|
||||
orderBy: String
|
||||
}
|
||||
|
||||
type SQLResultReference @since(version: "26.1.1") {
|
||||
"Name of the association (foreign key)"
|
||||
associationName: String!
|
||||
"Fully qualified name of the entity"
|
||||
targetEntityName: String
|
||||
"Navigator node URI of the target entity, suitable for opening it directly in the UI. Null if the entity is not present in the navigator tree"
|
||||
nodePath: String
|
||||
"Indices of result-set columns that participate as the source-side attributes of this reference"
|
||||
columnIndexList: [Int!]!
|
||||
}
|
||||
|
||||
type SQLResultColumn {
|
||||
position: Int!
|
||||
name: String
|
||||
@@ -122,6 +133,11 @@ type SQLResultSet {
|
||||
"Returns list of rows in the result set. Each row contains data and metadata"
|
||||
rowsWithMetaData: [SQLResultRowMetaData] @since(version: "23.3.5")
|
||||
|
||||
"Forward foreign keys navigable from this result set"
|
||||
associations: [SQLResultReference!]! @since(version: "26.1.1")
|
||||
"Reverse references (incoming FKs from other entities) navigable from this result set"
|
||||
references: [SQLResultReference!]! @since(version: "26.1.1")
|
||||
|
||||
# True means that resultset was generated by single entity query
|
||||
# New rows can be added, old rows can be deleted
|
||||
singleEntity: Boolean!
|
||||
@@ -451,6 +467,19 @@ extend type Mutation {
|
||||
dataFormat: ResultDataFormat
|
||||
): AsyncTaskInfo!
|
||||
|
||||
"Creates async task for navigating a foreign-key reference exposed in SQLResultSet.references. Pass associationName, isReference and any one of the chosen reference's columnIndices (used to locate the source entity)."
|
||||
asyncSqlNavigateForeignKey(
|
||||
projectId: ID,
|
||||
connectionId: ID!,
|
||||
contextId: ID!,
|
||||
resultsId: ID!,
|
||||
row: SQLResultRow!,
|
||||
columnIndex: Int!,
|
||||
associationName: String!,
|
||||
isReference: Boolean!,
|
||||
dataFormat: ResultDataFormat
|
||||
): AsyncTaskInfo! @since(version: "26.1.1")
|
||||
|
||||
"Returns transaction log info for the specified project, connection and context"
|
||||
getTransactionLogInfo(
|
||||
projectId: ID!,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -38,7 +38,9 @@ import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.impl.PropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNContainer;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNDataSource;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNNode;
|
||||
@@ -47,6 +49,8 @@ import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
import org.jkiss.dbeaver.registry.DataSourceNavigatorSettings;
|
||||
import org.jkiss.dbeaver.runtime.properties.PropertyCollector;
|
||||
import org.jkiss.dbeaver.runtime.properties.PropertySourceCustom;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
@@ -190,4 +194,52 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
|| (node instanceof DBNResourceManagerResource
|
||||
&& ((DBNResourceManagerResource) node).getResource().isFolder());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static WebPropertyInfo[] getDriverProperties(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull DBPDriver driver,
|
||||
@Nullable DBPDataSourceContainer dataSourceContainer,
|
||||
@NotNull DBPConnectionConfiguration cfg
|
||||
) {
|
||||
try {
|
||||
DBPPropertyDescriptor[] properties = driver.getDataSourceProvider().getConnectionProperties(
|
||||
webSession.getProgressMonitor(),
|
||||
driver,
|
||||
dataSourceContainer,
|
||||
cfg
|
||||
);
|
||||
Map<String, Object> connectionProperties = driver.getConnectionProperties();
|
||||
for (Map.Entry<String, Object> connProp : connectionProperties.entrySet()) {
|
||||
String propName = connProp.getKey();
|
||||
Object propValue = connProp.getValue();
|
||||
DBPPropertyDescriptor dbpPropertyDescriptor = new PropertyDescriptor(
|
||||
null,
|
||||
propName,
|
||||
propName,
|
||||
null,
|
||||
false,
|
||||
String.class,
|
||||
propValue,
|
||||
null
|
||||
);
|
||||
properties = ArrayUtils.add(DBPPropertyDescriptor.class, properties, dbpPropertyDescriptor);
|
||||
cfg.setProperty(propName, (String) propValue);
|
||||
}
|
||||
if (properties == null) {
|
||||
return new WebPropertyInfo[0];
|
||||
}
|
||||
|
||||
PropertySourceCustom propertySource = new PropertySourceCustom(
|
||||
properties,
|
||||
cfg.getProperties()
|
||||
);
|
||||
|
||||
return Arrays.stream(properties)
|
||||
.map(p -> new WebPropertyInfo(webSession, p, propertySource)).toArray(WebPropertyInfo[]::new);
|
||||
} catch (DBException e) {
|
||||
log.error("Error reading driver properties:\n" + e.getMessage());
|
||||
return new WebPropertyInfo[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-43
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -19,11 +19,11 @@ package io.cloudbeaver.model;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBConstants;
|
||||
import org.jkiss.dbeaver.model.connection.*;
|
||||
import org.jkiss.dbeaver.model.impl.PropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNative;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
|
||||
@@ -177,48 +177,16 @@ public class WebDatabaseDriverInfo {
|
||||
return driver.getDefaultConnectionProperties();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public WebPropertyInfo[] getDriverProperties() throws DBWebException {
|
||||
try {
|
||||
DBPConnectionConfiguration cfg = new DBPConnectionConfiguration();
|
||||
cfg.setUrl(CommonUtils.notEmpty(driver.getSampleURL()));
|
||||
cfg.setHostName(DBConstants.HOST_LOCALHOST);
|
||||
cfg.setHostPort(driver.getDefaultPort());
|
||||
cfg.setDatabaseName(driver.getDefaultDatabase());
|
||||
cfg.setUrl(driver.getConnectionURL(cfg));
|
||||
DBPPropertyDescriptor[] properties = driver.getDataSourceProvider().getConnectionProperties(webSession.getProgressMonitor(), driver, cfg);
|
||||
Map<String, Object> connectionProperties = driver.getConnectionProperties();
|
||||
for (Map.Entry<String, Object> connProp : connectionProperties.entrySet()) {
|
||||
String propName = connProp.getKey();
|
||||
Object propValue = connProp.getValue();
|
||||
DBPPropertyDescriptor dbpPropertyDescriptor = new PropertyDescriptor(
|
||||
null,
|
||||
propName,
|
||||
propName,
|
||||
null,
|
||||
false,
|
||||
String.class,
|
||||
propValue,
|
||||
null
|
||||
);
|
||||
properties = ArrayUtils.add(DBPPropertyDescriptor.class, properties, dbpPropertyDescriptor);
|
||||
cfg.setProperty(propName, (String) propValue);
|
||||
}
|
||||
if (properties == null) {
|
||||
return new WebPropertyInfo[0];
|
||||
}
|
||||
|
||||
|
||||
PropertySourceCustom propertySource = new PropertySourceCustom(
|
||||
properties,
|
||||
cfg.getProperties());
|
||||
|
||||
return Arrays.stream(properties)
|
||||
.map(p -> new WebPropertyInfo(webSession, p, propertySource)).toArray(WebPropertyInfo[]::new);
|
||||
} catch (DBException e) {
|
||||
log.error("Error reading driver properties:\n" + e.getMessage());
|
||||
return new WebPropertyInfo[0];
|
||||
}
|
||||
DBPConnectionConfiguration cfg = new DBPConnectionConfiguration();
|
||||
cfg.setUrl(CommonUtils.notEmpty(driver.getSampleURL()));
|
||||
cfg.setHostName(CBConstants.HOST_LOCALHOST);
|
||||
cfg.setHostPort(driver.getDefaultPort());
|
||||
cfg.setDatabaseName(driver.getDefaultDatabase());
|
||||
cfg.setUrl(driver.getConnectionURL(cfg));
|
||||
return WebServiceUtils.getDriverProperties(webSession, driver, null, cfg);
|
||||
}
|
||||
|
||||
@Property
|
||||
|
||||
+12
-4
@@ -24,16 +24,20 @@ import io.cloudbeaver.model.session.WebSession;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.NotNullWhen;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface WebAppSessionManager {
|
||||
@Nullable
|
||||
BaseWebSession closeSession(@NotNull HttpServletRequest request);
|
||||
|
||||
@Nullable
|
||||
BaseWebSession closeSession(@NotNull String sessionId);
|
||||
|
||||
@Nullable
|
||||
BaseWebSession closeSession(@NotNull String sessionId, boolean sendSessionExpiredEvent);
|
||||
|
||||
@NotNull
|
||||
@@ -53,21 +57,25 @@ public interface WebAppSessionManager {
|
||||
BaseWebSession getSession(@NotNull String sessionId);
|
||||
|
||||
@Nullable
|
||||
WebSession findWebSession(HttpServletRequest request);
|
||||
WebSession findWebSession(@NotNull HttpServletRequest request);
|
||||
|
||||
WebSession findWebSession(HttpServletRequest request, boolean errorOnNoFound) throws DBWebException;
|
||||
@NotNullWhen("errorOnNoFound")
|
||||
WebSession findWebSession(@NotNull HttpServletRequest request, boolean errorOnNoFound) throws DBWebException;
|
||||
|
||||
@NotNull
|
||||
Collection<BaseWebSession> getAllActiveSessions();
|
||||
|
||||
WebSession getOrRestoreWebSession(WebHttpRequestInfo httpRequest);
|
||||
@NotNull
|
||||
WebSession getOrRestoreWebSession(@NotNull WebHttpRequestInfo httpRequest);
|
||||
|
||||
@NotNullWhen("!create")
|
||||
WebHeadlessSession getHeadlessSession(
|
||||
@Nullable String smAccessToken,
|
||||
@NotNull WebHttpRequestInfo requestInfo,
|
||||
boolean create
|
||||
) throws DBException;
|
||||
|
||||
boolean touchSession(HttpServletRequest request, HttpServletResponse response) throws DBWebException;
|
||||
boolean touchSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws DBWebException;
|
||||
|
||||
default void expireIdleSessions() {
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -22,6 +22,7 @@ import io.cloudbeaver.model.app.WebAppConfiguration;
|
||||
import io.cloudbeaver.model.app.WebServerConfiguration;
|
||||
import io.cloudbeaver.registry.WebDriverRegistry;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
@@ -54,6 +55,7 @@ public interface WebApplication extends ServletApplication {
|
||||
|
||||
boolean isLicenseValid();
|
||||
|
||||
@Nullable
|
||||
String getLicenseStatus();
|
||||
|
||||
@NotNull
|
||||
|
||||
+25
-16
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -18,14 +18,18 @@ package io.cloudbeaver.server.events;
|
||||
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.websocket.WSEventHandler;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WSDefaultEventHandler<EVENT extends WSEvent> implements WSEventHandler<EVENT> {
|
||||
public abstract class WSDefaultEventHandler<EVENT extends WSEvent> implements WSEventHandler<EVENT> {
|
||||
|
||||
private static final Log log = Log.getLog(WSDefaultEventHandler.class);
|
||||
|
||||
@@ -34,19 +38,24 @@ public class WSDefaultEventHandler<EVENT extends WSEvent> implements WSEventHand
|
||||
if (!WebAppUtils.isWebApplication()) {
|
||||
return;
|
||||
}
|
||||
log.debug(event.getTopicId() + " event handled");
|
||||
Collection<BaseWebSession> allSessions = WebAppUtils.getWebApplication()
|
||||
.getSessionManager()
|
||||
.getAllActiveSessions();
|
||||
for (var activeUserSession : allSessions) {
|
||||
if (!isAcceptableInSession(activeUserSession, event)) {
|
||||
log.debug("Cannot handle " + event.getTopicId() + " event '" + event.getId() +
|
||||
"' in session " + activeUserSession.getSessionId());
|
||||
continue;
|
||||
}
|
||||
log.debug(event.getTopicId() + " event '" + event.getId() + "' handled");
|
||||
updateSessionData(activeUserSession, event);
|
||||
Consumer<BaseWebSession> consumer = getEventConsumer(event);
|
||||
if (consumer == null) {
|
||||
return;
|
||||
}
|
||||
Set<String> sessionIds = WebAppUtils.getWebApplication().getSessionManager().getAllActiveSessions().stream()
|
||||
.filter(session -> isAcceptableInSession(session, event))
|
||||
.peek(consumer)
|
||||
.map(session -> session.getUserContext().getSmSessionId())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (!sessionIds.isEmpty()) {
|
||||
log.debug(event.getTopicId() + " event '" + event.getId() + "' handled in sessions: " + String.join(", ", sessionIds));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Consumer<BaseWebSession> getEventConsumer(@NotNull EVENT event) {
|
||||
return (session) -> updateSessionData(session, event);
|
||||
}
|
||||
|
||||
protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull EVENT event) {
|
||||
@@ -54,6 +63,6 @@ public class WSDefaultEventHandler<EVENT extends WSEvent> implements WSEventHand
|
||||
}
|
||||
|
||||
protected boolean isAcceptableInSession(@NotNull BaseWebSession activeUserSession, @NotNull EVENT event) {
|
||||
return !WSWebUtils.isSessionIdEquals(activeUserSession, event.getSessionId()); // skip events from current session
|
||||
return !WebEventUtils.isSmSessionIdEquals(activeUserSession, event.getSessionId()); // skip events from current session
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -18,6 +18,7 @@ package io.cloudbeaver.server.events;
|
||||
|
||||
import io.cloudbeaver.model.WebServerMessage;
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
|
||||
|
||||
@@ -28,6 +29,6 @@ public class WSLogEventHandler extends WSDefaultEventHandler<WSSessionLogUpdated
|
||||
}
|
||||
|
||||
protected boolean isAcceptableInSession(@NotNull BaseWebSession activeUserSession, @NotNull WSSessionLogUpdatedEvent event) {
|
||||
return WSWebUtils.isSessionIdEquals(activeUserSession, event.getSessionId());
|
||||
return WebEventUtils.isSmSessionIdEquals(activeUserSession, event.getSessionId());
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -18,6 +18,7 @@ package io.cloudbeaver.server.events;
|
||||
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
@@ -54,7 +55,7 @@ public class WSProjectUpdatedEventHandler extends WSAbstractProjectEventHandler<
|
||||
|
||||
@Override
|
||||
protected boolean isAcceptableInSession(@NotNull BaseWebSession activeUserSession, @NotNull WSProjectUpdateEvent event) {
|
||||
return !WSWebUtils.isSessionIdEquals(activeUserSession, event.getSessionId()) &&
|
||||
return !WebEventUtils.isSmSessionIdEquals(activeUserSession, event.getSessionId()) &&
|
||||
(!event.getId().equals(WSProjectUpdateEvent.ADDED) ||
|
||||
activeUserSession.getUserContext().hasPermission(DBWConstants.PERMISSION_ADMIN));
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2022 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.events;
|
||||
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class WSWebUtils {
|
||||
public static boolean isSessionIdEquals(@NotNull BaseWebSession webSession, @Nullable String sessionId) {
|
||||
if (sessionId == null) {
|
||||
return false;
|
||||
}
|
||||
if (Objects.equals(webSession.getSessionId(), sessionId)) {
|
||||
return true;
|
||||
}
|
||||
if (webSession instanceof WebSession) {
|
||||
return Objects.equals(webSession.getUserContext().getSmSessionId(), sessionId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+3
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -19,17 +19,15 @@ package io.cloudbeaver.server.jobs;
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.server.WebAppSessionManager;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.DBPDataSource;
|
||||
import org.jkiss.dbeaver.model.app.DBPPlatform;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceDisconnectEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
|
||||
import org.jkiss.dbeaver.runtime.jobs.DataSourceMonitorJob;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Web data source monitor job.
|
||||
@@ -62,7 +60,7 @@ public class WebDataSourceMonitorJob extends DataSourceMonitorJob {
|
||||
new WSDataSourceDisconnectEvent(
|
||||
project.getId(),
|
||||
dataSource.getContainer().getId(),
|
||||
webSession.getSessionId(),
|
||||
WebEventUtils.getSmSessionId(webSession),
|
||||
webSession.getUserId()
|
||||
)
|
||||
);
|
||||
|
||||
+12
-5
@@ -77,6 +77,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
/**
|
||||
* Creates proxy for permission checks and other general API calls validation/logging.
|
||||
*/
|
||||
@NotNull
|
||||
protected API_TYPE getService(DataFetchingEnvironment env) {
|
||||
Object proxyImpl = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{apiInterface}, new ServiceInvocationHandler(serviceImpl, env));
|
||||
return apiInterface.cast(proxyImpl);
|
||||
@@ -99,20 +100,23 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
}
|
||||
}
|
||||
|
||||
protected static HttpServletResponse getServletResponse(DataFetchingEnvironment env) {
|
||||
@NotNull
|
||||
protected static HttpServletResponse getServletResponse(@NotNull DataFetchingEnvironment env) {
|
||||
return GraphQLEndpoint.getServletResponse(env);
|
||||
}
|
||||
|
||||
protected static DBWBindingContext getBindingContext(DataFetchingEnvironment env) {
|
||||
@NotNull
|
||||
protected static DBWBindingContext getBindingContext(@NotNull DataFetchingEnvironment env) {
|
||||
return GraphQLEndpoint.getBindingContext(env);
|
||||
}
|
||||
|
||||
protected static WebSession getWebSession(DataFetchingEnvironment env) throws DBWebException {
|
||||
@NotNull
|
||||
protected static WebSession getWebSession(@NotNull DataFetchingEnvironment env) throws DBWebException {
|
||||
return getWebSession(env, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected static WebSession getSessionFromContext(DataFetchingEnvironment env) {
|
||||
protected static WebSession getSessionFromContext(@NotNull DataFetchingEnvironment env) {
|
||||
return env.getGraphQlContext().get(WebSession.class.getName());
|
||||
}
|
||||
|
||||
@@ -125,6 +129,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
return webSession;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected static WebSession getWebSession(@NotNull DataFetchingEnvironment env, boolean errorOnNotFound) throws DBWebException {
|
||||
if (env.getGraphQlContext().getBoolean(CloudbeaverCliConstants.CLI_MODE)) {
|
||||
return getSessionFromContextOrThrow(env);
|
||||
@@ -133,6 +138,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
GraphQLEndpoint.getServletRequestOrThrow(env), getServletResponse(env), errorOnNotFound);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected static String getProjectReference(@NotNull DataFetchingEnvironment env) {
|
||||
return env.getArgument("projectId");
|
||||
}
|
||||
@@ -154,6 +160,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
GraphQLEndpoint.getServletRequestOrThrow(env));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static WebSession findWebSession(@NotNull DataFetchingEnvironment env, boolean errorOnNotFound) throws DBWebException {
|
||||
return WebAppUtils.getWebApplication().getSessionManager().findWebSession(
|
||||
GraphQLEndpoint.getServletRequestOrThrow(env), errorOnNotFound);
|
||||
@@ -261,7 +268,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
throw new DBException("Web session not instantiated");
|
||||
}
|
||||
|
||||
String projectId = args[objectIdArgumentIndex] == null ? null : String.valueOf(args[objectIdArgumentIndex]);
|
||||
String projectId = args[objectIdArgumentIndex] == null ? "" : String.valueOf(args[objectIdArgumentIndex]);
|
||||
// we should always get the project from the session, even if projectId is null - the active project
|
||||
// will be returned
|
||||
WebProjectImpl project = webSession.getProjectById(projectId);
|
||||
|
||||
+6
@@ -108,6 +108,12 @@ public interface DBWServiceCore extends DBWService {
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Connections
|
||||
@NotNull
|
||||
WebPropertyInfo[] getDriverProperties(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String projectId,
|
||||
@NotNull Map<String, Object> connectionConfig
|
||||
) throws DBWebException;
|
||||
|
||||
@WebAction
|
||||
WebConnectionInfo getConnectionState(WebSession webSession, @Nullable String projectId, String connectionId) throws DBWebException;
|
||||
|
||||
+8
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -81,6 +81,13 @@ public class WebServiceBindingCore extends WebServiceBindingBase<DBWServiceCore>
|
||||
getArgument(env, "maxEntries"),
|
||||
getArgument(env, "clearEntries"));
|
||||
})
|
||||
.dataFetcher(
|
||||
"listConnectionDriverProperties", env -> getService(env).getDriverProperties(
|
||||
getWebSession(env),
|
||||
getArgumentVal(env, "projectId"),
|
||||
getArgumentVal(env, "config")
|
||||
)
|
||||
)
|
||||
;
|
||||
|
||||
model.getMutationType()
|
||||
|
||||
+61
-26
@@ -43,6 +43,7 @@ import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
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.auth.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.exec.DBCConnectException;
|
||||
@@ -58,7 +59,6 @@ import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretValue;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.registry.DataSourceNavigatorSettings;
|
||||
import org.jkiss.dbeaver.registry.DataSourceNavigatorSettingsUtils;
|
||||
@@ -296,6 +296,31 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
return WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, connectionId);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WebPropertyInfo[] getDriverProperties(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String projectId,
|
||||
@NotNull Map<String, Object> connectionConfig
|
||||
) throws DBWebException {
|
||||
WebSessionProjectImpl project = getProjectById(webSession, projectId);
|
||||
WebConnectionConfig configInput = project.getConnectionConfigInput(connectionConfig);
|
||||
|
||||
configInput.setSaveCredentials(true); // It is used in createConnectionFromConfig
|
||||
|
||||
DataSourceDescriptor dataSource = (DataSourceDescriptor) WebDataSourceUtils.getLocalOrGlobalDataSource(
|
||||
webSession, projectId, configInput.getConnectionId());
|
||||
DataSourceDescriptor testDataSource = getDataSourceDescriptor(webSession, dataSource, configInput, project);
|
||||
DBPConnectionConfiguration connectionConfiguration = new DBPConnectionConfiguration(testDataSource.getConnectionConfiguration());
|
||||
return WebServiceUtils.getDriverProperties(
|
||||
webSession,
|
||||
testDataSource.getDriver(),
|
||||
testDataSource,
|
||||
connectionConfiguration
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public WebConnectionInfo initConnection(
|
||||
@@ -515,7 +540,41 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
|
||||
DataSourceDescriptor dataSource = (DataSourceDescriptor) WebDataSourceUtils.getLocalOrGlobalDataSource(
|
||||
webSession, projectId, configInput.getConnectionId());
|
||||
DataSourceDescriptor testDataSource = getDataSourceDescriptor(webSession, dataSource, configInput, project);
|
||||
try {
|
||||
ConnectionTestJob ct = new ConnectionTestJob(
|
||||
testDataSource, param -> {
|
||||
}
|
||||
);
|
||||
ct.run(webSession.getProgressMonitor());
|
||||
if (ct.getConnectError() != null) {
|
||||
if (ct.getConnectError() instanceof DBCConnectException error) {
|
||||
Throwable rootCause = CommonUtils.getRootCause(error);
|
||||
if (rootCause instanceof ClassNotFoundException) {
|
||||
log.error(error);
|
||||
throwDriverNotFoundException(testDataSource);
|
||||
}
|
||||
}
|
||||
throw new DBWebException("Connection failed", ct.getConnectError());
|
||||
}
|
||||
WebConnectionInfo connectionInfo = project.createConnectionInfo(testDataSource);
|
||||
connectionInfo.setConnectError(ct.getConnectError());
|
||||
connectionInfo.setServerVersion(ct.getServerVersion());
|
||||
connectionInfo.setClientVersion(ct.getClientVersion());
|
||||
connectionInfo.setConnectTime(RuntimeUtils.formatExecutionTime(ct.getConnectTime()));
|
||||
return connectionInfo;
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error connecting to database", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private DataSourceDescriptor getDataSourceDescriptor(
|
||||
@NotNull WebSession webSession,
|
||||
@Nullable DataSourceDescriptor dataSource,
|
||||
@NotNull WebConnectionConfig configInput,
|
||||
@NotNull WebSessionProjectImpl project
|
||||
) throws DBWebException {
|
||||
DataSourceDescriptor testDataSource;
|
||||
if (dataSource != null) {
|
||||
try {
|
||||
@@ -563,31 +622,7 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
);
|
||||
testDataSource.setSavePassword(true); // We need for test to avoid password callback
|
||||
testDataSource.setAccessCheckRequired(!webSession.hasPermission(DBWConstants.PERMISSION_ADMIN));
|
||||
try {
|
||||
ConnectionTestJob ct = new ConnectionTestJob(
|
||||
testDataSource, param -> {
|
||||
}
|
||||
);
|
||||
ct.run(webSession.getProgressMonitor());
|
||||
if (ct.getConnectError() != null) {
|
||||
if (ct.getConnectError() instanceof DBCConnectException error) {
|
||||
Throwable rootCause = CommonUtils.getRootCause(error);
|
||||
if (rootCause instanceof ClassNotFoundException) {
|
||||
log.error(error);
|
||||
throwDriverNotFoundException(testDataSource);
|
||||
}
|
||||
}
|
||||
throw new DBWebException("Connection failed", ct.getConnectError());
|
||||
}
|
||||
WebConnectionInfo connectionInfo = project.createConnectionInfo(testDataSource);
|
||||
connectionInfo.setConnectError(ct.getConnectError());
|
||||
connectionInfo.setServerVersion(ct.getServerVersion());
|
||||
connectionInfo.setClientVersion(ct.getClientVersion());
|
||||
connectionInfo.setConnectTime(RuntimeUtils.formatExecutionTime(ct.getConnectTime()));
|
||||
return connectionInfo;
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error connecting to database", e);
|
||||
}
|
||||
return testDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+8
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -97,7 +97,8 @@ public class WebDefaultFeatureProvider implements DBWFeatureProvider {
|
||||
} else {
|
||||
features.add(NODE_FEATURE_CAN_FILTER);
|
||||
}
|
||||
isShared = !node.getOwnerProject().getName().equals(webSession.getUserId());
|
||||
DBPProject nodeProject = node.getOwnerProjectOrNull();
|
||||
isShared = nodeProject != null && !nodeProject.getName().equals(webSession.getUserId());
|
||||
} else if (node instanceof DBNLocalFolder dbnLocalFolder) {
|
||||
DBPDataSourceFolder folder = dbnLocalFolder.getFolder();
|
||||
DBPProject project = folder.getDataSourceRegistry().getProject();
|
||||
@@ -168,7 +169,11 @@ public class WebDefaultFeatureProvider implements DBWFeatureProvider {
|
||||
if (node instanceof DBNResourceManagerResource rmr) {
|
||||
rmProject = rmr.getRmProject();
|
||||
} else {
|
||||
WebProjectImpl project = webSession.getProjectById(node.getOwnerProject().getId());
|
||||
DBPProject nodeProject = node.getOwnerProjectOrNull();
|
||||
if (nodeProject == null) {
|
||||
return false;
|
||||
}
|
||||
WebProjectImpl project = webSession.getProjectById(nodeProject.getId());
|
||||
if (project == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+3
-7
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -67,12 +67,6 @@ public class WebNavigatorNodeInfo {
|
||||
// General properties
|
||||
///////////////////////////////////
|
||||
|
||||
@Property
|
||||
@Deprecated(forRemoval = true)
|
||||
public String getId() {
|
||||
return node.getNodeItemPath();
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getUri() {
|
||||
return node.getNodeUri();
|
||||
@@ -209,6 +203,8 @@ public class WebNavigatorNodeInfo {
|
||||
return DBFUtils.getUriFromPath(dbnPath.getPath()).toString();
|
||||
} else if (node instanceof DBNFileSystem dbnFs) {
|
||||
return WebFSUtils.makeUniqueFsId(dbnFs.getFileSystem());
|
||||
} else if (node instanceof DBNDatabaseNode dbNode) {
|
||||
return dbNode.getDataSourceContainer().getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+4
-8
@@ -173,18 +173,14 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
throw new DBWebException("Node '" + nodePath + "' not found");
|
||||
}
|
||||
|
||||
boolean shouldSkipProjectNode = nodePath.startsWith(DBNNode.NodePathType.ext.getPrefix());
|
||||
List<WebNavigatorNodeInfo> nodeParents = new ArrayList<>();
|
||||
for (DBNNode parent = getLogicalParentNode(node);
|
||||
parent != null && !(parent instanceof DBNRoot);
|
||||
parent = getLogicalParentNode(parent)) {
|
||||
//FIXME remove after node path refactoring
|
||||
parent = getLogicalParentNode(parent)
|
||||
) {
|
||||
if (parent instanceof DBNProjectDatabases) {
|
||||
continue;
|
||||
}
|
||||
if (parent instanceof DBNProject && shouldSkipProjectNode) {
|
||||
continue;
|
||||
}
|
||||
nodeParents.add(new WebNavigatorNodeInfo(session, parent));
|
||||
}
|
||||
|
||||
@@ -462,7 +458,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
} else {
|
||||
node.rename(session.getProgressMonitor(), newName);
|
||||
}
|
||||
return node.getNodeItemPath();
|
||||
return node.getNodeUri();
|
||||
}
|
||||
if (node instanceof DBNDatabaseNode dbNode) {
|
||||
return renameDatabaseObject(
|
||||
@@ -723,7 +719,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
commandContext.resetChanges(true);
|
||||
throw e;
|
||||
}
|
||||
return node.getNodeItemPath();
|
||||
return node.getNodeUri();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -126,6 +126,17 @@ public interface DBWServiceSQL extends DBWService {
|
||||
@Nullable WebSQLDataFilter filter,
|
||||
@Nullable WebDataFormat dataFormat) throws DBWebException;
|
||||
|
||||
@WebAction
|
||||
WebAsyncTaskInfo asyncNavigateForeignKey(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull WebSQLContextInfo contextInfo,
|
||||
@NotNull String resultsId,
|
||||
@NotNull WebSQLResultsRow row,
|
||||
int columnIndex,
|
||||
@NotNull String associationName,
|
||||
boolean isReference,
|
||||
@Nullable WebDataFormat dataFormat) throws DBException;
|
||||
|
||||
/**
|
||||
* Reads dynamic trace from provided database results.
|
||||
*/
|
||||
|
||||
+6
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -25,6 +25,7 @@ import io.cloudbeaver.model.WebTransactionLogItemInfo;
|
||||
import io.cloudbeaver.model.session.WebAsyncTaskProcessor;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.session.WebSessionProvider;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
@@ -299,6 +300,7 @@ public class WebSQLContextInfo implements WebSessionProvider {
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public WebAsyncTaskInfo commitTransaction() {
|
||||
DBCExecutionContext context = processor.getExecutionContext();
|
||||
DBCTransactionManager txnManager = DBUtils.getTransactionManager(context);
|
||||
@@ -323,7 +325,7 @@ public class WebSQLContextInfo implements WebSessionProvider {
|
||||
}
|
||||
processor.getWebSession().addSessionEvent(
|
||||
new WSTransactionalCountEvent(
|
||||
processor.getWebSession().getSessionId(),
|
||||
WebEventUtils.getSmSessionId(processor.getWebSession()),
|
||||
processor.getWebSession().getUserId(),
|
||||
getProjectId(),
|
||||
getId(),
|
||||
@@ -338,6 +340,7 @@ public class WebSQLContextInfo implements WebSessionProvider {
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public WebAsyncTaskInfo rollbackTransaction() {
|
||||
DBCExecutionContext context = processor.getExecutionContext();
|
||||
DBCTransactionManager txnManager = DBUtils.getTransactionManager(context);
|
||||
@@ -361,7 +364,7 @@ public class WebSQLContextInfo implements WebSessionProvider {
|
||||
);
|
||||
processor.getWebSession().addSessionEvent(
|
||||
new WSTransactionalCountEvent(
|
||||
processor.getWebSession().getSessionId(),
|
||||
WebEventUtils.getSmSessionId(processor.getWebSession()),
|
||||
processor.getWebSession().getUserId(),
|
||||
getProjectId(),
|
||||
getId(),
|
||||
|
||||
+6
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -25,6 +25,7 @@ import io.cloudbeaver.model.session.WebSessionProvider;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.server.jobs.SqlOutputLogReaderJob;
|
||||
import io.cloudbeaver.service.sql.messages.WebSQLMessages;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
import io.cloudbeaver.websocket.event.task.WSSessionTaskConfirmationRequestEvent;
|
||||
import io.cloudbeaver.websocket.event.task.WSSessionTaskQueryConfirmationRequestEvent;
|
||||
import org.eclipse.jface.text.Document;
|
||||
@@ -478,7 +479,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
sendTransactionalEvent(contextInfo);
|
||||
}
|
||||
|
||||
WebSQLQueryResultSet updatedResultSet = new WebSQLQueryResultSet();
|
||||
WebSQLQueryResultSet updatedResultSet = new WebSQLQueryResultSet(webSession);
|
||||
updatedResultSet.setResultsInfo(resultsInfo);
|
||||
updatedResultSet.setColumns(resultsInfo.getAttributes());
|
||||
|
||||
@@ -494,11 +495,11 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void sendTransactionalEvent(WebSQLContextInfo contextInfo) {
|
||||
private void sendTransactionalEvent(@NotNull WebSQLContextInfo contextInfo) {
|
||||
int count = QMUtils.getTransactionState(getExecutionContext()).getUpdateCount();
|
||||
webSession.addSessionEvent(
|
||||
new WSTransactionalCountEvent(
|
||||
contextInfo.getWebSession().getSessionId(),
|
||||
WebEventUtils.getSmSessionId(webSession),
|
||||
contextInfo.getWebSession().getUserId(),
|
||||
contextInfo.getProjectId(),
|
||||
contextInfo.getId(),
|
||||
@@ -650,7 +651,7 @@ public class WebSQLProcessor implements WebSessionProvider {
|
||||
DBDAttributeBinding[] allAttributes = resultsInfo.getAttributes();
|
||||
DBDAttributeBinding[] keyAttributes = rowIdentifier.getAttributes().toArray(new DBDAttributeBinding[0]);
|
||||
|
||||
WebSQLQueryResultSet updatedResultSet = new WebSQLQueryResultSet();
|
||||
WebSQLQueryResultSet updatedResultSet = new WebSQLQueryResultSet(webSession);
|
||||
updatedResultSet.setResultsInfo(resultsInfo);
|
||||
updatedResultSet.setColumns(resultsInfo.getAttributes());
|
||||
|
||||
|
||||
+2
-1
@@ -54,7 +54,7 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
|
||||
private final WebDataFormat dataFormat;
|
||||
@Nullable
|
||||
private final DBDDataFilter dataFilter;
|
||||
private final WebSQLQueryResultSet webResultSet = new WebSQLQueryResultSet();
|
||||
private final WebSQLQueryResultSet webResultSet;
|
||||
|
||||
private DBDAttributeBinding[] bindings;
|
||||
private DBCTrace trace;
|
||||
@@ -71,6 +71,7 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
|
||||
this.dataContainer = dataContainer;
|
||||
this.dataFormat = dataFormat;
|
||||
this.dataFilter = dataFilter;
|
||||
this.webResultSet = new WebSQLQueryResultSet(contextInfo.getProcessor().getWebSession());
|
||||
rowLimit = ServletAppUtils.getServletApplication()
|
||||
.getAppConfiguration()
|
||||
.getResourceQuota(WebSQLConstants.QUOTA_PROP_ROW_LIMIT);
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.service.sql;
|
||||
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPEvaluationContext;
|
||||
import org.jkiss.dbeaver.model.DBUtils;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNNode;
|
||||
import org.jkiss.dbeaver.model.struct.DBSEntity;
|
||||
import org.jkiss.dbeaver.model.struct.DBSEntityAssociation;
|
||||
import org.jkiss.dbeaver.model.struct.DBSEntityConstraint;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WebSQLQueryResultReference {
|
||||
|
||||
private static final Log log = Log.getLog(WebSQLQueryResultReference.class);
|
||||
|
||||
@Nullable
|
||||
private final WebSession session;
|
||||
@NotNull
|
||||
private final DBSEntityAssociation association;
|
||||
private final boolean reverse;
|
||||
@NotNull
|
||||
private final List<Integer> columnIndexList;
|
||||
|
||||
public WebSQLQueryResultReference(
|
||||
@Nullable WebSession session,
|
||||
@NotNull DBSEntityAssociation association,
|
||||
boolean reverse,
|
||||
@NotNull List<Integer> columnIndexList
|
||||
) {
|
||||
this.session = session;
|
||||
this.association = association;
|
||||
this.reverse = reverse;
|
||||
this.columnIndexList = columnIndexList;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public String getAssociationName() {
|
||||
return association.getName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Property
|
||||
public String getTargetEntityName() {
|
||||
DBSEntity targetEntity = getTargetEntity();
|
||||
if (targetEntity == null) {
|
||||
return null;
|
||||
}
|
||||
return DBUtils.getObjectFullName(targetEntity, DBPEvaluationContext.UI);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Property
|
||||
public String getNodePath() {
|
||||
if (session == null) {
|
||||
return null;
|
||||
}
|
||||
DBSEntity targetEntity = getTargetEntity();
|
||||
if (targetEntity == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
DBNNode node = session.getNavigatorModelOrThrow()
|
||||
.getNodeByObject(session.getProgressMonitor(), targetEntity, false);
|
||||
return node == null ? null : node.getNodeUri();
|
||||
} catch (DBException e) {
|
||||
log.debug("Error resolving navigator node for entity " + targetEntity.getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public List<Integer> getColumnIndexList() {
|
||||
return columnIndexList;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DBSEntity getTargetEntity() {
|
||||
if (reverse) {
|
||||
return association.getParentObject();
|
||||
}
|
||||
DBSEntityConstraint referencedConstraint = association.getReferencedConstraint();
|
||||
if (referencedConstraint == null) {
|
||||
return null;
|
||||
}
|
||||
return referencedConstraint.getParentObject();
|
||||
}
|
||||
}
|
||||
+26
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.sql;
|
||||
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.service.sql.WebSQLResultSetRowIdentifier.WebSQLResultSetRowIdentifierState;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
@@ -35,6 +36,10 @@ public class WebSQLQueryResultSet {
|
||||
|
||||
private static final Log log = Log.getLog(WebSQLQueryResultSet.class);
|
||||
|
||||
@Nullable
|
||||
private final WebSession session;
|
||||
@Nullable
|
||||
private DBDAttributeBinding[] referencesBindings;
|
||||
private WebSQLQueryResultColumn[] columns;
|
||||
private List<WebSQLQueryResultSetRow> rows = Collections.emptyList();
|
||||
private boolean hasMoreData;
|
||||
@@ -50,7 +55,8 @@ public class WebSQLQueryResultSet {
|
||||
private boolean readOnly;
|
||||
private String readOnlyStatus;
|
||||
|
||||
public WebSQLQueryResultSet() {
|
||||
public WebSQLQueryResultSet(@Nullable WebSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -67,12 +73,29 @@ public class WebSQLQueryResultSet {
|
||||
this.columns = columns;
|
||||
}
|
||||
|
||||
public void setColumns(DBDAttributeBinding[] bindings) {
|
||||
public void setColumns(@NotNull DBDAttributeBinding[] bindings) {
|
||||
WebSQLQueryResultColumn[] columns = new WebSQLQueryResultColumn[bindings.length];
|
||||
for (int i = 0; i < bindings.length; i++) {
|
||||
columns[i] = new WebSQLQueryResultColumn(bindings[i]);
|
||||
}
|
||||
this.columns = columns;
|
||||
this.referencesBindings = bindings;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public List<WebSQLQueryResultReference> getAssociations() {
|
||||
return session != null && referencesBindings != null
|
||||
? WebSQLUtils.collectAssociations(session, referencesBindings)
|
||||
: Collections.emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public List<WebSQLQueryResultReference> getReferences() {
|
||||
return session != null && referencesBindings != null
|
||||
? WebSQLUtils.collectReferences(session, referencesBindings)
|
||||
: Collections.emptyList();
|
||||
}
|
||||
|
||||
@Property
|
||||
|
||||
+99
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -26,6 +26,7 @@ import io.cloudbeaver.utils.CBModelConstants;
|
||||
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.DBPEvaluationContext;
|
||||
import org.jkiss.dbeaver.model.data.*;
|
||||
@@ -35,8 +36,7 @@ import org.jkiss.dbeaver.model.gis.DBGeometry;
|
||||
import org.jkiss.dbeaver.model.gis.GisConstants;
|
||||
import org.jkiss.dbeaver.model.gis.GisTransformUtils;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.struct.DBSAttributeBase;
|
||||
import org.jkiss.dbeaver.model.struct.DBSTypedObject;
|
||||
import org.jkiss.dbeaver.model.struct.*;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
|
||||
import org.jkiss.dbeaver.utils.ContentUtils;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
@@ -50,6 +50,7 @@ import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Web SQL utils.
|
||||
@@ -116,8 +117,7 @@ public class WebSQLUtils {
|
||||
Map<String, Object> map = createMapOfType(WebSQLConstants.VALUE_TYPE_COLLECTION);
|
||||
map.put("value", items);
|
||||
return map;
|
||||
} else if (value instanceof DBDComposite) {
|
||||
DBDComposite composite = (DBDComposite)value;
|
||||
} else if (value instanceof DBDComposite composite && composite.getAttributes().length > 0) {
|
||||
Map<String, Object> struct = new LinkedHashMap<>();
|
||||
for (DBSAttributeBase attr : composite.getAttributes()) {
|
||||
struct.put(attr.getName(), makeWebCellValue(session, attr, composite.getAttributeValue(attr), dataFormat));
|
||||
@@ -329,4 +329,98 @@ public class WebSQLUtils {
|
||||
webSession.removeAttribute(attributeName);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<WebSQLQueryResultReference> collectAssociations(
|
||||
@NotNull WebSession session,
|
||||
@NotNull DBDAttributeBinding[] bindings
|
||||
) {
|
||||
return collectWebSQLQueryResultReference(session, bindings, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<WebSQLQueryResultReference> collectReferences(
|
||||
@NotNull WebSession session,
|
||||
@NotNull DBDAttributeBinding[] bindings
|
||||
) {
|
||||
return collectWebSQLQueryResultReference(session, bindings, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<WebSQLQueryResultReference> collectWebSQLQueryResultReference(
|
||||
@NotNull WebSession session,
|
||||
@NotNull DBDAttributeBinding[] bindings,
|
||||
boolean reverse
|
||||
) {
|
||||
Map<DBSEntityAttribute, Integer> attrToIndex = new HashMap<>();
|
||||
Set<DBSEntity> entities = new LinkedHashSet<>();
|
||||
for (int i = 0; i < bindings.length; i++) {
|
||||
DBSEntityAttribute ea = bindings[i].getEntityAttribute();
|
||||
if (ea == null) {
|
||||
continue;
|
||||
}
|
||||
attrToIndex.putIfAbsent(ea, i);
|
||||
DBSEntity parent = ea.getParentObject();
|
||||
entities.add(parent);
|
||||
}
|
||||
|
||||
Function<DBSEntityAttribute, DBDAttributeBinding> attrToBinding = attr -> {
|
||||
Integer idx = attrToIndex.get(attr);
|
||||
return idx == null ? null : bindings[idx];
|
||||
};
|
||||
|
||||
List<WebSQLQueryResultReference> result = new ArrayList<>();
|
||||
DBRProgressMonitor monitor = session.getProgressMonitor();
|
||||
for (DBSEntity entity : entities) {
|
||||
try {
|
||||
List<DBSEntityAssociation> source = reverse
|
||||
? DBStructUtils.readReferences(monitor, entity, attrToBinding)
|
||||
: DBStructUtils.readAssociations(monitor, entity, attrToBinding);
|
||||
for (DBSEntityAssociation association : source) {
|
||||
List<Integer> columnIndex = collectOwnColumnIndex(monitor, association, reverse, attrToIndex);
|
||||
if (columnIndex != null) {
|
||||
result.add(new WebSQLQueryResultReference(session, association, reverse, columnIndex));
|
||||
}
|
||||
}
|
||||
} catch (DBException e) {
|
||||
log.debug("Error collecting references for entity " + entity.getName(), e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static List<Integer> collectOwnColumnIndex(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull DBSEntityAssociation association,
|
||||
boolean reverse,
|
||||
@NotNull Map<DBSEntityAttribute, Integer> attrToIndex
|
||||
) throws DBException {
|
||||
DBSEntityReferrer ownSide;
|
||||
if (reverse) {
|
||||
DBSEntityConstraint refConstraint = association.getReferencedConstraint();
|
||||
if (!(refConstraint instanceof DBSEntityReferrer referrer)) {
|
||||
return null;
|
||||
}
|
||||
ownSide = referrer;
|
||||
} else {
|
||||
if (!(association instanceof DBSEntityReferrer associationRef)) {
|
||||
return null;
|
||||
}
|
||||
ownSide = associationRef;
|
||||
}
|
||||
List<? extends DBSEntityAttributeRef> attrs = ownSide.getAttributeReferences(monitor);
|
||||
if (attrs == null || attrs.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<Integer> indexList = new ArrayList<>(attrs.size());
|
||||
for (DBSEntityAttributeRef attrRef : attrs) {
|
||||
Integer idx = attrToIndex.get(attrRef.getAttribute());
|
||||
if (idx == null) {
|
||||
return null;
|
||||
}
|
||||
indexList.add(idx);
|
||||
}
|
||||
return indexList;
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -232,6 +232,17 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL>
|
||||
getDataFilter(env),
|
||||
getDataFormat(env)
|
||||
))
|
||||
.dataFetcher("asyncSqlNavigateForeignKey", env ->
|
||||
getService(env).asyncNavigateForeignKey(
|
||||
getWebSession(env),
|
||||
getSQLContext(env),
|
||||
getArgumentVal(env, "resultsId"),
|
||||
new WebSQLResultsRow(getArgument(env, "row")),
|
||||
getArgumentVal(env, "columnIndex"),
|
||||
getArgumentVal(env, "associationName"),
|
||||
CommonUtils.toBoolean(env.getArgument("isReference")),
|
||||
getDataFormat(env)
|
||||
))
|
||||
.dataFetcher("asyncSqlExecuteResults", env ->
|
||||
getService(env).asyncGetQueryResults(
|
||||
getWebSession(env), getArgumentVal(env, "taskId")
|
||||
|
||||
+71
-3
@@ -36,6 +36,8 @@ import org.jkiss.dbeaver.model.DBPDataSource;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBUtils;
|
||||
import org.jkiss.dbeaver.model.data.DBDAttributeBinding;
|
||||
import org.jkiss.dbeaver.model.data.DBDReferenceNavigation;
|
||||
import org.jkiss.dbeaver.model.data.DBDReferenceUtils;
|
||||
import org.jkiss.dbeaver.model.exec.DBCException;
|
||||
import org.jkiss.dbeaver.model.exec.DBCLogicalOperator;
|
||||
import org.jkiss.dbeaver.model.exec.DBExecUtils;
|
||||
@@ -59,9 +61,7 @@ import org.jkiss.dbeaver.model.sql.registry.SQLGeneratorDescriptor;
|
||||
import org.jkiss.dbeaver.model.sql.semantics.completion.SQLCompletionProposalComparator;
|
||||
import org.jkiss.dbeaver.model.sql.semantics.completion.SQLQueryCompletionAnalyzer;
|
||||
import org.jkiss.dbeaver.model.sql.semantics.completion.SQLQueryCompletionContext;
|
||||
import org.jkiss.dbeaver.model.struct.DBSDataContainer;
|
||||
import org.jkiss.dbeaver.model.struct.DBSObject;
|
||||
import org.jkiss.dbeaver.model.struct.DBSWrapper;
|
||||
import org.jkiss.dbeaver.model.struct.*;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.dbeaver.utils.RuntimeUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
@@ -603,6 +603,74 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
return contextInfo.getProcessor().getWebSession().createAndRunAsyncTask("Read data from container " + nodePath, runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebAsyncTaskInfo asyncNavigateForeignKey(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull WebSQLContextInfo contextInfo,
|
||||
@NotNull String resultsId,
|
||||
@NotNull WebSQLResultsRow row,
|
||||
int columnIndex,
|
||||
@NotNull String associationName,
|
||||
boolean isReference,
|
||||
@Nullable WebDataFormat dataFormat
|
||||
) {
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
|
||||
@Override
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
|
||||
try {
|
||||
monitor.beginTask("Navigate foreign key", 1);
|
||||
WebSQLResultsInfo resultsInfo = contextInfo.getResults(resultsId);
|
||||
DBSEntity sourceEntity = resolveSourceEntity(resultsInfo.getAttributes(), columnIndex);
|
||||
DBSEntityAssociation association = isReference
|
||||
? DBStructUtils.findReverseAssociationByName(monitor, sourceEntity, associationName)
|
||||
: DBStructUtils.findForwardAssociationByName(monitor, sourceEntity, associationName);
|
||||
|
||||
WebDBDResultSetDataProvider dataProvider = new WebDBDResultSetDataProvider(
|
||||
resultsId,
|
||||
contextInfo,
|
||||
List.of(row)
|
||||
);
|
||||
DBDReferenceNavigation navigation = isReference
|
||||
? DBDReferenceUtils.resolveReferenceNavigation(monitor, dataProvider, association, dataProvider.getSelectedRows())
|
||||
: DBDReferenceUtils.resolveAssociationNavigation(monitor, dataProvider, association, dataProvider.getSelectedRows());
|
||||
if (!(navigation.getTargetEntity() instanceof DBSDataContainer targetDataContainer)) {
|
||||
throw new DBWebException("Referenced entity '" + navigation.getTargetEntity().getName() + "' is not a data container");
|
||||
}
|
||||
WebSQLExecuteInfo executeResults = contextInfo.getProcessor().readDataFromContainer(
|
||||
contextInfo,
|
||||
monitor,
|
||||
targetDataContainer,
|
||||
null,
|
||||
WebSQLDataFilter.from(navigation.getTargetFilter()),
|
||||
dataFormat
|
||||
);
|
||||
this.result = executeResults.getStatusMessage();
|
||||
this.extendedResults = executeResults;
|
||||
} catch (Throwable e) {
|
||||
throw new InvocationTargetException(e);
|
||||
} finally {
|
||||
monitor.done();
|
||||
}
|
||||
}
|
||||
};
|
||||
return webSession.createAndRunAsyncTask("Navigate foreign key from results " + resultsId, runnable);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private DBSEntity resolveSourceEntity(
|
||||
@NotNull DBDAttributeBinding[] attributes,
|
||||
int columnIndex
|
||||
) throws DBException {
|
||||
if (columnIndex < 0 || columnIndex >= attributes.length) {
|
||||
throw new DBWebException("Column index '" + columnIndex + "' is out of range");
|
||||
}
|
||||
DBSEntityAttribute entityAttribute = attributes[columnIndex].getEntityAttribute();
|
||||
if (entityAttribute == null) {
|
||||
throw new DBException("Column [" + attributes[columnIndex].getName() + "] is not bound to any entity");
|
||||
}
|
||||
return entityAttribute.getParentObject();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<DBCTraceProperty> readDynamicTrace(
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Administration
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.admin;singleton:=true
|
||||
Bundle-Version: 1.0.146.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Version: 1.0.148.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.admin</artifactId>
|
||||
<version>1.0.146-SNAPSHOT</version>
|
||||
<version>1.0.148-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -88,6 +88,8 @@ type AdminUserInfo {
|
||||
disabledBy: String @since(version: "25.0.2")
|
||||
"Reason why the user was disabled"
|
||||
disableReason: String @since(version: "25.0.2")
|
||||
"Date of the user's last successful login"
|
||||
lastLoginTime: DateTime @since(version: "26.1.1")
|
||||
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -18,9 +18,9 @@ package io.cloudbeaver.service.admin;
|
||||
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.auth.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.security.SMDataSourceGrant;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.security.SMTeamMemberInfo;
|
||||
import org.jkiss.dbeaver.model.security.user.SMTeam;
|
||||
|
||||
|
||||
+7
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -26,9 +26,9 @@ import io.cloudbeaver.utils.CBModelConstants;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.auth.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.security.SMDataSourceGrant;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -142,4 +142,9 @@ public class AdminUserInfo {
|
||||
public String getDisableReason() {
|
||||
return user.getDisableReason();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLastLoginTime() {
|
||||
return user.getLastLoginTime() == null ? null : CBModelConstants.ISO_DATE_FORMAT.format(user.getLastLoginTime());
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -42,13 +42,17 @@ import org.jkiss.dbeaver.model.DBConstants;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.auth.AuthInfo;
|
||||
import org.jkiss.dbeaver.model.auth.SMAuthConfiguration;
|
||||
import org.jkiss.dbeaver.model.auth.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
import org.jkiss.dbeaver.model.security.*;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
import org.jkiss.dbeaver.model.security.SMConstants;
|
||||
import org.jkiss.dbeaver.model.security.SMDataSourceGrant;
|
||||
import org.jkiss.dbeaver.model.security.SMSubjectType;
|
||||
import org.jkiss.dbeaver.model.security.user.SMTeam;
|
||||
import org.jkiss.dbeaver.model.security.user.SMUser;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
@@ -642,7 +646,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
adminName = curUser == null ? null : curUser.getUserId();
|
||||
adminPassword = null;
|
||||
}
|
||||
List<AuthInfo> authInfos = new ArrayList<>();
|
||||
List<SMAuthConfiguration> authInfos = new ArrayList<>();
|
||||
List<WebAuthInfo> authInfoList = webSession.getAllAuthInfo();
|
||||
if (CommonUtils.isEmpty(adminName)) {
|
||||
// Try to get admin name from existing authentications (first one)
|
||||
@@ -654,7 +658,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
adminName = CBConstants.DEFAULT_ADMIN_NAME;
|
||||
}
|
||||
for (WebAuthInfo webAuthInfo : authInfoList) {
|
||||
authInfos.add(new AuthInfo(
|
||||
authInfos.add(new SMAuthConfiguration(
|
||||
webAuthInfo.getAuthProviderDescriptor().getId(),
|
||||
webAuthInfo.getUserCredentials()));
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Authentication
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.auth;singleton:=true
|
||||
Bundle-Version: 1.0.146.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Version: 1.0.148.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.auth</artifactId>
|
||||
<version>1.0.146-SNAPSHOT</version>
|
||||
<version>1.0.148-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Data Transfer
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.data.transfer;singleton:=true
|
||||
Bundle-Version: 1.0.147.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Version: 1.0.149.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.data.transfer</artifactId>
|
||||
<version>1.0.147-SNAPSHOT</version>
|
||||
<version>1.0.149-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+20
-11
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -42,18 +42,23 @@ import java.util.List;
|
||||
*/
|
||||
public interface DBWServiceDataTransfer extends DBWService {
|
||||
|
||||
@NotNull
|
||||
@WebAction
|
||||
List<WebDataTransferStreamProcessor> getAvailableStreamProcessors(WebSession session) throws DBWebException;
|
||||
List<WebDataTransferStreamProcessor> getAvailableStreamProcessors(@NotNull WebSession session) throws DBWebException;
|
||||
|
||||
@NotNull
|
||||
@WebAction
|
||||
List<WebDataTransferStreamProcessor> getAvailableImportStreamProcessors(WebSession session) throws DBWebException;
|
||||
List<WebDataTransferStreamProcessor> getAvailableImportStreamProcessors(@NotNull WebSession session) throws DBWebException;
|
||||
|
||||
@WebAction
|
||||
@NotNull
|
||||
@WebAction(requireGlobalPermissions = RMConstants.PERMISSION_DATA_EDITOR_EXPORT)
|
||||
WebAsyncTaskInfo dataTransferExportDataFromContainer(
|
||||
WebSQLProcessor sqlProcessor,
|
||||
String containerNodePath,
|
||||
WebDataTransferParameters parameters) throws DBWebException;
|
||||
@NotNull WebSQLProcessor sqlProcessor,
|
||||
@NotNull String containerNodePath,
|
||||
@NotNull WebDataTransferParameters parameters
|
||||
) throws DBWebException;
|
||||
|
||||
@NotNull
|
||||
@WebAction
|
||||
WebAsyncTaskInfo asyncImportDataContainer(
|
||||
@NotNull String processorId,
|
||||
@@ -61,20 +66,24 @@ public interface DBWServiceDataTransfer extends DBWService {
|
||||
@NotNull WebSQLResultsInfo webSQLResultsInfo,
|
||||
@NotNull WebSession webSession) throws DBWebException;
|
||||
|
||||
@NotNull
|
||||
@WebAction(requireGlobalPermissions = RMConstants.PERMISSION_DATA_EDITOR_EXPORT)
|
||||
WebAsyncTaskInfo dataTransferExportDataFromResults(
|
||||
WebSQLContextInfo sqlContextInfo,
|
||||
String resultsId,
|
||||
WebDataTransferParameters parameters) throws DBWebException;
|
||||
@NotNull WebSQLContextInfo sqlContextInfo,
|
||||
@NotNull String resultsId,
|
||||
@NotNull WebDataTransferParameters parameters
|
||||
) throws DBWebException;
|
||||
|
||||
/**
|
||||
* It's deprecated because now we use streaming file to response directly, and we don't need to clean up any files
|
||||
* after data transfer.
|
||||
*/
|
||||
@NotNull
|
||||
@WebAction
|
||||
@Deprecated
|
||||
Boolean dataTransferRemoveDataFile(WebSession session, String dataFileId) throws DBWebException;
|
||||
Boolean dataTransferRemoveDataFile(@NotNull WebSession session, @NotNull String dataFileId) throws DBWebException;
|
||||
|
||||
@NotNull
|
||||
WebDataTransferDefaultExportSettings defaultExportSettings();
|
||||
|
||||
/**
|
||||
|
||||
+12
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -23,6 +23,7 @@ import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.server.BaseWebPlatform;
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.server.WebApplication;
|
||||
import io.cloudbeaver.service.WebServiceServletBase;
|
||||
@@ -36,6 +37,7 @@ import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.annotation.MultipartConfig;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
@@ -77,7 +79,7 @@ public class WebDataTransferImportServlet extends WebServiceServletBase {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Permission denied");
|
||||
return;
|
||||
}
|
||||
if (!session.hasGlobalPermission(DBWConstants.GLOBAL_PERMISSION_DATA_EDITOR_IMPORT)) {
|
||||
if (validateImportPermission(session)) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Import is not allowed for this user");
|
||||
return;
|
||||
}
|
||||
@@ -134,4 +136,12 @@ public class WebDataTransferImportServlet extends WebServiceServletBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validateImportPermission(@NotNull WebSession session) {
|
||||
if (WebAppUtils.getWebApplication().isCommunity()) {
|
||||
Map<String, Object> productSettings = WebAppUtils.getWebApplication().getServerConfiguration().getProductSettings();
|
||||
return JSONUtils.getBoolean(productSettings, CBConstants.PREF_DATA_EDITOR_IMPORT_DISABLED_OLD, false);
|
||||
}
|
||||
return !session.hasGlobalPermission(DBWConstants.GLOBAL_PERMISSION_DATA_EDITOR_IMPORT);
|
||||
}
|
||||
}
|
||||
+78
-37
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -16,11 +16,14 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.data.transfer.impl;
|
||||
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.session.WebAsyncTaskProcessor;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import io.cloudbeaver.server.CBPlatform;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.service.data.transfer.DBWServiceDataTransfer;
|
||||
import io.cloudbeaver.service.sql.WebSQLContextInfo;
|
||||
import io.cloudbeaver.service.sql.WebSQLProcessor;
|
||||
@@ -73,8 +76,9 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<WebDataTransferStreamProcessor> getAvailableStreamProcessors(WebSession session) {
|
||||
public List<WebDataTransferStreamProcessor> getAvailableStreamProcessors(@NotNull WebSession session) {
|
||||
List<DataTransferProcessorDescriptor> processors = DataTransferRegistry.getInstance()
|
||||
.getAvailableProcessors(StreamTransferConsumer.class, DBSEntity.class);
|
||||
if (CommonUtils.isEmpty(processors)) {
|
||||
@@ -84,8 +88,9 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
|
||||
return processors.stream().map(x -> new WebDataTransferStreamProcessor(session, x)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<WebDataTransferStreamProcessor> getAvailableImportStreamProcessors(WebSession session) {
|
||||
public List<WebDataTransferStreamProcessor> getAvailableImportStreamProcessors(@NotNull WebSession session) {
|
||||
List<DataTransferProcessorDescriptor> processors =
|
||||
DataTransferRegistry.getInstance().getAvailableProcessors(StreamTransferProducer.class, DBSEntity.class);
|
||||
if (CommonUtils.isEmpty(processors)) {
|
||||
@@ -95,12 +100,16 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
|
||||
return processors.stream().map(x -> new WebDataTransferStreamProcessor(session, x)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WebAsyncTaskInfo dataTransferExportDataFromContainer(
|
||||
WebSQLProcessor sqlProcessor,
|
||||
String containerNodePath,
|
||||
WebDataTransferParameters parameters) throws DBWebException {
|
||||
|
||||
@NotNull WebSQLProcessor sqlProcessor,
|
||||
@NotNull String containerNodePath,
|
||||
@NotNull WebDataTransferParameters parameters
|
||||
) throws DBWebException {
|
||||
if (!validateExportPermission(sqlProcessor.getWebSession())) {
|
||||
throw new DBWebException("Data export was disabled by administrator");
|
||||
}
|
||||
DBSDataContainer dataContainer;
|
||||
try {
|
||||
dataContainer = sqlProcessor.getDataContainerByNodePath(sqlProcessor.getWebSession().getProgressMonitor(), containerNodePath, DBSDataContainer.class);
|
||||
@@ -111,11 +120,23 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
|
||||
return asyncExportFromDataContainer(sqlProcessor, parameters, dataContainer, null);
|
||||
}
|
||||
|
||||
private boolean validateExportPermission(@NotNull WebSession webSession) {
|
||||
if (!WebAppUtils.getWebApplication().isCommunity()) {
|
||||
// global permission is already checked
|
||||
return true;
|
||||
}
|
||||
Map<String, Object> productSettings = WebAppUtils.getWebApplication().getServerConfiguration().getProductSettings();
|
||||
// we need to check in product settings
|
||||
// product settings parameter saves only disabled state that's why we invert result
|
||||
return webSession.hasPermission(DBWConstants.PERMISSION_ADMIN) ||
|
||||
!CommonUtils.getOption(productSettings, CBConstants.PREF_DATA_EDITOR_EXPORT_DISABLED_OLD, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String makeUniqueFileName(
|
||||
WebSQLProcessor sqlProcessor,
|
||||
DataTransferProcessorDescriptor processor,
|
||||
Map<String, Object> processorProperties
|
||||
@NotNull WebSQLProcessor sqlProcessor,
|
||||
@NotNull DataTransferProcessorDescriptor processor,
|
||||
@Nullable Map<String, Object> processorProperties
|
||||
) {
|
||||
if (processorProperties != null && processorProperties.get(StreamConsumerSettings.PROP_FILE_EXTENSION) != null) {
|
||||
return sqlProcessor.getWebSession().getSessionId() + "_" + UUID.randomUUID() +
|
||||
@@ -124,25 +145,31 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
|
||||
return sqlProcessor.getWebSession().getSessionId() + "_" + UUID.randomUUID() + "." + WebDataTransferUtils.getProcessorFileExtension(processor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WebAsyncTaskInfo dataTransferExportDataFromResults(
|
||||
WebSQLContextInfo sqlContext,
|
||||
String resultsId,
|
||||
WebDataTransferParameters parameters) throws DBWebException {
|
||||
|
||||
@NotNull WebSQLContextInfo sqlContext,
|
||||
@NotNull String resultsId,
|
||||
@NotNull WebDataTransferParameters parameters
|
||||
) throws DBWebException {
|
||||
if (!validateExportPermission(sqlContext.getProcessor().getWebSession())) {
|
||||
throw new DBWebException("Data export was disabled by administrator");
|
||||
}
|
||||
WebSQLResultsInfo results = sqlContext.getResults(resultsId);
|
||||
|
||||
return asyncExportFromDataContainer(sqlContext.getProcessor(), parameters, results.getDataContainer(), results);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WebDataTransferDefaultExportSettings defaultExportSettings() {
|
||||
return new WebDataTransferDefaultExportSettings();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@Deprecated
|
||||
public Boolean dataTransferRemoveDataFile(WebSession webSession, String dataFileId) throws DBWebException {
|
||||
public Boolean dataTransferRemoveDataFile(@NotNull WebSession webSession, @NotNull String dataFileId) throws DBWebException {
|
||||
//deprecated
|
||||
return true;
|
||||
}
|
||||
@@ -158,7 +185,9 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
|
||||
DBSDataContainer dataContainer = taskConfig.getDataContainer();
|
||||
WebSQLResultsInfo resultsInfo = taskConfig.getResultsInfo();
|
||||
DataTransferProcessorDescriptor processor = DataTransferRegistry.getInstance().getProcessor(parameters.getProcessorId());
|
||||
|
||||
if (processor == null) {
|
||||
throw new DBException("Wrong data processor '" + parameters.getProcessorId() + "'");
|
||||
}
|
||||
try {
|
||||
exportData(monitor, processor, dataContainer, parameters, resultsInfo, outputStream);
|
||||
} catch (Exception e) {
|
||||
@@ -166,16 +195,20 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private WebAsyncTaskInfo asyncExportFromDataContainer(
|
||||
@NotNull WebSQLProcessor sqlProcessor,
|
||||
@NotNull WebDataTransferParameters parameters,
|
||||
@NotNull DBSDataContainer dataContainer,
|
||||
@Nullable WebSQLResultsInfo resultsInfo
|
||||
) {
|
||||
) throws DBWebException {
|
||||
sqlProcessor.getWebSession().addInfoMessage("Export data");
|
||||
log.info(String.format("Data export started: [userId=%s]", sqlProcessor.getWebSession().getUserId()));
|
||||
|
||||
DataTransferProcessorDescriptor processor = DataTransferRegistry.getInstance().getProcessor(parameters.getProcessorId());
|
||||
if (processor == null) {
|
||||
throw new DBWebException("Wrong data processor '" + parameters.getProcessorId() + "'");
|
||||
}
|
||||
String uniqueFileName = makeUniqueFileName(sqlProcessor, processor, parameters.getProcessorProperties());
|
||||
var outputSettings = parameters.getOutputSettings();
|
||||
String fileNameKey = WebDataTransferUtils.normalizeFileName(uniqueFileName, outputSettings);
|
||||
@@ -199,18 +232,24 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
|
||||
);
|
||||
}
|
||||
|
||||
public WebAsyncTaskInfo asyncImportDataContainer(@NotNull String processorId,
|
||||
@NotNull Path path,
|
||||
@NotNull WebSQLResultsInfo sqlContext,
|
||||
@NotNull WebSession webSession) throws DBWebException {
|
||||
@NotNull
|
||||
public WebAsyncTaskInfo asyncImportDataContainer(
|
||||
@NotNull String processorId,
|
||||
@NotNull Path path,
|
||||
@NotNull WebSQLResultsInfo sqlContext,
|
||||
@NotNull WebSession webSession
|
||||
) throws DBWebException {
|
||||
webSession.addInfoMessage("Import data");
|
||||
DataTransferProcessorDescriptor processor = DataTransferRegistry.getInstance().getProcessor(processorId);
|
||||
if (processor == null) {
|
||||
throw new DBWebException("Wrong data processor '" + processorId + "'");
|
||||
}
|
||||
|
||||
log.info(String.format("Data import started: [userId=%s]", webSession.getUserId()));
|
||||
DBSDataContainer dataContainer = sqlContext.getDataContainer();
|
||||
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
|
||||
@Override
|
||||
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
|
||||
public void run(@NotNull DBRProgressMonitor monitor) throws InvocationTargetException {
|
||||
monitor.beginTask("Import data", 1);
|
||||
try {
|
||||
monitor.subTask("Import data using " + processor.getName());
|
||||
@@ -238,13 +277,13 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
|
||||
}
|
||||
|
||||
private void exportData(
|
||||
DBRProgressMonitor monitor,
|
||||
DataTransferProcessorDescriptor processor,
|
||||
DBSDataContainer dataContainer,
|
||||
WebDataTransferParameters parameters,
|
||||
WebSQLResultsInfo resultsInfo,
|
||||
OutputStream outputStream
|
||||
) throws DBException, IOException {
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull DataTransferProcessorDescriptor processor,
|
||||
@NotNull DBSDataContainer dataContainer,
|
||||
@NotNull WebDataTransferParameters parameters,
|
||||
@NotNull WebSQLResultsInfo resultsInfo,
|
||||
@NotNull OutputStream outputStream
|
||||
) throws DBException {
|
||||
IDataTransferProcessor processorInstance = processor.getInstance();
|
||||
if (!(processorInstance instanceof IStreamDataExporter exporter)) {
|
||||
throw new DBException("Invalid processor. " + IStreamDataExporter.class.getSimpleName() + " expected");
|
||||
@@ -300,10 +339,11 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
|
||||
}
|
||||
|
||||
private void importData(
|
||||
DBRProgressMonitor monitor,
|
||||
DataTransferProcessorDescriptor processor,
|
||||
@NotNull DBSDataManipulator dataContainer,
|
||||
Path path) throws DBException {
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull DataTransferProcessorDescriptor processor,
|
||||
@NotNull DBSDataManipulator dataContainer,
|
||||
@NotNull Path path
|
||||
) throws DBException {
|
||||
IDataTransferProcessor processorInstance = processor.getInstance();
|
||||
|
||||
StreamTransferProducer producer;
|
||||
@@ -323,10 +363,11 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
|
||||
}
|
||||
producerSettings.setProcessorProperties(properties);
|
||||
producerSettings.updateProducerSettingsFromStream(
|
||||
monitor,
|
||||
producer,
|
||||
processorInstance,
|
||||
properties);
|
||||
monitor,
|
||||
producer,
|
||||
processorInstance,
|
||||
properties
|
||||
);
|
||||
DatabaseMappingContainer databaseMappingContainer =
|
||||
new DatabaseMappingContainer(monitor, databaseConsumerSettings, producer.getDatabaseObject(), consumer.getTargetObject());
|
||||
databaseMappingContainer.getAttributeMappings(monitor);
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - File System
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.fs;singleton:=true
|
||||
Bundle-Version: 1.0.64.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Version: 1.0.66.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.fs</artifactId>
|
||||
<version>1.0.64-SNAPSHOT</version>
|
||||
<version>1.0.66-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+5
-13
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -25,8 +25,6 @@ import io.cloudbeaver.service.fs.model.FSFile;
|
||||
import io.cloudbeaver.service.fs.model.FSFileSystem;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNProject;
|
||||
import org.jkiss.dbeaver.model.navigator.fs.DBNFileSystem;
|
||||
import org.jkiss.dbeaver.model.navigator.fs.DBNFileSystems;
|
||||
import org.jkiss.dbeaver.model.navigator.fs.DBNPathBase;
|
||||
@@ -51,18 +49,12 @@ public class WebServiceFS implements DBWServiceFS {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FSFileSystem[] getAvailableFileSystems(@NotNull WebSession webSession, @NotNull String projectId)
|
||||
throws DBWebException {
|
||||
public FSFileSystem[] getAvailableFileSystems(@NotNull WebSession webSession, @NotNull String projectId) throws DBWebException {
|
||||
try {
|
||||
DBPProject project = webSession.getProjectById(projectId);
|
||||
if (project == null) {
|
||||
throw new DBException(MessageFormat.format("Project ''{0}'' is not found in session", projectId));
|
||||
DBNFileSystems dbnFileSystems = webSession.getNavigatorModelOrThrow().getRoot().getExtraNode(DBNFileSystems.class);
|
||||
if (dbnFileSystems == null) {
|
||||
throw new DBWebException("File systems not found in navigator");
|
||||
}
|
||||
DBNProject projectNode = webSession.getNavigatorModelOrThrow().getRoot().getProjectNode(project);
|
||||
if (projectNode == null) {
|
||||
throw new DBException(MessageFormat.format("Project ''{0}'' is not found in navigator model", projectId));
|
||||
}
|
||||
DBNFileSystems dbnFileSystems = projectNode.getExtraNode(DBNFileSystems.class);
|
||||
var fsRegistry = FileSystemProviderRegistry.getInstance();
|
||||
DBNFileSystem[] children = dbnFileSystems.getChildren(webSession.getProgressMonitor());
|
||||
if (children == null) {
|
||||
|
||||
+1
-1
@@ -55,6 +55,6 @@ public class FSFile {
|
||||
@Property
|
||||
//TODO: node URI after finish migration to the new node path format
|
||||
public String getNodePath() {
|
||||
return node.getNodeItemPath();
|
||||
return node.getNodeUri();
|
||||
}
|
||||
}
|
||||
|
||||
+26
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -29,6 +29,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.Part;
|
||||
import org.eclipse.jetty.ee11.servlet.ServletContextRequest;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
@@ -41,6 +42,7 @@ import org.jkiss.utils.IOUtils;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -102,9 +104,10 @@ public class WebFSServlet extends WebServiceServletBase {
|
||||
if (CommonUtils.isEmpty(fileName)) {
|
||||
continue;
|
||||
}
|
||||
Path safeTarget = resolveSafeChild(path, fileName);
|
||||
try (InputStream is = part.getInputStream()) {
|
||||
Files.copy(is, path.resolve(fileName));
|
||||
node.addChildResource(path.resolve(fileName));
|
||||
Files.copy(is, safeTarget);
|
||||
node.addChildResource(safeTarget);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -113,6 +116,26 @@ public class WebFSServlet extends WebServiceServletBase {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Path resolveSafeChild(@NotNull Path parent, @NotNull String submittedFileName) throws DBException {
|
||||
Path candidate;
|
||||
try {
|
||||
candidate = Path.of(submittedFileName);
|
||||
} catch (InvalidPathException e) {
|
||||
throw new DBException("Invalid file name");
|
||||
}
|
||||
Path baseName = candidate.getFileName();
|
||||
if (baseName == null || baseName.toString().isBlank()) {
|
||||
throw new DBException("Invalid file name");
|
||||
}
|
||||
Path normalizedParent = parent.normalize();
|
||||
Path resolved = normalizedParent.resolve(baseName.toString()).normalize();
|
||||
if (!resolved.startsWith(normalizedParent) || resolved.equals(normalizedParent)) {
|
||||
throw new DBException("Invalid file name");
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> getVariables(HttpServletRequest request) {
|
||||
Map<String, Object> variables = super.getVariables(request);
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Cloudbeaver Web Service - LDAP
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.ldap.auth;singleton:=true
|
||||
Bundle-Version: 1.0.29.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Version: 1.0.31.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.model;visibility:=reexport,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.ldap.auth</artifactId>
|
||||
<version>1.0.29-SNAPSHOT</version>
|
||||
<version>1.0.31-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Metadata
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.metadata;singleton:=true
|
||||
Bundle-Version: 1.0.150.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Version: 1.0.152.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.metadata</artifactId>
|
||||
<version>1.0.150-SNAPSHOT</version>
|
||||
<version>1.0.152-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Resource manager NIO implementation
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.rm.nio;singleton:=true
|
||||
Bundle-Version: 1.0.64.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Version: 1.0.66.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.model,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.rm.nio</artifactId>
|
||||
<version>1.0.64-SNAPSHOT</version>
|
||||
<version>1.0.66-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+6
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* 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.
|
||||
@@ -18,8 +18,8 @@ package io.cloudbeaver.service.rm.fs;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.fs.AbstractFileSystemProvider;
|
||||
import org.jkiss.dbeaver.model.fs.DBFFileSystemContainer;
|
||||
import org.jkiss.dbeaver.model.fs.DBFVirtualFileSystem;
|
||||
import org.jkiss.dbeaver.model.rm.RMControllerProvider;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
@@ -27,15 +27,16 @@ import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
public class RMVirtualFileSystemProvider extends AbstractFileSystemProvider {
|
||||
private static final Log log = Log.getLog(RMVirtualFileSystemProvider.class);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DBFVirtualFileSystem[] getAvailableFileSystems(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull DBPProject project
|
||||
@NotNull DBFFileSystemContainer fsContainer
|
||||
) {
|
||||
if (!(project instanceof RMControllerProvider)) {
|
||||
if (!(fsContainer instanceof RMControllerProvider)) {
|
||||
return new DBFVirtualFileSystem[0];
|
||||
}
|
||||
RMControllerProvider rmControllerProvider = (RMControllerProvider) project;
|
||||
RMControllerProvider rmControllerProvider = (RMControllerProvider) fsContainer;
|
||||
return new DBFVirtualFileSystem[]{new RMVirtualFileSystem(rmControllerProvider.getResourceController(),
|
||||
rmControllerProvider.getRMProject())};
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user