mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
157
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 | ||
|
|
961430f988 | ||
|
|
759b30a24b | ||
|
|
225ce91fd7 | ||
|
|
f7bdd80e9f | ||
|
|
e42d34ac58 | ||
|
|
89a1af2df9 | ||
|
|
e4260b4cd2 | ||
|
|
956ebaa515 | ||
|
|
bccf47020e | ||
|
|
dcd2b608ea | ||
|
|
84af14d2f7 | ||
|
|
f513f7dc44 | ||
|
|
ffa7f730de | ||
|
|
694d3b241f | ||
|
|
9c8f3c47e6 | ||
|
|
f918c9e25e | ||
|
|
b84031bb84 | ||
|
|
af651f9d10 | ||
|
|
3245943f3d | ||
|
|
fe01b23fce | ||
|
|
8ec052e1b1 | ||
|
|
b10b407270 | ||
|
|
5f5a6743eb | ||
|
|
4b30dc3ba5 | ||
|
|
86e114d471 | ||
|
|
d4d2e2d1fa | ||
|
|
6670cc81ae | ||
|
|
f8a107256b | ||
|
|
4f796e5f9c | ||
|
|
fb6f3c8246 | ||
|
|
f3eaed955f | ||
|
|
339f19ff24 | ||
|
|
0229ed1327 | ||
|
|
6a6c29f38e | ||
|
|
c3054c1f26 | ||
|
|
20b129d5a2 | ||
|
|
a08b9c2f2b | ||
|
|
3a250e2e7a | ||
|
|
951b51b403 | ||
|
|
72fcd7bfe2 | ||
|
|
9ea3de9edf | ||
|
|
729d47fda1 | ||
|
|
ebda152b6b | ||
|
|
430ae8e5cc | ||
|
|
97c12d026b | ||
|
|
ea0997d3b0 | ||
|
|
04a2eed596 | ||
|
|
10d8741403 | ||
|
|
ded139584c | ||
|
|
04b3bd9e2b | ||
|
|
9303f4dacf | ||
|
|
7b258d7ba6 | ||
|
|
76c2d4b7fe | ||
|
|
61d0c43bd2 | ||
|
|
0c07a5cd09 | ||
|
|
3dcae7063c | ||
|
|
635d2f8d7f | ||
|
|
cea06eecae | ||
|
|
0e402cd27d | ||
|
|
d79054a5d8 | ||
|
|
0903025942 | ||
|
|
b36b11008f | ||
|
|
f8acdb58af | ||
|
|
4e4292376e | ||
|
|
d5860dc9e8 | ||
|
|
5301adaed4 | ||
|
|
fc9ef33e1d | ||
|
|
2a8f34682f | ||
|
|
9c6f301fa9 | ||
|
|
a2ccd0b0f7 | ||
|
|
acc3e42f1c | ||
|
|
075fa916f0 | ||
|
|
f7b96db2de | ||
|
|
dff9adaf34 | ||
|
|
86c50a1557 | ||
|
|
7d6c05f50c | ||
|
|
022778f95c | ||
|
|
d752d8ac26 | ||
|
|
e1a029b238 | ||
|
|
644dc2a136 | ||
|
|
768fe00f6a | ||
|
|
1e1f684379 | ||
|
|
ac3ce9e085 | ||
|
|
3cc9c3ce86 | ||
|
|
1660e62391 | ||
|
|
c27fca442b | ||
|
|
318100300f | ||
|
|
9c9e8e4496 | ||
|
|
4789acb76a | ||
|
|
39ad4568d0 | ||
|
|
860b8e2e16 | ||
|
|
4b9ba00663 | ||
|
|
897dd994ba | ||
|
|
e81a150287 | ||
|
|
0d17208be4 | ||
|
|
9c1678c256 | ||
|
|
321d45bd48 | ||
|
|
0f33fb5514 | ||
|
|
e7648b1b4c | ||
|
|
45cfded51f | ||
|
|
9822147d68 | ||
|
|
3b0f52f3ed | ||
|
|
3b032a275d | ||
|
|
a11ca992fe | ||
|
|
5138c73ae5 | ||
|
|
ea36247797 | ||
|
|
a6a1916299 | ||
|
|
b8699e6bcb | ||
|
|
8cff50b0cf | ||
|
|
b06a96dc31 |
@@ -46,7 +46,7 @@ jobs:
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
node-version: "24.13.1"
|
||||
cache: "yarn"
|
||||
cache-dependency-path: |
|
||||
cloudbeaver/webapp/yarn.lock
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
node-version: "24.13.1"
|
||||
cache: "yarn"
|
||||
cache-dependency-path: "./cloudbeaver/webapp/yarn.lock"
|
||||
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vscode-java-configs",
|
||||
"packageManager": "yarn@4.9.2",
|
||||
"packageManager": "yarn@4.14.1",
|
||||
"devDependencies": {
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
|
||||
@@ -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,90 +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.1 2026-03-23
|
||||
### 26.1.0 2026-06-01
|
||||
|
||||
- Data Editor:
|
||||
### 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.
|
||||
- Fixed a bug in the authentication form: the existing password is now required for password changes.
|
||||
- SQL Editor:
|
||||
- Updated the SQL Editor to use real tab characters for indentation. The application displayed tabs as four-space gaps with a distinct visual marker.
|
||||
- Added an ability to view and edit scripts without connecting to the database.
|
||||
- Added the ability to open generated queries right from the Generate SQL window.
|
||||
- Added fuzzy search to SQL Editor auto-completion for object names. The feature allows users to find tables and containers even with typos or incorrect letter ordering.
|
||||
- Added the tooltip about quoting rules for the Bind variables parameters dialog.
|
||||
- Fixed keyword autocompletion for partially typed keywords to avoid redundant letters.
|
||||
- Data Editor:
|
||||
- Added the ability to copy-paste multiple cells at once. Pasted values will be distributed across selected cells.
|
||||
- Added an ability to add, duplicate, delete, or set values to NULL in multiple highlighted rows via the Data Editor context menu.
|
||||
- Added the Find and Replace functionality for the Data Editor with the ability to find data by matching case, whole word, or using regular expressions.
|
||||
- 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:
|
||||
- Data Editor started to keep the state of column configurations, such as filters, sorting, and ordering, after the reconnect, page refresh, and re-login.
|
||||
- Added key column mark for tables with unique keys in the data grid.
|
||||
- Added more settings to the Generated SQL window, including the ability to select fully qualified names for query or use compact SQL formatting.
|
||||
- Added a DDL generation option to the Data Editor and Result Set context menu.
|
||||
- Fixed copy and paste behavior for multiline and special characters in the Data Editor. Added visual glyphs to indicate line breaks and tabs in the data grid.
|
||||
- Fixed the ability to copy Boolean values in the clipboard in the Data Editor.
|
||||
- Fixed the ability to calculate row count for non-standard SELECT statements. (thanks to @fdcastel)
|
||||
- Navigator tree:
|
||||
- Reorganized the context menu on the connection level to make it more compact.
|
||||
- Added support for special symbols (pipe, comma, and asterisk) for the search field.
|
||||
- Removed the "Folders" setting from the settings list for the Navigator Tree.
|
||||
- Accessibility:
|
||||
- Added the Skip to content option for quick keyboard access to the Navigator Tree, editors, and shortcuts list tab to improve application accessibility.
|
||||
- Improved keyboard navigation for context menu and buttons for Data Editor, SQL Editor, and Navigator tree.
|
||||
- Added the Ctrl + / keyboard shortcut to open the context menu for selected cells in the Data Editor.
|
||||
- Fixed the ability to switch tabs in the Data Editor using the keyboard.
|
||||
- Fixed contrast for elements across different application parts in the light and dark themes to meet WCAG requirements.
|
||||
- General:
|
||||
- Marked icons for non-saved (temporary) scripts with the hourglass symbol to add visual distinction from the saved ones.
|
||||
- Added the ability to set to NULL any value in the Driver Properties.
|
||||
- Fixed custom driver properties display for the connection page.
|
||||
- Fixed data export in SQL format for values containing single quotes.
|
||||
- Databases and drivers:
|
||||
- ClickHouse:
|
||||
- Fixed the display of Array(JSON) types in the data grid.
|
||||
- Updated driver version to 0.9.7.
|
||||
- Fixed IP address display in the Data Editor by removing the leading slash.
|
||||
- Added support for the "prompt=false" connection property for DB2 for IBM i to turn off interactive prompts and prevent related errors.
|
||||
- DuckDB:
|
||||
- Fixed map rendering for GEOMETRY types with CRS parameters.
|
||||
- Fixed geometry type display for database versions > 1.5.
|
||||
- H2 database:
|
||||
- Added new embedded driver version 2.4.2.
|
||||
- Updated server driver to version 2.4.2.
|
||||
- Firebird: Expanded database-specific SQL dialect coverage for the SQL Editor. Added more keywords, built-in functions, and SQL generators.
|
||||
- MySQL: Fixed query boundaries recognition in the SQL Editor for scripts with the DELIMITER keyword.
|
||||
- PostgreSQL:
|
||||
- Updated driver to version 42.7.11
|
||||
- Fixed missing INOUT parameters in CHECK generation for procedures and functions.
|
||||
- Fixed handling of 24:xx values in time columns.
|
||||
- Fixed an error when connecting via URL if the database name contains a hyphen.
|
||||
- SQL Server:
|
||||
- Fixed DDL generation for external tables.
|
||||
- Fixed the database connectivity issue after the Java update.
|
||||
- Security:
|
||||
- Added an administrative setting to restrict SSH tunneling capabilities. Administrators can now limit tunnel configuration to authorized users, reducing the risk of unauthorized network access.
|
||||
- Fixed the critical vulnerability (CVE-2025-62718) in the axios library. The library was updated to version 1.15.0.
|
||||
- Fixed the high vulnerability (CVE-2026-4800) in the lodash library. The library was updated to version 4.18.0.
|
||||
- Fixed the high vulnerability (CVE-2026-39363) in the vite library. The library was updated to version 7.3.2.
|
||||
- Fixed the high vulnerability (CVE-2026-42035) in the axios library. The library was updated to version 1.15.1.
|
||||
- Fixed the high vulnerability (CVE-2026-3505) in the bcpg-jdk18on library. The library was updated to version 1.84.0.
|
||||
- Fixed the high vulnerability (CVE-2026-33671) in the picomatch library. The library was updated to version 2.3.2.
|
||||
- Fixed the high vulnerability (CVE-2026-33943) in the happy-dom library. The library was updated to version 20.8.8.
|
||||
- Fixed the high vulnerability (CVE-2025-45141) in the tar library. The library was updated to version 7.5.11.
|
||||
- Fixed the high vulnerability (CVE-2026-27148) in the storybook library. The library was updated to version 10.2.10.
|
||||
- Fixed the high vulnerability (CVE-2026-27606) in the rollup library. The library was updated to version 4.59.0.
|
||||
- Fixed the high vulnerability (CVE-2026-27904) in the minimatch library. The library was updated to version 3.1.3.
|
||||
- Fixed the high vulnerability (CVE-2026-27959) in the koa library. The library was updated to version 2.16.4.
|
||||
- Fixed the high vulnerability (CVE-2026-32141) in the flatted library. The library was updated to version 4.4.0.
|
||||
|
||||
### 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: []
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ cd ../..
|
||||
echo "Pull dbeaver platform"
|
||||
[ ! -d dbeaver ] && git clone --depth 1 https://github.com/dbeaver/dbeaver.git
|
||||
[ ! -d dbeaver-common ] && git clone --depth 1 https://github.com/dbeaver/dbeaver-common.git
|
||||
[ ! -d dbeaver-jdbc-libsql ] && git clone --depth 1 https://github.com/dbeaver/dbeaver-jdbc-libsql.git
|
||||
|
||||
|
||||
cd cloudbeaver/deploy
|
||||
@@ -32,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
|
||||
|
||||
+3
-1
@@ -20,7 +20,6 @@ echo Pull dbeaver platform
|
||||
|
||||
IF NOT EXIST dbeaver git clone https://github.com/dbeaver/dbeaver.git
|
||||
IF NOT EXIST dbeaver-common git clone https://github.com/dbeaver/dbeaver-common.git
|
||||
IF NOT EXIST dbeaver-jdbc-libsql git clone https://github.com/dbeaver/dbeaver-jdbc-libsql.git
|
||||
|
||||
cd cloudbeaver\deploy
|
||||
|
||||
@@ -43,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; \
|
||||
|
||||
@@ -13,7 +13,7 @@ if [ "$(id -u)" -eq 0 ]; then
|
||||
|
||||
chown -R $DBEAVER_UID:$DBEAVER_GID $PWD/workspace
|
||||
# Execute run-cloudbeaver-server.sh as the dbeaver user with the JAVA_HOME and PATH environment variables
|
||||
exec su "$TARGET_USER" -c "JAVA_HOME=$JAVA_HOME PATH=$PATH ./run-cloudbeaver-server.sh"
|
||||
exec su "$TARGET_USER" -c "JAVA_HOME=$JAVA_HOME PATH=$PATH ./run-cloudbeaver-server.sh $*"
|
||||
else
|
||||
exec ./run-cloudbeaver-server.sh
|
||||
fi
|
||||
exec ./run-cloudbeaver-server.sh "$@"
|
||||
fi
|
||||
|
||||
@@ -35,7 +35,7 @@ exec java ${JAVA_OPTS} \
|
||||
--add-opens=java.sql/java.sql=ALL-UNNAMED \
|
||||
-jar ${launcherJar} \
|
||||
-product io.cloudbeaver.product.ce.product \
|
||||
-web-config conf/cloudbeaver.conf \
|
||||
"$@" \
|
||||
-web-config conf/cloudbeaver.conf \
|
||||
-nl en \
|
||||
-registryMultiLanguage
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
dbeaver-common
|
||||
dbeaver-jdbc-libsql
|
||||
dbeaver
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Model
|
||||
Bundle-SymbolicName: io.cloudbeaver.model.cli;singleton:=true
|
||||
Bundle-Version: 1.0.7.qualifier
|
||||
Bundle-Release-Date: 20260406
|
||||
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.7-SNAPSHOT</version>
|
||||
<version>1.0.13-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+6
-2
@@ -22,8 +22,11 @@ import org.jkiss.dbeaver.model.cli.ApplicationCommandLine;
|
||||
import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
|
||||
import org.jkiss.dbeaver.model.cli.CLIContextImpl;
|
||||
import org.jkiss.dbeaver.model.cli.CLIRunMeta;
|
||||
import org.jkiss.dbeaver.model.cli.registry.CLICommandDescriptor;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationInstanceController> {
|
||||
private final CloudBeaverMixin mixin;
|
||||
|
||||
@@ -53,9 +56,10 @@ public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationIn
|
||||
protected CommandLine initCommandLine(
|
||||
@Nullable ApplicationInstanceController applicationInstanceController,
|
||||
@NotNull CLIContextImpl context,
|
||||
@NotNull CLIRunMeta runMeta
|
||||
@NotNull CLIRunMeta runMeta,
|
||||
@NotNull List<CLICommandDescriptor> commandsToExecute
|
||||
) {
|
||||
var cmd = super.initCommandLine(applicationInstanceController, context, runMeta);
|
||||
var cmd = super.initCommandLine(applicationInstanceController, context, runMeta, commandsToExecute);
|
||||
cmd.addMixin("cloudbeaver", mixin);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
@@ -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.98.qualifier
|
||||
Bundle-Release-Date: 20260406
|
||||
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,
|
||||
@@ -17,7 +17,6 @@ Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
org.jkiss.bundle.logback;visibility:=reexport,
|
||||
org.jkiss.bundle.graphql;visibility:=reexport,
|
||||
org.jkiss.bundle.apache.dbcp,
|
||||
com.google.gson;visibility:=reexport,
|
||||
jakarta.servlet-api;bundle-version:="6.0.0";visibility:=reexport
|
||||
Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.auth,
|
||||
@@ -29,6 +28,11 @@ Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.websocket.event.client,
|
||||
io.cloudbeaver.websocket.event.task,
|
||||
io.cloudbeaver.model,
|
||||
io.cloudbeaver.model.api.annotation,
|
||||
io.cloudbeaver.model.api.annotation.enums,
|
||||
io.cloudbeaver.model.api.annotation.media,
|
||||
io.cloudbeaver.model.api.annotation.responses,
|
||||
io.cloudbeaver.model.api.annotation.tags,
|
||||
io.cloudbeaver.model.app,
|
||||
io.cloudbeaver.model.config,
|
||||
io.cloudbeaver.model.apilog,
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
label="Default secret controller" description="Default void secret controller"/>
|
||||
</extension>
|
||||
|
||||
<extension point="io.cloudbeaver.feature">
|
||||
<feature id="ssh" label="SSH tunnels for databases" description="Enables SSH tunnels for secure database connections, allowing access to remote databases through encrypted channels" enabledByDefault="true"/>
|
||||
</extension>
|
||||
|
||||
|
||||
<extension point="org.jkiss.dbeaver.ws.event">
|
||||
<event topicId="cb_session_task" id="cb_session_task_info_updated"
|
||||
class="io.cloudbeaver.websocket.event.task.WSSessionTaskInfoEvent"/>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model</artifactId>
|
||||
<version>1.0.98-SNAPSHOT</version>
|
||||
<version>1.0.104-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+5
-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.
|
||||
@@ -27,6 +27,7 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.registry.DataSourcePreferenceStore;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C extends DataSourceDescriptor> {
|
||||
@@ -93,6 +94,9 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
|
||||
input.isSharedCredentials()
|
||||
);
|
||||
dataSource.setConnectionReadOnly(input.isReadOnly());
|
||||
DataSourcePreferenceStore preferenceStore = dataSource.getPreferenceStore();
|
||||
preferenceStore.clear();
|
||||
preferenceStore.setProperties(input.getDefaultUserPreferences());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+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()
|
||||
)
|
||||
);
|
||||
|
||||
+10
-20
@@ -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.
|
||||
@@ -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,
|
||||
@@ -407,12 +397,12 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
|
||||
}
|
||||
|
||||
@Override
|
||||
public void persistSecrets(DBSSecretController secretController) throws DBException {
|
||||
public void persistSecrets(@NotNull DBSSecretController secretController) throws DBException {
|
||||
dataSourceRegistry.persistSecrets(secretController);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveSecrets(DBSSecretController secretController) throws DBException {
|
||||
public void resolveSecrets(@NotNull DBSSecretController secretController) throws DBException {
|
||||
dataSourceRegistry.resolveSecrets(secretController);
|
||||
}
|
||||
|
||||
|
||||
+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;
|
||||
|
||||
@@ -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,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;
|
||||
@@ -252,14 +252,14 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
|
||||
}
|
||||
case WSDataSourceEvent.UPDATED -> {
|
||||
if (event.getProperty() == WSDataSourceProperty.CONFIGURATION) {
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds);
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds, true);
|
||||
}
|
||||
if (event.getProperty() != WSDataSourceProperty.INTERNAL) {
|
||||
sendDataSourceUpdatedEvent = true;
|
||||
}
|
||||
}
|
||||
case WSDataSourceEvent.DELETED -> {
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds);
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds, false);
|
||||
if (registry instanceof DBPDataSourceRegistryCache dsrc) {
|
||||
dsrc.removeDataSourceFromList(ds);
|
||||
}
|
||||
@@ -345,7 +345,7 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
|
||||
@NotNull
|
||||
private WebConnectionInfo closeAndDeleteConnection(@NotNull WebConnectionInfo connectionInfo) throws DBWebException {
|
||||
DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer();
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer);
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer, false);
|
||||
DBPDataSourceRegistry registry = getDataSourceRegistry();
|
||||
registry.removeDataSource(dataSourceContainer);
|
||||
removeConnection(dataSourceContainer);
|
||||
|
||||
+15
-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.
|
||||
@@ -23,6 +23,7 @@ import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -64,6 +65,8 @@ public class WebConnectionConfig {
|
||||
private Boolean defaultAutoCommit;
|
||||
private String defaultCatalogName;
|
||||
private String defaultSchemaName;
|
||||
@NotNull
|
||||
private Map<String, String> defaultUserPreferences = new LinkedHashMap<>();
|
||||
|
||||
public WebConnectionConfig() {
|
||||
}
|
||||
@@ -111,6 +114,8 @@ public class WebConnectionConfig {
|
||||
String configType = JSONUtils.getString(params, "configurationType");
|
||||
configurationType = configType == null ? null : DBPDriverConfigurationType.valueOf(configType);
|
||||
|
||||
Map<String, Object> stringObjectUserPrefMap = JSONUtils.getObject(params, "defaultUserPreferences");
|
||||
stringObjectUserPrefMap.forEach((key, value) -> defaultUserPreferences.put(key, value.toString()));
|
||||
networkHandlersConfig = new ArrayList<>();
|
||||
for (Map<String, Object> nhc : JSONUtils.getObjectList(params, "networkHandlersConfig")) {
|
||||
networkHandlersConfig.add(new WebNetworkHandlerConfigInput(nhc));
|
||||
@@ -367,4 +372,13 @@ public class WebConnectionConfig {
|
||||
public void setDefaultSchemaName(String defaultSchemaName) {
|
||||
this.defaultSchemaName = defaultSchemaName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, String> getDefaultUserPreferences() {
|
||||
return defaultUserPreferences;
|
||||
}
|
||||
|
||||
public void setDefaultUserPreferences(@NotNull Map<String, String> defaultUserPreferences) {
|
||||
this.defaultUserPreferences = defaultUserPreferences;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-2
@@ -39,12 +39,15 @@ 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;
|
||||
import org.jkiss.dbeaver.model.rm.RMConstants;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRRunnableParametrized;
|
||||
import org.jkiss.dbeaver.registry.DataSourcePreferenceStore;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
@@ -211,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
|
||||
@@ -293,6 +297,16 @@ public class WebConnectionInfo {
|
||||
return dataSourceContainer.getNavigatorSettings().getOriginalSettings();
|
||||
}
|
||||
|
||||
@Property
|
||||
@NotNull
|
||||
public Map<String, String> getDefaultUserPreferences() {
|
||||
DBPPreferenceStore preferenceStore = dataSourceContainer.getPreferenceStore();
|
||||
if (preferenceStore instanceof DataSourcePreferenceStore dataSourcePreferenceStore) {
|
||||
return dataSourcePreferenceStore.getProperties();
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Property
|
||||
@NotNull
|
||||
public List<WebDataFormat> getSupportedDataFormats() {
|
||||
@@ -578,4 +592,5 @@ public class WebConnectionInfo {
|
||||
public void setCredentialsSavedInSession(@Nullable Boolean credentialsSavedInSession) {
|
||||
this.credentialsSavedInSession = credentialsSavedInSession;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-2
@@ -16,6 +16,8 @@
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerType;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
@@ -28,12 +30,14 @@ import java.util.Map;
|
||||
*/
|
||||
public class WebNetworkHandlerConfigInput {
|
||||
|
||||
@NotNull
|
||||
private final Map<String, Object> cfg;
|
||||
|
||||
public WebNetworkHandlerConfigInput(Map<String, Object> cfg) {
|
||||
public WebNetworkHandlerConfigInput(@NotNull Map<String, Object> cfg) {
|
||||
this.cfg = cfg;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DBWHandlerType getType() {
|
||||
return CommonUtils.valueOf(DBWHandlerType.class, JSONUtils.getString(cfg, "type"), null);
|
||||
}
|
||||
@@ -42,6 +46,7 @@ public class WebNetworkHandlerConfigInput {
|
||||
return JSONUtils.getString(cfg, "id");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean isEnabled() {
|
||||
if (cfg.containsKey("enabled")) {
|
||||
return JSONUtils.getBoolean(cfg, "enabled");
|
||||
@@ -62,11 +67,13 @@ public class WebNetworkHandlerConfigInput {
|
||||
return JSONUtils.getString(cfg, "password");
|
||||
}
|
||||
|
||||
@Deprecated // use secured properties
|
||||
// Deprecated. Use secured properties
|
||||
@Nullable
|
||||
public String getKey() {
|
||||
return JSONUtils.getString(cfg, "key");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean isSavePassword() {
|
||||
if (cfg.containsKey("savePassword")) {
|
||||
return JSONUtils.getBoolean(cfg, "savePassword");
|
||||
@@ -75,10 +82,12 @@ public class WebNetworkHandlerConfigInput {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<String, Object> getProperties() {
|
||||
return JSONUtils.getObjectOrNull(cfg, "properties");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<String, String> getSecureProperties() {
|
||||
var secureProperties = JSONUtils.getObjectOrNull(cfg, "secureProperties");
|
||||
if (secureProperties == null) {
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation;
|
||||
|
||||
import io.cloudbeaver.model.api.annotation.responses.ApiResponse;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Operation {
|
||||
|
||||
String summary() default "";
|
||||
|
||||
String description() default "";
|
||||
|
||||
String operationId() default "";
|
||||
|
||||
String method() default "";
|
||||
|
||||
Parameter[] parameters() default {};
|
||||
|
||||
ApiResponse[] responses() default {};
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation;
|
||||
|
||||
import io.cloudbeaver.model.api.annotation.enums.Explode;
|
||||
import io.cloudbeaver.model.api.annotation.enums.ParameterIn;
|
||||
import io.cloudbeaver.model.api.annotation.enums.ParameterStyle;
|
||||
import io.cloudbeaver.model.api.annotation.media.ArraySchema;
|
||||
import io.cloudbeaver.model.api.annotation.media.Schema;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Repeatable(Parameters.class)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
|
||||
public @interface Parameter {
|
||||
|
||||
String name() default "";
|
||||
|
||||
String description() default "";
|
||||
|
||||
boolean required() default false;
|
||||
|
||||
String example() default "";
|
||||
|
||||
ParameterIn in() default ParameterIn.DEFAULT;
|
||||
|
||||
Schema schema() default @Schema;
|
||||
|
||||
ArraySchema array() default @ArraySchema;
|
||||
|
||||
ParameterStyle style() default ParameterStyle.DEFAULT;
|
||||
|
||||
Explode explode() default Explode.DEFAULT;
|
||||
|
||||
boolean hidden() default false;
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
|
||||
public @interface Parameters {
|
||||
|
||||
Parameter[] value();
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.enums;
|
||||
|
||||
public enum Explode {
|
||||
TRUE("true"),
|
||||
FALSE("false"),
|
||||
DEFAULT("default");
|
||||
|
||||
private final String value;
|
||||
|
||||
Explode(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.enums;
|
||||
|
||||
public enum ParameterIn {
|
||||
DEFAULT("default"),
|
||||
QUERY("query"),
|
||||
HEADER("header"),
|
||||
PATH("path"),
|
||||
COOKIE("cookie");
|
||||
|
||||
private final String value;
|
||||
|
||||
ParameterIn(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.enums;
|
||||
|
||||
public enum ParameterStyle {
|
||||
MATRIX("matrix"),
|
||||
LABEL("label"),
|
||||
FORM("form"),
|
||||
SIMPLE("simple"),
|
||||
SPACEDELIMITED("spaceDelimited"),
|
||||
PIPEDELIMITED("pipeDelimited"),
|
||||
DEEPOBJECT("deepObject"),
|
||||
DEFAULT("default");
|
||||
|
||||
private final String value;
|
||||
|
||||
ParameterStyle(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.media;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
|
||||
public @interface ArraySchema {
|
||||
|
||||
Schema schema() default @Schema;
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.media;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
public @interface Content {
|
||||
|
||||
Schema schema() default @Schema;
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.media;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
|
||||
public @interface Schema {
|
||||
|
||||
String description() default "";
|
||||
|
||||
String example() default "";
|
||||
|
||||
String type() default "";
|
||||
|
||||
Class<?> implementation() default Void.class;
|
||||
|
||||
boolean required() default false;
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.responses;
|
||||
|
||||
import io.cloudbeaver.model.api.annotation.media.Content;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
public @interface ApiResponse {
|
||||
|
||||
String responseCode() default "default";
|
||||
|
||||
String description() default "";
|
||||
|
||||
Content[] content() default {};
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.tags;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Tag {
|
||||
|
||||
String name() default "";
|
||||
|
||||
String description() default "";
|
||||
}
|
||||
+18
-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.
|
||||
@@ -25,10 +25,13 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBFileController;
|
||||
import org.jkiss.dbeaver.model.app.DBPLockManagerProvider;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockManager;
|
||||
import org.jkiss.dbeaver.model.fs.lock.shared.SharedFileLockManager;
|
||||
import org.jkiss.dbeaver.model.impl.app.ApplicationRegistry;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
|
||||
@@ -48,7 +51,7 @@ import java.util.Map;
|
||||
/**
|
||||
* Servlet application
|
||||
*/
|
||||
public abstract class BaseServletApplication extends BaseApplicationImpl implements ServletApplication {
|
||||
public abstract class BaseServletApplication extends BaseApplicationImpl implements ServletApplication, DBPLockManagerProvider {
|
||||
|
||||
public static final String DEFAULT_CONFIG_FILE_PATH = "/etc/cloudbeaver.conf";
|
||||
public static final String CUSTOM_CONFIG_FOLDER = "custom";
|
||||
@@ -59,6 +62,7 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
|
||||
private String instanceId;
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public RMController createResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@@ -73,6 +77,18 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
throw new IllegalStateException("File controller is not supported by " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LockManager createLockManager(@NotNull String applicationId, @NotNull Path metadataFolder) throws DBException {
|
||||
return new SharedFileLockManager(applicationId, metadataFolder);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LockManager createLockManager() throws DBException {
|
||||
return new SharedFileLockManager(getApplicationInstanceId());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Path getDefaultWorkingFolder() {
|
||||
|
||||
-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 {
|
||||
|
||||
+10
-8
@@ -26,7 +26,9 @@ import org.jkiss.dbeaver.model.DBPDataSourceFolder;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockManager;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMEvent;
|
||||
import org.jkiss.dbeaver.model.rm.RMEventManager;
|
||||
@@ -53,11 +55,11 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull
|
||||
protected final DBPWorkspace workspace;
|
||||
@NotNull
|
||||
protected final FileLockController lockController;
|
||||
protected final LockManager lockController;
|
||||
|
||||
protected BaseLocalResourceController(
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull FileLockController lockController
|
||||
@NotNull LockManager lockController
|
||||
) {
|
||||
this.workspace = workspace;
|
||||
this.lockController = lockController;
|
||||
@@ -157,7 +159,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String configuration,
|
||||
@Nullable List<String> dataSourceIds
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "updateProjectDataSources")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("updateProjectDataSources"))) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
return doFileWriteOperation(
|
||||
projectId, project.getMetadataFolder(false),
|
||||
@@ -188,7 +190,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String projectId,
|
||||
@NotNull String[] dataSourceIds
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "deleteDataSources")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteDataSources"))) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false), () -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
@@ -213,7 +215,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String projectId,
|
||||
@NotNull String folderPath
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
log.debug("Creating data source folder '" + folderPath + "' in project '" + projectId + "'");
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
@@ -241,7 +243,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String[] folderPaths,
|
||||
boolean dropContents
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
() -> {
|
||||
@@ -267,7 +269,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String oldPath,
|
||||
@NotNull String newPath
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
log.debug("Moving data source folder from '" + oldPath + "' to '" + newPath + "' in project '" + projectId + "'");
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
|
||||
+48
-25
@@ -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.
|
||||
@@ -33,13 +33,15 @@ 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.fs.lock.FileLockController;
|
||||
import org.jkiss.dbeaver.model.auth.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockManager;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
|
||||
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNLocalFolder;
|
||||
import org.jkiss.dbeaver.model.rm.*;
|
||||
import org.jkiss.dbeaver.model.security.SMAdminController;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.sql.DBQuotaException;
|
||||
import org.jkiss.dbeaver.model.websocket.event.MessageType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
|
||||
@@ -51,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;
|
||||
@@ -90,9 +93,10 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull Path rootPath,
|
||||
@NotNull Path userProjectsPath,
|
||||
@NotNull Path sharedProjectsPath,
|
||||
@NotNull Supplier<SMAdminController> smControllerSupplier
|
||||
@NotNull Supplier<SMAdminController> smControllerSupplier,
|
||||
@NotNull LockManager lockController
|
||||
) throws DBException {
|
||||
super(workspace, new FileLockController(ServletAppUtils.getServletApplication().getApplicationInstanceId()));
|
||||
super(workspace, lockController);
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
this.rootPath = rootPath;
|
||||
this.userProjectsPath = userProjectsPath;
|
||||
@@ -101,7 +105,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName();
|
||||
this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers();
|
||||
this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath);
|
||||
this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath, lockController);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -280,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)) {
|
||||
@@ -303,7 +307,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@Override
|
||||
public RMProject updateProject(@NotNull String projectId, @NotNull RMProjectInfo projectInfo) throws DBException {
|
||||
validateProjectName(projectId, projectInfo.getName());
|
||||
try (var ignoredLock = lockController.lock(projectId, "updateProject")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("updateProject"))) {
|
||||
RMLocalProject project = getWebProject(projectId, false);
|
||||
Path targetPath = getProjectPath(projectId);
|
||||
if (!Files.exists(targetPath)) {
|
||||
@@ -330,7 +334,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
@Override
|
||||
public void deleteProject(@NotNull String projectId) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "deleteProject")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteProject"))) {
|
||||
Path targetPath = getProjectPath(projectId);
|
||||
if (!Files.exists(targetPath)) {
|
||||
log.error(MessageFormat.format("Project folder ''{0}'' is not found", projectId));
|
||||
@@ -699,7 +703,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull String resourcePath,
|
||||
boolean isFolder
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "createResource")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createResource"))) {
|
||||
validateResourcePath(resourcePath);
|
||||
Path targetPath = getTargetPath(projectId, resourcePath);
|
||||
if (Files.exists(targetPath)) {
|
||||
@@ -731,7 +735,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull String oldResourcePath,
|
||||
@NotNull String newResourcePath
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "moveResource")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("moveResource"))) {
|
||||
var normalizedOldResourcePath = CommonUtils.normalizeResourcePath(oldResourcePath);
|
||||
var normalizedNewResourcePath = CommonUtils.normalizeResourcePath(newResourcePath);
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -803,7 +807,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
@Override
|
||||
public void deleteResource(@NotNull String projectId, @NotNull String resourcePath, boolean recursive) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "deleteResource")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteResource"))) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Removing resource from '" + resourcePath + "' in project '" + projectId + "'" + (recursive ? " recursive" : ""));
|
||||
}
|
||||
@@ -891,7 +895,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull byte[] data,
|
||||
boolean forceOverwrite
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "setResourceContents")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("setResourceContents"))) {
|
||||
validateResourcePath(resourcePath);
|
||||
Number fileSizeLimit = ServletAppUtils.getServletApplication()
|
||||
.getAppConfiguration()
|
||||
@@ -936,7 +940,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull String propertyName,
|
||||
@Nullable Object propertyValue
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "resourcePropertyUpdate")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("resourcePropertyUpdate"))) {
|
||||
validateResourcePath(resourcePath);
|
||||
RMLocalProject webProject = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
|
||||
@@ -957,7 +961,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull String resourcePath,
|
||||
@NotNull Map<String, Object> properties
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "resourcePropertyUpdate")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("resourcePropertyUpdate"))) {
|
||||
validateResourcePath(resourcePath);
|
||||
RMLocalProject webProject = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
|
||||
@@ -1103,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)) {
|
||||
@@ -1204,12 +1209,14 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
return getProjectPath(projectId).toAbsolutePath().relativize(path).toString().replace('\\', IPath.SEPARATOR);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Builder builder(
|
||||
SMCredentialsProvider credentialsProvider,
|
||||
DBPWorkspace workspace,
|
||||
Supplier<SMAdminController> smControllerSupplier
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull LockManager lockController,
|
||||
@NotNull Supplier<SMAdminController> smControllerSupplier
|
||||
) {
|
||||
return new Builder(workspace, credentialsProvider, smControllerSupplier);
|
||||
return new Builder(workspace, credentialsProvider, lockController, smControllerSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1225,10 +1232,13 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
protected Path rootPath;
|
||||
protected Path userProjectsPath;
|
||||
protected Path sharedProjectsPath;
|
||||
protected LockManager lockController;
|
||||
|
||||
protected Builder(
|
||||
DBPWorkspace workspace, SMCredentialsProvider credentialsProvider,
|
||||
Supplier<SMAdminController> smControllerSupplier
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull LockManager lockController,
|
||||
@NotNull Supplier<SMAdminController> smControllerSupplier
|
||||
) {
|
||||
this.workspace = workspace;
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
@@ -1236,25 +1246,38 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
this.rootPath = RMUtils.getRootPath();
|
||||
this.userProjectsPath = RMUtils.getUserProjectsPath();
|
||||
this.sharedProjectsPath = RMUtils.getSharedProjectsPath();
|
||||
this.lockController = lockController;
|
||||
}
|
||||
|
||||
public Builder setRootPath(Path rootPath) {
|
||||
@NotNull
|
||||
public Builder setRootPath(@NotNull Path rootPath) {
|
||||
this.rootPath = rootPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setUserProjectsPath(Path userProjectsPath) {
|
||||
@NotNull
|
||||
public Builder setUserProjectsPath(@NotNull Path userProjectsPath) {
|
||||
this.userProjectsPath = userProjectsPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setSharedProjectsPath(Path sharedProjectsPath) {
|
||||
@NotNull
|
||||
public Builder setSharedProjectsPath(@NotNull Path sharedProjectsPath) {
|
||||
this.sharedProjectsPath = sharedProjectsPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LocalResourceController build() throws DBException {
|
||||
return new LocalResourceController(workspace, credentialsProvider, rootPath, userProjectsPath, sharedProjectsPath, smController);
|
||||
return new LocalResourceController(
|
||||
workspace,
|
||||
credentialsProvider,
|
||||
rootPath,
|
||||
userProjectsPath,
|
||||
sharedProjectsPath,
|
||||
smController,
|
||||
lockController
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-7
@@ -17,14 +17,15 @@
|
||||
package io.cloudbeaver.model.rm.local;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockManager;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectInfo;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
@@ -45,12 +46,12 @@ public class ProjectsMetadataInfo {
|
||||
|
||||
private final Map<String, RMProjectInfo> projectsInfo = new LinkedHashMap<>();
|
||||
private final Path projectsPath;
|
||||
private final FileLockController lockController;
|
||||
private final LockManager lockController;
|
||||
|
||||
|
||||
public ProjectsMetadataInfo(@NotNull Path projectsPath) throws DBException {
|
||||
public ProjectsMetadataInfo(@NotNull Path projectsPath, @NotNull LockManager lockController) throws DBException {
|
||||
this.projectsPath = projectsPath;
|
||||
this.lockController = new FileLockController(ServletAppUtils.getServletApplication().getApplicationInstanceId());
|
||||
this.lockController = lockController;
|
||||
readProjectInfos(projectsPath);
|
||||
}
|
||||
|
||||
@@ -139,7 +140,7 @@ public class ProjectsMetadataInfo {
|
||||
}
|
||||
|
||||
private void saveProjectsInfo() {
|
||||
try (var lock = lockController.lock(PROJECTS_INFO_FILE_NAME, "saveProjectsInfo")) {
|
||||
try (var lock = lockController.lock(LockTarget.of(PROJECTS_INFO_FILE_NAME), LockOptions.of("saveProjectsInfo"))) {
|
||||
log.info("Saving project information");
|
||||
Files.writeString(projectsPath.resolve(PROJECTS_INFO_FILE_NAME), JSONUtils.GSON.toJson(projectsInfo));
|
||||
} catch (IOException e) {
|
||||
@@ -148,4 +149,4 @@ public class ProjectsMetadataInfo {
|
||||
log.error("Error locking file " + PROJECTS_INFO_FILE_NAME + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-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.
|
||||
@@ -55,14 +55,21 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
@NotNull
|
||||
protected final ServletApplication application;
|
||||
protected volatile long lastAccessTime;
|
||||
@NotNull
|
||||
private final SessionType sessionType;
|
||||
|
||||
private final List<CBWebSessionEventHandler> sessionEventHandlers = new CopyOnWriteArrayList<>();
|
||||
private WebSessionEventsFilter eventsFilter;
|
||||
private final WebSessionWorkspace workspace;
|
||||
|
||||
public BaseWebSession(@NotNull String id, @NotNull ServletApplication application) throws DBException {
|
||||
public BaseWebSession(
|
||||
@NotNull String id,
|
||||
@NotNull ServletApplication application,
|
||||
@NotNull SessionType sessionType
|
||||
) throws DBException {
|
||||
this.id = id;
|
||||
this.application = application;
|
||||
this.sessionType = sessionType;
|
||||
this.createTime = System.currentTimeMillis();
|
||||
this.lastAccessTime = this.createTime;
|
||||
this.workspace = createWebWorkspace();
|
||||
@@ -151,7 +158,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
}
|
||||
|
||||
protected synchronized void clearSessionContext() {
|
||||
this.workspace.getAuthContext().clear();
|
||||
this.workspace.getAuthContext().clearContext(false);
|
||||
this.workspace.getAuthContext().addSession(this);
|
||||
}
|
||||
|
||||
@@ -176,6 +183,11 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
return lastAccessTime;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SessionType getSessionType() {
|
||||
return sessionType;
|
||||
}
|
||||
|
||||
public void touchSession() {
|
||||
this.lastAccessTime = System.currentTimeMillis();
|
||||
}
|
||||
@@ -187,16 +199,17 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
cleanUpSession(true);
|
||||
super.close();
|
||||
}
|
||||
|
||||
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
|
||||
cleanUpSession(sendSessionExpiredEvent);
|
||||
super.close();
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
public enum SessionType {
|
||||
WEB,
|
||||
HEADLESS,
|
||||
API_TOKEN
|
||||
}
|
||||
+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.
|
||||
@@ -31,7 +31,7 @@ public class WebHeadlessSession extends BaseWebSession {
|
||||
@NotNull String id,
|
||||
@NotNull ServletAuthApplication application
|
||||
) throws DBException {
|
||||
super(id, application);
|
||||
super(id, application, SessionType.HEADLESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+25
-4
@@ -17,6 +17,7 @@
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.utils.HttpConstants;
|
||||
|
||||
@@ -31,12 +32,16 @@ public class WebHttpRequestInfo {
|
||||
private final String lastRemoteAddress;
|
||||
@Nullable
|
||||
private final String lastRemoteUserAgent;
|
||||
@NotNull
|
||||
private final SessionType sessionType;
|
||||
|
||||
public WebHttpRequestInfo(HttpServletRequest request) {
|
||||
this.id = request.getSession() == null ? null : request.getSession().getId();
|
||||
this.locale = request.getAttribute("locale");
|
||||
this.lastRemoteAddress = request.getRemoteAddr();
|
||||
this.lastRemoteUserAgent = request.getHeader(USER_AGENT);
|
||||
this(
|
||||
request.getSession() == null ? null : request.getSession().getId(),
|
||||
request.getAttribute("locale"),
|
||||
request.getRemoteAddr(),
|
||||
request.getHeader(USER_AGENT)
|
||||
);
|
||||
}
|
||||
|
||||
public WebHttpRequestInfo(
|
||||
@@ -44,11 +49,22 @@ public class WebHttpRequestInfo {
|
||||
@Nullable Object locale,
|
||||
@Nullable String lastRemoteAddress,
|
||||
@Nullable String lastRemoteUserAgent
|
||||
) {
|
||||
this(id, locale, lastRemoteAddress, lastRemoteUserAgent, SessionType.WEB);
|
||||
}
|
||||
|
||||
public WebHttpRequestInfo(
|
||||
@Nullable String id,
|
||||
@Nullable Object locale,
|
||||
@Nullable String lastRemoteAddress,
|
||||
@Nullable String lastRemoteUserAgent,
|
||||
@NotNull SessionType sessionType
|
||||
) {
|
||||
this.id = id;
|
||||
this.locale = locale;
|
||||
this.lastRemoteAddress = lastRemoteAddress;
|
||||
this.lastRemoteUserAgent = lastRemoteUserAgent;
|
||||
this.sessionType = sessionType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -70,4 +86,9 @@ public class WebHttpRequestInfo {
|
||||
public String getLastRemoteUserAgent() {
|
||||
return lastRemoteUserAgent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SessionType getSessionType() {
|
||||
return sessionType;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-5
@@ -116,7 +116,8 @@ public class WebSession extends BaseWebSession
|
||||
CommonUtils.toString(requestInfo.getLocale()),
|
||||
application,
|
||||
sessionHandlers,
|
||||
requestInfo.getLastRemoteAddress()
|
||||
requestInfo.getLastRemoteAddress(),
|
||||
requestInfo.getSessionType()
|
||||
);
|
||||
updateSessionParameters(requestInfo);
|
||||
}
|
||||
@@ -128,7 +129,18 @@ public class WebSession extends BaseWebSession
|
||||
@NotNull Map<String, DBWSessionHandler<WebSession>> sessionHandlers,
|
||||
@Nullable String remoteAddr
|
||||
) throws DBException {
|
||||
super(id, application);
|
||||
this(id, locale, application, sessionHandlers, remoteAddr, SessionType.WEB);
|
||||
}
|
||||
|
||||
protected WebSession(
|
||||
@NotNull String id,
|
||||
@Nullable String locale,
|
||||
@NotNull ServletApplication application,
|
||||
@NotNull Map<String, DBWSessionHandler<WebSession>> sessionHandlers,
|
||||
@Nullable String remoteAddr,
|
||||
@NotNull SessionType sessionType
|
||||
) throws DBException {
|
||||
super(id, application, sessionType);
|
||||
if (CommonUtils.isEmpty(remoteAddr)) {
|
||||
throw new DBException("Remote address cannot be empty");
|
||||
}
|
||||
@@ -301,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();
|
||||
|
||||
@@ -492,7 +504,6 @@ public class WebSession extends BaseWebSession
|
||||
public void close() {
|
||||
try {
|
||||
resetNavigationModel();
|
||||
resetSessionCache();
|
||||
} catch (Throwable e) {
|
||||
log.error(e);
|
||||
}
|
||||
@@ -509,7 +520,6 @@ public class WebSession extends BaseWebSession
|
||||
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
|
||||
try {
|
||||
resetNavigationModel();
|
||||
resetSessionCache();
|
||||
} catch (Throwable e) {
|
||||
log.error(e);
|
||||
}
|
||||
@@ -609,11 +619,13 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebAsyncTaskInfo createAndRunAsyncTask(@NotNull String taskName, @NotNull WebAsyncTaskProcessor<?> runnable) {
|
||||
WebAsyncTaskInfo asyncTask = createAsyncTask(taskName);
|
||||
return runAsyncTask(asyncTask, runnable);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebAsyncTaskInfo runAsyncTask(@NotNull WebAsyncTaskInfo asyncTask, @NotNull WebAsyncTaskProcessor<?> runnable) {
|
||||
AbstractJob job = new AbstractCancelableJob(asyncTask.getName()) {
|
||||
@NotNull
|
||||
|
||||
+21
-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.
|
||||
@@ -17,6 +17,7 @@
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import io.cloudbeaver.WebSessionProjectImpl;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
@@ -26,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;
|
||||
@@ -45,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;
|
||||
@@ -199,12 +202,28 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
|
||||
@Override
|
||||
public boolean hasRealmPermission(@NotNull String permission) {
|
||||
if (getWebSession() instanceof WebSession webSession) {
|
||||
return webSession.getSessionPermissions().contains(permission);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealmFeature(@NotNull String feature) {
|
||||
return false;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
+6
-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.
|
||||
@@ -31,6 +31,8 @@ import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthCredentialsProfile;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderDescriptor;
|
||||
import org.jkiss.dbeaver.model.security.SMSubjectType;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.dbeaver.utils.SystemVariablesResolver;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
@@ -110,7 +112,9 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return cfg.getAttribute(WebRegistryConstant.ATTR_DESCRIPTION);
|
||||
return GeneralUtils.replaceVariables(
|
||||
cfg.getAttribute(WebRegistryConstant.ATTR_DESCRIPTION), SystemVariablesResolver.INSTANCE
|
||||
);
|
||||
}
|
||||
|
||||
public DBPImage getIcon() {
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
+16
-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.
|
||||
@@ -20,11 +20,14 @@ import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.model.app.ServletApplication;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPPlatform;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.auth.SMSession;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -55,8 +58,14 @@ public class ServerGlobalWorkspace extends BaseWorkspaceImpl {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public void initializeProjects() {
|
||||
protected SMSession acquireWorkspaceSession(@NotNull DBRProgressMonitor monitor) throws DBException {
|
||||
return new ServerGlobalWorkspaceSession(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeProjects() throws DBException {
|
||||
initializeWorkspaceSession();
|
||||
|
||||
// Load global project
|
||||
@@ -106,4 +115,9 @@ public class ServerGlobalWorkspace extends BaseWorkspaceImpl {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealmFeature(@NotNull String feature) {
|
||||
return application.getAppConfiguration().isFeatureEnabled(feature);
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.impl.app.LocalWorkspaceSession;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
|
||||
public class ServerGlobalWorkspaceSession extends LocalWorkspaceSession {
|
||||
|
||||
public ServerGlobalWorkspaceSession(@NotNull DBPWorkspace workspace) {
|
||||
super(workspace);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBSSecretController getSecretController() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -309,12 +309,6 @@ public class ServletAppUtils {
|
||||
log.trace("X-Origin header: " + origin);
|
||||
}
|
||||
}
|
||||
if (CommonUtils.isEmpty(origin)) {
|
||||
origin = request.getHeader(HEADER_REFERER);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Referer header: " + origin);
|
||||
}
|
||||
}
|
||||
String forwardedScheme = request.getHeader(HEADER_FORWARDED_SCHEME);
|
||||
if (CommonUtils.isEmpty(forwardedScheme)) {
|
||||
forwardedScheme = request.getHeader(HEADER_FORWARDED_PROTO);
|
||||
@@ -325,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);
|
||||
@@ -350,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()
|
||||
@@ -365,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;
|
||||
}
|
||||
|
||||
+15
-9
@@ -162,18 +162,24 @@ public class WebDataSourceUtils {
|
||||
}
|
||||
|
||||
|
||||
public static boolean disconnectDataSource(@NotNull WebSession webSession, @NotNull DBPDataSourceContainer dataSource) {
|
||||
public static boolean disconnectDataSource(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull DBPDataSourceContainer dataSource,
|
||||
boolean sendDisconnectEvent
|
||||
) {
|
||||
if (dataSource.isConnected()) {
|
||||
try {
|
||||
dataSource.disconnect(webSession.getProgressMonitor());
|
||||
webSession.addSessionEvent(
|
||||
new WSDataSourceDisconnectEvent(
|
||||
dataSource.getProject().getId(),
|
||||
dataSource.getId(),
|
||||
webSession.getSessionId(),
|
||||
webSession.getUserId()
|
||||
)
|
||||
);
|
||||
if (sendDisconnectEvent) {
|
||||
webSession.addSessionEvent(
|
||||
new WSDataSourceDisconnectEvent(
|
||||
dataSource.getProject().getId(),
|
||||
dataSource.getId(),
|
||||
WebEventUtils.getSmSessionId(webSession),
|
||||
webSession.getUserId()
|
||||
)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
} catch (DBException e) {
|
||||
log.error("Error closing connection", e);
|
||||
|
||||
@@ -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.0.2.qualifier
|
||||
Bundle-Release-Date: 20260406
|
||||
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.0.2-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.143.qualifier
|
||||
Bundle-Release-Date: 20260406
|
||||
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
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<resource name="drivers/h2_v2"/>
|
||||
<resource name="drivers/h2_v3"/>
|
||||
<resource name="drivers/sqlite/xerial"/>
|
||||
<resource name="drivers/libsql"/>
|
||||
<resource name="drivers/mssql/new"/>
|
||||
<resource name="drivers/trino"/>
|
||||
<resource name="drivers/kyuubi"/>
|
||||
@@ -36,8 +37,9 @@
|
||||
<bundle id="drivers.firebird" label="Firebird drivers"/>
|
||||
<bundle id="drivers.h2" label="H2 drivers"/>
|
||||
<bundle id="drivers.h2_v2" label="H2 v2 drivers"/>
|
||||
<bundle id="drivers.h2_v3" label="H2 v2.4+ drivers"/>
|
||||
<bundle id="drivers.h2_v3" label="H2 v3 drivers"/>
|
||||
<bundle id="drivers.sqlite.xerial" label="SQLite drivers"/>
|
||||
<bundle id="drivers.libsql" label="LibSQL drivers"/>
|
||||
<bundle id="drivers.mssql.new" label="SQL Server drivers"/>
|
||||
<bundle id="drivers.trino" label="Trino drivers"/>
|
||||
<bundle id="drivers.kyuubi" label="Apache Kyuubi drivers"/>
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
|
||||
<version>1.0.143-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.0.2.qualifier
|
||||
Bundle-Release-Date: 20260406
|
||||
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.0.2-SNAPSHOT</version>
|
||||
<version>26.1.2-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+13
-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.
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
import graphql.execution.instrumentation.Instrumentation;
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.auth.NoAuthCredentialsProvider;
|
||||
import io.cloudbeaver.model.CBWebServerConfig;
|
||||
@@ -29,6 +30,7 @@ import io.cloudbeaver.model.config.CBAppConfig;
|
||||
import io.cloudbeaver.model.config.CBServerConfig;
|
||||
import io.cloudbeaver.registry.WebDriverRegistry;
|
||||
import io.cloudbeaver.registry.WebServiceRegistry;
|
||||
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
|
||||
import io.cloudbeaver.server.jetty.CBJettyServer;
|
||||
import io.cloudbeaver.service.DBWServiceInitializer;
|
||||
import io.cloudbeaver.service.DBWServiceServerConfigurator;
|
||||
@@ -45,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;
|
||||
@@ -532,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
|
||||
@@ -589,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 {
|
||||
@@ -685,6 +687,11 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
return new CBSessionManager(this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GraphQLEndpoint createGraphQLEndpoint(@NotNull Instrumentation instrumentation) {
|
||||
return new GraphQLEndpoint(instrumentation);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebDriverRegistry getDriverRegistry() {
|
||||
return WebDriverRegistry.getInstance();
|
||||
@@ -736,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();
|
||||
|
||||
+11
-8
@@ -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.
|
||||
@@ -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;
|
||||
@@ -75,13 +75,16 @@ public class CBApplicationCE extends CBApplication<CBServerConfig> {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public RMController createResourceController(@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace) throws DBException {
|
||||
return LocalResourceController.builder(credentialsProvider, workspace, this::getSecurityController).build();
|
||||
public RMController createResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace
|
||||
) throws DBException {
|
||||
var lockManager = createLockManager();
|
||||
return LocalResourceController
|
||||
.builder(credentialsProvider, workspace, lockManager, this::getSecurityController)
|
||||
.build();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -98,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);
|
||||
|
||||
@@ -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.
|
||||
@@ -21,6 +21,7 @@ import io.cloudbeaver.server.jobs.SessionStateJob;
|
||||
import io.cloudbeaver.server.jobs.WebDataSourceMonitorJob;
|
||||
import io.cloudbeaver.server.jobs.WebSessionMonitorJob;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPRegistryListener;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
|
||||
@@ -51,7 +52,7 @@ public class CBPlatform extends BaseWebPlatform {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void initialize() {
|
||||
protected synchronized void initialize() throws DBException {
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.info("Initialize web platform " + getClass().getSimpleName());
|
||||
this.preferenceStore = new WebServerPreferenceStore(WebPlatformActivator.getInstance().getPreferences());
|
||||
|
||||
+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
|
||||
|
||||
+6
-2
@@ -24,6 +24,7 @@ import io.cloudbeaver.server.CBConstants;
|
||||
import io.cloudbeaver.server.filters.ServerConfigurationTimeLimitFilter;
|
||||
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
|
||||
import io.cloudbeaver.server.servlets.CBImageServlet;
|
||||
import io.cloudbeaver.server.servlets.CBManifestServlet;
|
||||
import io.cloudbeaver.server.servlets.CBStaticServlet;
|
||||
import io.cloudbeaver.server.servlets.WebStatusServlet;
|
||||
import io.cloudbeaver.server.websockets.CBEventsLongPollingServlet;
|
||||
@@ -119,12 +120,15 @@ public class CBJettyServer {
|
||||
ServletHolder imagesServletHolder = new ServletHolder("images", new CBImageServlet());
|
||||
servletContextHandler.addServlet(imagesServletHolder, serverConfiguration.getServicesURI() + "images/*");
|
||||
|
||||
ServletHolder manifestServletHolder = new ServletHolder("manifest", new CBManifestServlet(contentRootPath));
|
||||
servletContextHandler.addServlet(manifestServletHolder, "/manifest.webmanifest");
|
||||
|
||||
servletContextHandler.addServlet(new ServletHolder("status", new WebStatusServlet()), "/status");
|
||||
|
||||
ServletHolder eventsServletHolder = new ServletHolder("events", new CBEventsLongPollingServlet());
|
||||
servletContextHandler.addServlet(eventsServletHolder, serverConfiguration.getServicesURI() + "events/*");
|
||||
|
||||
GraphQLEndpoint endpoint = new GraphQLEndpoint(new ServerConfigurationTimeLimitFilter(application));
|
||||
GraphQLEndpoint endpoint = application.createGraphQLEndpoint(new ServerConfigurationTimeLimitFilter(application));
|
||||
application.addApplicationContextValue(GraphQL.class.getName(), endpoint.getGraphQL());
|
||||
String gqlServletPath = serverConfiguration.getServicesURI() + "gql/*";
|
||||
servletContextHandler.addServlet(
|
||||
@@ -257,4 +261,4 @@ public class CBJettyServer {
|
||||
cbSessionHandler.setSecureCookies(application.getServerConfiguration().isForceHttps());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.server.servlets;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import io.cloudbeaver.model.config.CBServerConfig;
|
||||
import io.cloudbeaver.server.CBApplication;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.annotation.WebServlet;
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.utils.MimeTypes;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
@WebServlet(urlPatterns = "/manifest.webmanifest")
|
||||
public class CBManifestServlet extends HttpServlet {
|
||||
|
||||
private static final Log log = Log.getLog(CBManifestServlet.class);
|
||||
private static final String MANIFEST_FILE = "manifest.webmanifest";
|
||||
|
||||
// Fix: Create Gson instance with HTML escaping disabled to prevent \u003d escaping
|
||||
private static final Gson GSON = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping() // This prevents escaping = to \u003d
|
||||
.create();
|
||||
|
||||
@NotNull
|
||||
private final Path contentRoot;
|
||||
|
||||
public CBManifestServlet(@NotNull Path contentRoot) {
|
||||
this.contentRoot = contentRoot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
Path manifestPath = contentRoot.resolve(MANIFEST_FILE).normalize();
|
||||
|
||||
if (!manifestPath.startsWith(contentRoot) || !Files.exists(manifestPath)) {
|
||||
log.error("Manifest file not found or path is invalid: " + manifestPath);
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Read the original manifest file
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try (InputStream fis = Files.newInputStream(manifestPath)) {
|
||||
IOUtils.copyStream(fis, baos);
|
||||
}
|
||||
String manifestContent = baos.toString(StandardCharsets.UTF_8);
|
||||
|
||||
// Parse the JSON and update dynamic fields
|
||||
CBServerConfig serverConfig = CBApplication.getInstance().getServerConfiguration();
|
||||
String rootURI = serverConfig.getRootURI();
|
||||
|
||||
// Ensure rootURI ends with / for proper scope and path handling
|
||||
String normalizedRootURI = rootURI;
|
||||
if (!normalizedRootURI.endsWith("/")) {
|
||||
normalizedRootURI += "/";
|
||||
}
|
||||
|
||||
// Use JsonParser to parse the existing manifest
|
||||
JsonObject manifest = JsonParser.parseString(manifestContent).getAsJsonObject();
|
||||
|
||||
// Update scope to match the rootURI
|
||||
manifest.addProperty("scope", normalizedRootURI);
|
||||
|
||||
// Update start_url and id to match the rootURI
|
||||
String startUrl = normalizedRootURI + "?source=pwa";
|
||||
manifest.addProperty("start_url", startUrl);
|
||||
manifest.addProperty("id", startUrl);
|
||||
|
||||
// Convert back to JSON string using the Gson instance with HTML escaping disabled
|
||||
String updatedManifest = GSON.toJson(manifest);
|
||||
byte[] manifestBytes = updatedManifest.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
// Set appropriate headers
|
||||
response.setHeader(HttpHeader.CONTENT_TYPE.toString(), "application/manifest+json");
|
||||
response.setHeader(HttpHeader.CACHE_CONTROL.toString(), "no-cache, no-store, must-revalidate");
|
||||
response.setHeader(HttpHeader.EXPIRES.toString(), "0");
|
||||
response.setContentLength(manifestBytes.length);
|
||||
|
||||
// Write the response
|
||||
response.getOutputStream().write(manifestBytes);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Error processing manifest file", e);
|
||||
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-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.
|
||||
@@ -24,9 +24,9 @@ import io.cloudbeaver.registry.WebSessionHandlerDescriptor;
|
||||
import io.cloudbeaver.server.CBApplication;
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import io.cloudbeaver.server.WebAppSessionManager;
|
||||
import io.cloudbeaver.server.events.WSWebUtils;
|
||||
import io.cloudbeaver.service.DBWSessionHandler;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
@@ -51,7 +51,7 @@ public class CBSessionManager implements WebAppSessionManager {
|
||||
private static final Log log = Log.getLog(CBSessionManager.class);
|
||||
|
||||
private final CBApplication<?> application;
|
||||
private final Map<String, BaseWebSession> sessionMap = new HashMap<>();
|
||||
protected final Map<String, BaseWebSession> sessionMap = new HashMap<>();
|
||||
|
||||
public CBSessionManager(CBApplication<?> application) {
|
||||
this.application = application;
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -527,4 +532,5 @@ public class CBSessionManager implements WebAppSessionManager {
|
||||
log.debug("> Expire session '" + session.getSessionId() + "'");
|
||||
session.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,17 +3,13 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Server
|
||||
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
|
||||
Bundle-Version: 26.0.2.qualifier
|
||||
Bundle-Release-Date: 20260406
|
||||
Bundle-Version: 26.1.2.qualifier
|
||||
Bundle-Release-Date: 20260706
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
|
||||
org.apache.commons.jexl,
|
||||
org.jkiss.utils;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.sql;visibility:=reexport,
|
||||
Require-Bundle: org.jkiss.dbeaver.model.sql;visibility:=reexport,
|
||||
org.jkiss.dbeaver.data.gis,
|
||||
org.jkiss.bundle.jetty.server;visibility:=reexport,
|
||||
com.google.gson;visibility:=reexport,
|
||||
org.jkiss.bundle.graphql;visibility:=reexport,
|
||||
org.jkiss.bundle.apache.dbcp,
|
||||
org.jkiss.dbeaver.net.ssh,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server</artifactId>
|
||||
<version>26.0.2-SNAPSHOT</version>
|
||||
<version>26.1.2-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -565,6 +565,8 @@ type ConnectionInfo {
|
||||
requiredAuth: String
|
||||
defaultCatalogName: String @since(version: "25.0.5")
|
||||
defaultSchemaName: String @since(version: "25.0.5")
|
||||
"Default user settings for connection"
|
||||
defaultUserPreferences: Object @since(version: "26.0.2")
|
||||
|
||||
"List of tools that can be used with this connection. Returns empty list if no tools are available"
|
||||
tools: [String!]! @since(version: "24.1.3")
|
||||
@@ -741,6 +743,8 @@ input ConnectionConfig {
|
||||
defaultCatalogName: String @since(version: "25.0.5") @deprecated(reason: "25.2.1 use expertPropertyValues instead")
|
||||
"Sets schema name for the connection"
|
||||
defaultSchemaName: String @since(version: "25.0.5") @deprecated(reason: "25.2.1 use expertPropertyValues instead")
|
||||
"Default user settings for connection"
|
||||
defaultUserPreferences: Object @since(version: "26.0.2")
|
||||
}
|
||||
|
||||
####################################################
|
||||
@@ -783,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 {
|
||||
@@ -846,6 +853,7 @@ extend type Mutation {
|
||||
"Sets to default navigator settings for connection. Resets all user navigator settings for this connection."
|
||||
clearConnectionNavigatorSettings(projectId: ID!, id: ID!): ConnectionInfo! @since(version: "25.3.2")
|
||||
|
||||
setObjectSettingsForDatasource(id: ID!, projectId:ID, settings: Object!): Object! @since(version: "25.3.5")
|
||||
#### Generic async functions
|
||||
|
||||
"Cancel async task by ID"
|
||||
|
||||
@@ -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!
|
||||
@@ -228,6 +244,10 @@ type DataTypeLogicalOperation {
|
||||
type SQLExecutionPlan {
|
||||
query: String!
|
||||
nodes: [SQLExecutionPlanNode!]!
|
||||
hasCost: Boolean! @since(version: "26.0.3")
|
||||
hasRows: Boolean! @since(version: "26.0.3")
|
||||
hasDuration: Boolean! @since(version: "26.0.3")
|
||||
durationMeasure: String @since(version: "26.0.3")
|
||||
}
|
||||
|
||||
type SQLExecutionPlanNode {
|
||||
@@ -240,6 +260,10 @@ type SQLExecutionPlanNode {
|
||||
condition: String
|
||||
description: String
|
||||
properties: [ObjectPropertyInfo!]!
|
||||
cost: Float @since(version: "26.0.3")
|
||||
rowCount: Float @since(version: "26.0.3")
|
||||
duration: Float @since(version: "26.0.3")
|
||||
percent: Float @since(version: "26.0.3")
|
||||
}
|
||||
|
||||
####################################################
|
||||
@@ -254,6 +278,11 @@ type SQLQueryGenerator {
|
||||
multiObject: Boolean!
|
||||
}
|
||||
|
||||
input SQLQueryGeneratorOptions {
|
||||
useFullyQualifiedNames: Boolean!
|
||||
compactSql: Boolean!
|
||||
}
|
||||
|
||||
####################################################
|
||||
# SQL Query info
|
||||
####################################################
|
||||
@@ -338,19 +367,12 @@ extend type Query {
|
||||
|
||||
"""
|
||||
Generates SQL query for the specified entity query generator.
|
||||
Available options:
|
||||
fullyQualifiedNames - Use fully qualified names for entities
|
||||
compactSQL - Use compact SQL format
|
||||
showComments - Show comments in generated SQL
|
||||
showPermissions - Show permissions in generated SQL
|
||||
showFullDdl - Show full DDL in generated SQL
|
||||
excludeAutoGeneratedColumn - Exclude auto-generated columns in generated SQL
|
||||
useCustomDataFormat - Use custom data format for generated SQL
|
||||
"""
|
||||
sqlGenerateEntityQuery(
|
||||
generatorId: String!,
|
||||
options: Object!,
|
||||
nodePathList: [String!]!
|
||||
options: Object @deprecated(reason: "use generatorOptions (26.0.3)")
|
||||
nodePathList: [String!]!,
|
||||
generatorOptions: SQLQueryGeneratorOptions
|
||||
): String!
|
||||
|
||||
"""
|
||||
@@ -364,6 +386,7 @@ extend type Query {
|
||||
generatorId: SQLResultSetGeneratorId!,
|
||||
resultsId: ID!,
|
||||
selectedRows: [SQLResultRow!]!,
|
||||
generatorOptions: SQLQueryGeneratorOptions
|
||||
): String!
|
||||
|
||||
"Parses SQL script and returns script info with queries start and end positions"
|
||||
@@ -444,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
-24
@@ -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,7 +19,8 @@ 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.*;
|
||||
@@ -176,30 +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);
|
||||
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
|
||||
|
||||
@@ -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,6 +19,7 @@ package io.cloudbeaver.server;
|
||||
import io.cloudbeaver.server.websockets.WebSocketPingPongJob;
|
||||
import org.eclipse.core.runtime.Plugin;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.DBConstants;
|
||||
import org.jkiss.dbeaver.model.app.DBACertificateStorage;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
@@ -35,7 +36,7 @@ public abstract class BaseWebPlatform extends BaseServletPlatform {
|
||||
private ServerGlobalWorkspace workspace;
|
||||
|
||||
@Override
|
||||
protected synchronized void initialize() {
|
||||
protected synchronized void initialize() throws DBException {
|
||||
// Register BC security provider
|
||||
SecurityProviderUtils.registerSecurityProvider();
|
||||
|
||||
|
||||
+13
-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.
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+53
-16
@@ -239,6 +239,14 @@ public class GraphQLEndpoint extends HttpServlet {
|
||||
@Nullable Map<String, Object> variables,
|
||||
@Nullable String operationName
|
||||
) throws IOException {
|
||||
|
||||
String userId = GraphQLLoggerUtil.getUserId(request);
|
||||
LocalDateTime startTime = LocalDateTime.now();
|
||||
|
||||
if (isQueryHandledBeforeExecution(request, response, variables, operationName, userId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> mapOfContext =
|
||||
Map.of(
|
||||
"request", request,
|
||||
@@ -254,8 +262,6 @@ public class GraphQLEndpoint extends HttpServlet {
|
||||
if (operationName != null) {
|
||||
contextBuilder.operationName(operationName);
|
||||
}
|
||||
String userId = GraphQLLoggerUtil.getUserId(request);
|
||||
LocalDateTime startTime = LocalDateTime.now();
|
||||
ExecutionInput executionInput = contextBuilder.build();
|
||||
ExecutionResult executionResult = null;
|
||||
Exception executionException = null;
|
||||
@@ -271,23 +277,54 @@ public class GraphQLEndpoint extends HttpServlet {
|
||||
} else if (executionException != null) {
|
||||
errorMessage = executionException.getMessage();
|
||||
}
|
||||
if (WebAppUtils.getWebApplication() instanceof ApiCallInterceptor apiCallInterceptor) {
|
||||
apiCallInterceptor.onApiCallEvent(
|
||||
request,
|
||||
variables,
|
||||
CommonUtils.notEmpty(operationName), userId, startTime,
|
||||
errorMessage,
|
||||
API_PROTOCOL
|
||||
);
|
||||
}
|
||||
notifyApiCallInterceptor(request, variables, operationName, userId, startTime, errorMessage);
|
||||
}
|
||||
|
||||
if (executionResult != null) {
|
||||
Map<String, Object> resJSON = executionResult.toSpecification();
|
||||
String resString = gson.toJson(resJSON);
|
||||
setDevelHeaders(request, response);
|
||||
response.setContentType(GraphQLConstants.CONTENT_TYPE_JSON_UTF8);
|
||||
response.getWriter().print(resString);
|
||||
writeExecutionResult(request, response, executionResult);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isQueryHandledBeforeExecution(
|
||||
@NotNull HttpServletRequest request,
|
||||
@NotNull HttpServletResponse response,
|
||||
@Nullable Map<String, Object> variables,
|
||||
@Nullable String operationName,
|
||||
@Nullable String userId
|
||||
) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void writeExecutionResult(
|
||||
@NotNull HttpServletRequest request,
|
||||
@NotNull HttpServletResponse response,
|
||||
@NotNull ExecutionResult executionResult
|
||||
) throws IOException {
|
||||
Map<String, Object> resJSON = executionResult.toSpecification();
|
||||
String resString = gson.toJson(resJSON);
|
||||
setDevelHeaders(request, response);
|
||||
response.setContentType(GraphQLConstants.CONTENT_TYPE_JSON_UTF8);
|
||||
response.getWriter().print(resString);
|
||||
}
|
||||
|
||||
protected void notifyApiCallInterceptor(
|
||||
@NotNull HttpServletRequest request,
|
||||
@Nullable Map<String, Object> variables,
|
||||
@Nullable String operationName,
|
||||
@Nullable String userId,
|
||||
@NotNull LocalDateTime startTime,
|
||||
@Nullable String errorMessage
|
||||
) {
|
||||
if (WebAppUtils.getWebApplication() instanceof ApiCallInterceptor apiCallInterceptor) {
|
||||
apiCallInterceptor.onApiCallEvent(
|
||||
request,
|
||||
variables,
|
||||
CommonUtils.notEmpty(operationName),
|
||||
userId,
|
||||
startTime,
|
||||
errorMessage,
|
||||
API_PROTOCOL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-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.
|
||||
@@ -27,6 +27,7 @@ import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNNode;
|
||||
import org.jkiss.dbeaver.model.qm.QMConstants;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
@@ -122,6 +123,9 @@ public class GraphQLLoggerUtil {
|
||||
if (value instanceof String sv && CommonUtils.isEmpty(sv)) {
|
||||
continue;
|
||||
}
|
||||
if (value instanceof DBNNode node) {
|
||||
value = node.getNodeUri();
|
||||
}
|
||||
|
||||
if (value != null && !isSimple(value.getClass())) {
|
||||
String stringValue;
|
||||
|
||||
+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()
|
||||
)
|
||||
);
|
||||
|
||||
+13
@@ -25,6 +25,8 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.websocket.WSUtils;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.registry.WSClientEventDescriptor;
|
||||
import org.jkiss.dbeaver.model.websocket.registry.WSEventRegistry;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
public class CBClientEventProcessor {
|
||||
@@ -50,6 +52,17 @@ public class CBClientEventProcessor {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle with custom handlers
|
||||
WSClientEventDescriptor ced = WSEventRegistry.getInstance().getClientEvent(clientEvent.getId());
|
||||
if (ced != null && ced.getHandler() != null) {
|
||||
try {
|
||||
ced.getHandler().handleEvent(webSession, clientEvent);
|
||||
} catch (Exception e) {
|
||||
log.error("Error handling event '" + clientEvent.getId() + "'", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (clientEvent.getId()) {
|
||||
case WSSubscribeOnTopicClientEvent.ID: {
|
||||
webSession.getEventsFilter().subscribeOnEventTopic(clientEvent.getTopicId());
|
||||
|
||||
+20
-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.
|
||||
@@ -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,24 +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 {
|
||||
if (env.getGraphQlContext().getBoolean(CloudbeaverCliConstants.CLI_MODE)) {
|
||||
return getSessionFromContextOrThrow(env);
|
||||
}
|
||||
return WebAppUtils.getWebApplication().getSessionManager().getWebSession(
|
||||
GraphQLEndpoint.getServletRequestOrThrow(env), getServletResponse(env));
|
||||
@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());
|
||||
}
|
||||
|
||||
@@ -129,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);
|
||||
@@ -137,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");
|
||||
}
|
||||
@@ -158,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);
|
||||
@@ -265,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);
|
||||
@@ -353,9 +356,13 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
}
|
||||
String sessionId = GraphQLLoggerUtil.getSmSessionId(request);
|
||||
String userId = GraphQLLoggerUtil.getUserId(request);
|
||||
String loggerMessage = GraphQLLoggerUtil.buildLoggerMessage(sessionId, userId, method, args);
|
||||
try {
|
||||
String loggerMessage = GraphQLLoggerUtil.buildLoggerMessage(sessionId, userId, method, args);
|
||||
|
||||
log.debug("API > " + method.getName() + loggerMessage);
|
||||
log.debug("API > " + method.getName() + loggerMessage);
|
||||
} catch (Throwable e) {
|
||||
log.error("Logging error", e);
|
||||
}
|
||||
|
||||
setLogContext(method, args);
|
||||
}
|
||||
|
||||
+16
-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.
|
||||
@@ -24,6 +24,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.rm.RMConstants;
|
||||
import org.jkiss.dbeaver.registry.DataSourceNavigatorSettings;
|
||||
import org.jkiss.dbeaver.registry.settings.ProductSettingDescriptor;
|
||||
@@ -107,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;
|
||||
@@ -218,6 +225,14 @@ public interface DBWServiceCore extends DBWService {
|
||||
@NotNull String id
|
||||
) throws DBWebException;
|
||||
|
||||
@NotNull
|
||||
Map<String, String> setObjectSettingsForDatasource(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String projectId,
|
||||
@NotNull String objectId,
|
||||
@NotNull Map<String, String> settings
|
||||
) throws DBException;
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Async tasks
|
||||
|
||||
|
||||
+14
-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()
|
||||
@@ -163,6 +170,12 @@ public class WebServiceBindingCore extends WebServiceBindingBase<DBWServiceCore>
|
||||
getArgumentVal(env, "id")
|
||||
)
|
||||
)
|
||||
.dataFetcher("setObjectSettingsForDatasource", env -> getService(env).setObjectSettingsForDatasource(
|
||||
getWebSession(env),
|
||||
getProjectReference(env),
|
||||
getArgumentVal(env, "id"),
|
||||
getArgumentVal(env, "settings")
|
||||
))
|
||||
|
||||
.dataFetcher("asyncTaskInfo", env -> getService(env).getAsyncTaskInfo(
|
||||
getWebSession(env),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user