mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
415
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7ded85ff0 | ||
|
|
6364581b5b | ||
|
|
bfcc7764d8 | ||
|
|
798adb4787 | ||
|
|
211ca65acb | ||
|
|
4c71bd6005 | ||
|
|
74a5877e6d | ||
|
|
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 | ||
|
|
446bd81e08 | ||
|
|
09863ea260 | ||
|
|
34b4556773 | ||
|
|
9b4c874e51 | ||
|
|
ddbc9789e7 | ||
|
|
810d0215cb | ||
|
|
3dadc17ff1 | ||
|
|
8c69985083 | ||
|
|
1d5b475482 | ||
|
|
b779b9d855 | ||
|
|
df676913a0 | ||
|
|
fab9d1dd43 | ||
|
|
36dfdc8b33 | ||
|
|
8b0a20cfd7 | ||
|
|
9553c30ee5 | ||
|
|
9c36651aa0 | ||
|
|
24a251cd5b | ||
|
|
a44ba89cd9 | ||
|
|
44077ad27a | ||
|
|
3ae74763ff | ||
|
|
45a63f70ee | ||
|
|
1bafe29e99 | ||
|
|
10ad601f8b | ||
|
|
db9f145609 | ||
|
|
d920227db0 | ||
|
|
874760f780 | ||
|
|
0e286f383f | ||
|
|
40385f3b1c | ||
|
|
4bc74883e0 | ||
|
|
33d1d97fb7 | ||
|
|
296e03ec74 | ||
|
|
2abdbce8ff | ||
|
|
051695451b | ||
|
|
5b7a7fc3d6 | ||
|
|
0ba013e7ad | ||
|
|
2d2f7476b4 | ||
|
|
23a0ad49b9 | ||
|
|
d21a5aac7f | ||
|
|
f9cd51db0f | ||
|
|
3687a645b6 | ||
|
|
c142996db0 | ||
|
|
36d2b46496 | ||
|
|
cb8b1cfc52 | ||
|
|
743afcaad7 | ||
|
|
52c78e1aee | ||
|
|
ff275c36b6 | ||
|
|
9a94639167 | ||
|
|
74d09ee4dc | ||
|
|
360aaed795 | ||
|
|
912ca3fdcd | ||
|
|
32bbfc3a37 | ||
|
|
1e05ad9eea | ||
|
|
4b516c770d | ||
|
|
4e1fed8177 | ||
|
|
4b30d1073b | ||
|
|
269128360d | ||
|
|
07db19635e | ||
|
|
41d9ac4296 | ||
|
|
a536a93ea3 | ||
|
|
aefdca73fa | ||
|
|
5d402d0c20 | ||
|
|
fcd1924853 | ||
|
|
05c0aafb53 | ||
|
|
159b1b8bd6 | ||
|
|
f03c08bde7 | ||
|
|
1b07a66192 | ||
|
|
ce1686e0eb | ||
|
|
091160a59a | ||
|
|
47d2377b39 | ||
|
|
8a3f6455ec | ||
|
|
801139e3ff | ||
|
|
457909dd82 | ||
|
|
9f7e086369 | ||
|
|
6e8a09a49e | ||
|
|
8d11f94ad6 | ||
|
|
3b0cd66232 | ||
|
|
a60b7d4496 | ||
|
|
07a3a069fb | ||
|
|
4511208cad | ||
|
|
1ab8d15b00 | ||
|
|
ae331effde | ||
|
|
6c88030106 | ||
|
|
5e53341ba1 | ||
|
|
9338539564 | ||
|
|
af1aa83d8b | ||
|
|
32671ff239 | ||
|
|
f7c33a7f7e | ||
|
|
d70e4976e5 | ||
|
|
a0e6154a08 | ||
|
|
1b6e729506 | ||
|
|
3adb898f5a | ||
|
|
c886ee7bcd | ||
|
|
a5a9700ab9 | ||
|
|
2c38e75a99 | ||
|
|
12bc5ba5dc | ||
|
|
2719f53655 | ||
|
|
a2d336166b | ||
|
|
06da0705d2 | ||
|
|
058a341815 | ||
|
|
f5b0aae8bd | ||
|
|
789760aa9e | ||
|
|
fe77133226 | ||
|
|
8e237d1bbc | ||
|
|
0f3b8033df | ||
|
|
586d4154ab | ||
|
|
e0fe55c3bf | ||
|
|
9c406a4e1e | ||
|
|
05bf79e59c | ||
|
|
d013ece4f6 | ||
|
|
b0205e34c7 | ||
|
|
0b41ae89e8 | ||
|
|
b318c262b9 | ||
|
|
547831d060 | ||
|
|
93af9a8719 | ||
|
|
19125fed5e | ||
|
|
ea5fb47a56 | ||
|
|
89b4d1f970 | ||
|
|
0bd89236d2 | ||
|
|
abe0f6fb7d | ||
|
|
0b2b851772 | ||
|
|
dcb8e536b8 | ||
|
|
7881551821 | ||
|
|
cdc94dce5f | ||
|
|
70b67ce6f4 | ||
|
|
e570a05175 | ||
|
|
f82598ad66 | ||
|
|
d3b0b96bae | ||
|
|
ec9c2b895b | ||
|
|
be8744232b | ||
|
|
4088e7998e | ||
|
|
c540728fee | ||
|
|
84f0903fc7 | ||
|
|
89152a09a1 | ||
|
|
0bfdcf0886 | ||
|
|
d2d3ada58a | ||
|
|
a62478a574 | ||
|
|
980473918e | ||
|
|
26712e7bca | ||
|
|
9597e99bc9 | ||
|
|
7f92abd33e | ||
|
|
ad686faca8 | ||
|
|
c02411085e | ||
|
|
90fcbe6b7f | ||
|
|
1deffb984b | ||
|
|
bd52ee6583 | ||
|
|
013ad81581 | ||
|
|
4843002e0d | ||
|
|
9a05d0bf7f | ||
|
|
ecaa4934b1 | ||
|
|
8d89bc25ba | ||
|
|
40581e746b | ||
|
|
a9911d9297 | ||
|
|
a8d437dce6 | ||
|
|
bacd1bff91 | ||
|
|
1d9b0a2943 | ||
|
|
aa6a81428c | ||
|
|
b91ca06359 | ||
|
|
244479cd06 | ||
|
|
1c56d55462 | ||
|
|
be78d2fe92 | ||
|
|
4178826ff4 | ||
|
|
27ef1fdf73 | ||
|
|
5b0bedcf5e | ||
|
|
821805eba8 | ||
|
|
6531e45357 | ||
|
|
598fb92eaf | ||
|
|
71dda4aa45 | ||
|
|
cc1861c614 | ||
|
|
67c15af74d | ||
|
|
79194b2b50 | ||
|
|
673b824cb5 | ||
|
|
4e7555c419 | ||
|
|
9d0e75dba8 | ||
|
|
44cc1badf4 | ||
|
|
fdb4894ae4 | ||
|
|
a836412ee4 | ||
|
|
38b0246240 | ||
|
|
0bf769a662 | ||
|
|
0349bcee4c | ||
|
|
44b89296d2 | ||
|
|
b75d802883 | ||
|
|
9652520073 | ||
|
|
7e7d6634de | ||
|
|
c12561965f | ||
|
|
7a12ad4b91 | ||
|
|
c39de17df8 | ||
|
|
147edde923 | ||
|
|
aeaa864f57 | ||
|
|
bcda3e4312 | ||
|
|
72a007ceb6 | ||
|
|
e08ba6845e | ||
|
|
8f1fc1f36e | ||
|
|
b5cfcaef31 | ||
|
|
e9b1079f85 | ||
|
|
326514f914 | ||
|
|
67489a03b3 | ||
|
|
4ea0baf79b | ||
|
|
939f4d046a | ||
|
|
cd3cc2e92f | ||
|
|
7a8c4a141f | ||
|
|
5db7a0de5e | ||
|
|
15c61239ec | ||
|
|
164d155e7d | ||
|
|
821c9c8e56 | ||
|
|
b58e01794d | ||
|
|
fbc5f2d3ce | ||
|
|
ae9e87950a | ||
|
|
58db928e34 | ||
|
|
583519d9a7 | ||
|
|
a96862a444 | ||
|
|
b9d115ec33 | ||
|
|
78daa21c87 | ||
|
|
50090a3035 | ||
|
|
fc9b1d388f | ||
|
|
0c053efd68 | ||
|
|
abf07b8c5b | ||
|
|
c119c93a7d | ||
|
|
c2045091d0 | ||
|
|
9483df70ac | ||
|
|
d774540803 | ||
|
|
736442be35 | ||
|
|
8ee58e0ce8 | ||
|
|
163b96bcae | ||
|
|
6c915eedc3 | ||
|
|
b9d55bc121 | ||
|
|
cb23fda311 | ||
|
|
dc97f315ab | ||
|
|
5d2c6dc062 | ||
|
|
435aac2860 | ||
|
|
e0cdf745ad | ||
|
|
5e11a1d0b1 | ||
|
|
83125bf8b5 | ||
|
|
3b435459a2 | ||
|
|
5dad35b73d | ||
|
|
e801f85e7c | ||
|
|
a39af0fd1b | ||
|
|
9ec227ddb1 | ||
|
|
749c116743 | ||
|
|
d6221e3d33 | ||
|
|
6316ab0cbb | ||
|
|
e2e18e01e9 | ||
|
|
d042a18d46 | ||
|
|
88d7a8d6eb | ||
|
|
33bf82970f | ||
|
|
ea552d613b | ||
|
|
0ae6a648d8 | ||
|
|
a674e11960 | ||
|
|
d1065037b9 | ||
|
|
dd3ab9d5a1 | ||
|
|
7b94dc60ab | ||
|
|
25089a9a70 | ||
|
|
d645365869 | ||
|
|
eb4f11aa0a | ||
|
|
ec3ba12699 | ||
|
|
a75a7ea5a4 | ||
|
|
3ca904924b | ||
|
|
46a0adb682 | ||
|
|
b9b4dc040c | ||
|
|
5973bd708f | ||
|
|
698ee3cba1 | ||
|
|
9394667673 | ||
|
|
37877ccf66 | ||
|
|
dc28d68208 | ||
|
|
b55398a969 | ||
|
|
6584a2252e | ||
|
|
411def42f9 | ||
|
|
8b471138db | ||
|
|
e2a5e1fca2 | ||
|
|
9c2bc100b1 | ||
|
|
8b08927af8 | ||
|
|
7ea627a24f | ||
|
|
dc5f86c40c | ||
|
|
24b7b6021d | ||
|
|
2f64827ad4 | ||
|
|
e443a88ae7 | ||
|
|
f46857fe6d | ||
|
|
11f8568238 | ||
|
|
b81c7732d1 | ||
|
|
3592e77851 | ||
|
|
4b0e8b5e91 | ||
|
|
802a3e69fe | ||
|
|
d409aad739 | ||
|
|
9b01659a6f | ||
|
|
354f13d0d4 | ||
|
|
e39b1b1ee2 | ||
|
|
f818fb1c85 | ||
|
|
6253d3e543 | ||
|
|
c7a6cc967e | ||
|
|
c3ac257fdd | ||
|
|
c9b1278788 | ||
|
|
58d316f396 | ||
|
|
37e5e1865c | ||
|
|
8cfe6a98e5 | ||
|
|
20026d1d30 | ||
|
|
7c72232215 | ||
|
|
64118de61c | ||
|
|
17331e6a8f | ||
|
|
46079dea6a |
@@ -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"
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -15,11 +15,11 @@
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"streetsidesoftware.code-spell-checker-russian",
|
||||
"syler.sass-indented",
|
||||
"VisualStudioExptTeam.vscodeintellicode",
|
||||
"github.copilot-chat",
|
||||
"yzhang.markdown-all-in-one",
|
||||
"GraphQL.vscode-graphql-syntax",
|
||||
"mrmlnc.vscode-json5",
|
||||
"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"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/cloudbeaver-logo.png" align="right" width="250"/>
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/cloudbeaver-logo.png" alt="CloudBeaver logo" align="right" width="250"/>
|
||||
|
||||
# CloudBeaver Community
|
||||
|
||||
@@ -25,57 +25,175 @@ You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
|
||||
|
||||
## Changelog
|
||||
|
||||
### 25.2.4 2025-11-03
|
||||
- A confirmation prompt has been added for potentially dangerous queries in the SQL Editor to help prevent unintended execution. You can customize this behavior in Preferences > SQL Editor;
|
||||
- The value panel in the Data Editor has been redesigned to provide users with more space for viewing and editing data. Tab buttons for different data type visualizations are now aligned on a single level, creating a cleaner layout;
|
||||
- Permission configuration for connections was simplified, providing a clearer appearance and improved performance.
|
||||
|
||||
### 25.2.3 2025-10-20
|
||||
- Added visualization for column reordering in the Data Editor;
|
||||
- Fixed the critical issue with duplicating the execution of queries executed by a shortcut in the SQL Editor;
|
||||
- Fixed the critical vulnerability (CVE-2025-61927) in the Happy DOM library. The library was updated to version 20.0.2.
|
||||
|
||||
### 25.2.2 2025-10-06
|
||||
- Replaced true/false text with intuitive checkboxes for boolean values in the metadata editor, making editing easier and faster;
|
||||
- Keyboard navigation was improved across the application. Additional shortcuts are available in the main info panel (top-right corner). To help you discover them faster, relevant keyboard shortcuts are also shown directly in the tooltips of Data Editor buttons;
|
||||
- Fixed an issue in the Data Editor where resizing one column in a wide table would cause previously resized columns to revert to their original width. Users can resize multiple columns as intended.
|
||||
|
||||
### 25.2.1 2025-09-22
|
||||
- Autocomplete values in SQL Editor are ordered alphabetically to simplify object search;
|
||||
- "Show all databases" setting was added on the connection page for MySQL and MariaDB;
|
||||
- Different bug fixes and enhancements have been made.
|
||||
|
||||
### 25.2.0 2025-09-01
|
||||
### Changes since 25.1.0:
|
||||
- Administration:
|
||||
- Added support for multiple server URLs to accommodate different access policies for internal and external users, improving flexibility in network-restricted environments. To set it up, use the Allowed Server URLs field in the Server configuration within the Administration section;
|
||||
- Added the Force HTTPS mode setting in Server Configuration to enforce redirecting from HTTP to HTTPS. This setting helps avoid a potential man-in-the-middle attack. This setting is turned off by default. Please remember to configure your proxy before enabling;
|
||||
- Added the CLOUDBEAVER_BIND_SESSION_TO_IP option to improve session security by linking user sessions to their IP address. It is disabled by default. When enabled, this helps protect against certain types of session hijacking attacks, where an attacker could try to take over a user’s session. Note: Users will be logged out automatically on the IP address change (switching networks or using mobile data);
|
||||
- A password confirmation field has been added for administrators in the Easy Config section to prevent accidental misconfigurations.
|
||||
- SQL Editor:
|
||||
- Changed the default engine used for autocompletion in the SQL Editor. This Semantic engine offers improved suggestions for database objects, keywords, and functions. You can switch back to the Legacy engine in Preferences > SQL Editor;
|
||||
- A “Clear” button was added to the output panel.
|
||||
### 26.0.5 2026-05-18
|
||||
- Authorization:
|
||||
- Added HTTP header authentication support for GraphQL queries. Users can provide API tokens via Authorization or X-Api-Key headers, allowing external data tools to authenticate without modifying request bodies.
|
||||
- Data Editor:
|
||||
- Column descriptions were added in the Data Editor under column names to provide more metadata context. You can disable this in Preferences > Data Viewer;
|
||||
- Added an ability to review the script before execution when users edit tables without primary keys.
|
||||
- Added a DDL generation option to the Data Editor and Result Set. Users can view table schemas directly while editing or reviewing records.
|
||||
- Fixed the copying of Boolean values in the clipboard in the Data Editor.
|
||||
- SQL Editor:
|
||||
- Updated the SQL Editor to use real tab characters for indentation. The application displayed tabs as four-space gaps with a distinct visual marker.
|
||||
- Added an ability to view and edit scripts without connecting to the database.
|
||||
- Added more settings to the Generated SQL window, including the ability to select fully qualified names for query or use compact SQL formatting.
|
||||
- Added the ability to open generated queries right from the Generate SQL window.
|
||||
- General:
|
||||
- Added a new welcome screen for a freshly opened application. This screen contains shortcuts to create a new connection, open SQL Editor, or documentation;
|
||||
- Added a search panel for SQL Editor and Value panel: press Ctrl/Cmd + F to open a panel that allows searching and replacing by keyword or regular expression;
|
||||
- Changed the data transfer mechanism to avoid intermediate file creation. The parameter dataExportFileSizeLimit was removed from the server configuration as deprecated;
|
||||
- The CloudBeaver default theme matches the device theme by default now. You can change this behavior in user preferences under the Theme section;
|
||||
- Added the option to display tabs across multiple rows, allowing you to see all tabs without scrolling. You can enable this in Preferences > Interface;
|
||||
- Improved dark theme accessibility by enhancing color contrast across the app for better readability and compliance with accessibility standards;
|
||||
- The database navigator now automatically hides empty folders in shared projects, reducing visual clutter and speeding up the process of finding active connections.
|
||||
- Databases and drivers:
|
||||
- Clickhouse: fixed the presentation of tuples and map data types in the Data Editor;
|
||||
- Databend database support has been added (thanks to @hantmac);
|
||||
- DuckDB: driver has been updated to version 1.3;
|
||||
- MySQL: Improved performance when retrieving foreign keys metadata;
|
||||
- PostgreSQL: fixed misplaced comment for table DDL generation.
|
||||
- Updated the manifest.webmanifest file to dynamically generate the environment variable according to the CLOUDBEAVER_ROOT_URI. (thanks to @houssemexo26 )
|
||||
- Databases:
|
||||
- PostgreSQL:
|
||||
- Updated driver to version 42.7.11.
|
||||
- Fixed missing INOUT parameters in CHECK generation for procedures and functions.
|
||||
- Fixed handling of 24:xx values in time columns.
|
||||
- Snowflake: Updated driver to version 4.1.0.
|
||||
- SQL Server: Fixed the issue with connecting to the database after the Java update.
|
||||
- Security:
|
||||
- Added an administrative setting to restrict SSH tunneling capabilities. Administrators can now limit tunnel configuration to authorized users, reducing the risk of unauthorized network access.
|
||||
- Fixed the high vulnerability (CVE-2026-42035) in the axios library. The library was updated to version 1.15.1.
|
||||
- Fixed the medium vulnerability (CVE-2026-41305) in the postcss library. The library was updated to version 8.5.14.
|
||||
|
||||
## Contribution
|
||||
As a community-driven open-source project, we warmly welcome contributions through GitHub pull requests.
|
||||
### 26.0.4 2026-05-04
|
||||
|
||||
- Data Editor:
|
||||
- Added the ability to copy-paste multiple cells at once. Pasted values will be distributed across separate cells, not put in a single cell.
|
||||
- Data Editor started to keep the state of column configurations, such as filters, sorting, and ordering, after the reconnect, page refresh, and re-login.
|
||||
- SQL Editor:
|
||||
- Updated the SQL Editor to use real tab characters for indentation. The application displayed tabs as four-space gaps with a distinct visual marker.
|
||||
- Added the ability to view and edit scripts without connecting to the database.
|
||||
- Added more settings to the Generated SQL window, including the ability to select fully qualified names for query or use compact SQL formatting.
|
||||
- Added the ability to open in the SQL Editor generated queries right from the Generate SQL window.
|
||||
- Accessibility:
|
||||
- Fixed contrast for elements across different application parts in the light and dark themes to meet WCAG requirements.
|
||||
- General:
|
||||
- Removed the "Folders" setting from the available settings for the Navigator Tree.
|
||||
- Databases:
|
||||
- DuckDB: Fixed map rendering for GEOMETRY types with CRS parameters.
|
||||
- Firebird: Expanded database-specific SQL dialect coverage for the SQL Editor. Added more keywords, built-in functions, and SQL generators.
|
||||
- MySQL: Fixed query boundaries recognition in the SQL Editor for scripts with the DELIMITER keyword.
|
||||
- SQL Server: Fixed DDL generation for external tables.
|
||||
- Security:
|
||||
- Updated session handling to prompt the application to generate a new session identifier for each login.
|
||||
- Fixed the high vulnerability (CVE-2026-3505) in the bcpg-jdk18on library. The library was updated to version 1.84.0.
|
||||
|
||||
### 26.0.3 2026-04-20
|
||||
|
||||
- Data Editor:
|
||||
- Added key column mark for tables with unique keys in the data grid.
|
||||
- Added the ability to paste a copied cell into multiple cells and to set multiple cells to NULL.
|
||||
- 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.
|
||||
- Accessibility:
|
||||
- Added the Skip to content option for quick keyboard access to the Navigator Tree, editors, and shortcuts to improve application accessibility.
|
||||
- General:
|
||||
- SQL Editor: Fixed keyword autocompletion for partially typed keywords. There are no extra letters for them after completion.
|
||||
- Added support for custom startup arguments when running CloudBeaver in a container. Administrators can specify configuration file locations to adapt the application to their environment. (thanks to @MalteHei)
|
||||
- Fixed custom driver properties display for the connection page.
|
||||
- Fixed data export in SQL format for values containing single quotes.
|
||||
- Databases:
|
||||
- ClickHouse: Fixed the display of Array(JSON) types in the data grid.
|
||||
- Added support for the "prompt=false" connection property for DB2 for IBM i to turn off interactive prompts and prevent related errors.
|
||||
- Security:
|
||||
- Fixed the critical vulnerability (CVE-2025-62718) in the axios library. The library was updated to version 1.15.0.
|
||||
- Fixed the high vulnerability (CVE-2026-4800) in the lodash library. The library was updated to version 4.18.0.
|
||||
- Fixed the high vulnerability (CVE-2026-39363) in the vite library. The library was updated to version 7.3.2.
|
||||
|
||||
### 26.0.2 2026-04-06
|
||||
|
||||
- UI improvements:
|
||||
- Marked icons for non-saved (temporary) scripts with the hourglass symbol to add visual distinction from the saved ones.
|
||||
- Added the tooltip about quoting rules for the Bind variables parameters dialog.
|
||||
- Improved keyboard navigation for context menu and buttons for Data Editor, SQL Editor, and Navigator tree.
|
||||
- Authentication:
|
||||
- Fixed the bug with the authentication form. Now the same password is required for the password change operation.
|
||||
- Fixed the display of authentication fields for SQL Server with Active Directory - MSI. The unnecessary Password field was removed.
|
||||
- Databases and drivers:
|
||||
- DuckDB: Fixed geometry type display for database versions > 1.5. (thanks @EastLord).
|
||||
- H2 database:
|
||||
- Added new embedded driver version 2.4.2.
|
||||
- Updated server driver to the version 2.4.2.
|
||||
- Security:
|
||||
- Fixed the high vulnerability (CVE-2026-33671) in the picomatch library. The library was updated to version 2.3.2.
|
||||
- Fixed the high vulnerability (CVE-2026-33943) in the happy-dom library. The library was updated to version 20.8.8.
|
||||
- Fixed the high vulnerability (CVE-2026-33228) in the flatted library. The library was updated to version 3.4.2.
|
||||
|
||||
### 26.0.1 2026-03-23
|
||||
|
||||
- Data Editor:
|
||||
- Added the Find and Replace functionality for the Data Editor with the ability to find data by matching case, whole word, or using regular expressions.
|
||||
- Added support for adding, duplicating, deleting, or setting values to NULL in multiple highlighted rows via the Data Editor context menu.
|
||||
- SQL query generation in the Data Editor is now available for columns containing large object data.
|
||||
- Improved column drag-and-drop behavior in the Data Editor. Users could drag columns from any area within the header and drop them onto any part of the target column or table body.
|
||||
- SQL Editor:
|
||||
- Added fuzzy search to SQL Editor auto-completion for object names. The feature allowed users to find tables and containers even with typos or incorrect letter ordering by matching strings that start with, contain, or resemble the input.
|
||||
- Fixed data editing for SQL scripts with variables.
|
||||
- General:
|
||||
- Added a close button to the right tab when it is the only open tab (ex. Scripts). Users no longer needed to use the main menu to close a single tab.
|
||||
- Replaced the generic "Something went wrong" message with a specific network error notification when the application cannot connect to the server.
|
||||
- Null or empty values can now be assigned to the Driver properties for all drivers.
|
||||
- Fixed an issue where re-selecting an existing SSH key file during connection edits caused problems.
|
||||
- Fixed the ability to switch tabs in the Data editor using the keyboard.
|
||||
- Fixed error notification spelling. Thanks to @Malcolm-B-Breaks.
|
||||
- ClickHouse:
|
||||
- Updated driver version to 0.9.7.
|
||||
- Fixed IP address display in the Data Editor by removing the leading slash.
|
||||
- PostgreSQL: Fixed an error when connecting via URL if the database name contains a hyphen.
|
||||
- Security:
|
||||
- Fixed the high vulnerability (CVE-2025-45141) in the tar library. The library was updated to version 7.5.11.
|
||||
- Fixed the high vulnerability (CVE-2026-27148) in the storybook library. The library was updated to version 10.2.10.
|
||||
- Fixed the high vulnerability (CVE-2026-27606) in the rollup library. The library was updated to version 4.59.0.
|
||||
- Fixed the high vulnerability (CVE-2026-27904) in the minimatch library. The library was updated to version 3.1.3.
|
||||
- Fixed the high vulnerability (CVE-2026-27959) in the koa library. The library was updated to version 2.16.4.
|
||||
- Fixed the high vulnerability (CVE-2026-32141) in the flatted library. The library was updated to version 4.4.0.
|
||||
|
||||
### 26.0.0 2026-03-02
|
||||
|
||||
### Changes since 25.3.0:
|
||||
|
||||
- Security:
|
||||
- Enforced complete logout and screen data clearance upon session expiration;
|
||||
- Fixed the high vulnerability (CVE-2026-25639) in the axios library. The library was updated to version 1.13.5.
|
||||
- Administration:
|
||||
- Added support for mapping users to CloudBeaver teams based on LDAP memberOf group membership;
|
||||
- Change the User list settings in the Administration part to show both active and inactive users by default.
|
||||
- SQL Editor:
|
||||
- Added support for parameters and variables in queries. This feature allows queries to be reused by changing parameters at execution time. Enabled by default and configurable in personal preferences;
|
||||
- Added SQL preview to the Bind parameters/variables dialog to review queries with changed values on the fly;
|
||||
- Enabled Tab key for autocompletion in the SQL Editor alongside the Enter key;
|
||||
- Added a new setting in the SQL Editor to highlight spaces, tabs, and other whitespace characters to help users read, debug, and maintain their scripts. It is turned off by default and can be configured in personal preferences;
|
||||
- Dangerous query confirmation is now shown for all DROP statements, not just for tables.
|
||||
- Data Editor:
|
||||
- Added ability to automatically generate INSERT, SELECT, DELETE, and UPDATE statements for the selected values;
|
||||
- Added undo and redo functionality for cell edits, row operations, and other data modifications. Retains the last 50 actions across the Data Editor, result sets, and related panels;
|
||||
- Added "Use local formatting" setting. Users can choose how to display numbers and dates: using the OS locale, a custom locale, or keeping values unformatted. This formatting applies only to displayed values. Data in the database remains unchanged;
|
||||
- Added column pinning to keep key columns (e.g., IDs, names) visible while horizontal scrolling through wide tables;
|
||||
- Added status indicator icon in the top-left corner with tooltips explaining table editability. Indicates presence of primary keys, read-only connection settings, or read-only columns;
|
||||
- Added shortcut Ctrl/Cmd + . to cancel operations in Data Editor;
|
||||
- Fixed application freeze in canceling fetch size requests for large tables.
|
||||
- Navigator tree:
|
||||
- Added the ability to duplicate connection configuration in the project navigation tree. The "Clone connection" feature is available in the context menu;
|
||||
- Added the ability for users to configure the Simple or Advanced view in the Navigation tree for all connections or for each connection separately;
|
||||
- Added the ability to show table objects, such as columns or keys, in the Navigation tree. The setting is disabled by default and can be turned on in the Navigator settings panel;
|
||||
- Added the ability to rename connections via context menu in the Navigation Tree;
|
||||
- Added Connection Info tab to display basic information about the current connection for all users.
|
||||
- General:
|
||||
- Added support for long polling as a fallback when WebSockets are unavailable or blocked. Ensures reliable communication for metadata updates and SQL execution;
|
||||
- Extended browser support to versions up to three years old;
|
||||
- Redesigned the connection configuration page. Reorganized form fields and sections to provide more input space and reduce visual clutter;
|
||||
- Expanded pointer target areas for icons in the Navigator, editors, and tabs according to the accessibility standards;
|
||||
- Fixed a keyboard navigation issue for panels to keep the focus inside;
|
||||
- Renamed "Database Native" authentication type to "Username/password" in the connection dialog;
|
||||
- Fixed issue where the missing pg_dump utility caused errors during initialization or deployment updates when PostgreSQL was selected as the inner database.
|
||||
- Databases:
|
||||
- ClickHouse:
|
||||
- Updated driver to version 0.9.5
|
||||
- Added spatial data support
|
||||
- Fixed an issue with displaying arrays of UUID, IPv4/IPv6, and Map types
|
||||
- Fixed JSON data type reading
|
||||
- Resolved an issue with CTE expressions
|
||||
- DuckDB:
|
||||
- Updated driver to version 1.4.4.0;
|
||||
- Added support for the dollar-quoted string syntax for the SQL Editor
|
||||
- Oracle: Added a new "Set Username to OS_USER" option in the Misc section of Oracle connection settings. Automatically uses the current database username as the operating system user identifier in session metadata when enabled;
|
||||
- PostgreSQL: Added DDL display support for PostgreSQL policies.
|
||||
|
||||
[We are happy to reward](https://dbeaver.com/help-dbeaver/) our most active contributors every major sprint.
|
||||
The most significant contribution to our code for the major release 25.2.0 was made by:
|
||||
1. [hantmac](https://github.com/hantmac) - added support for Databend in CloudBeaver Community Edition.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,13 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Model
|
||||
Bundle-SymbolicName: io.cloudbeaver.model.cli;singleton:=true
|
||||
Bundle-Version: 1.0.11.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.model.cli;visibility:=reexport
|
||||
Export-Package: io.cloudbeaver.model.cli
|
||||
Automatic-Module-Name: io.cloudbeaver.model.cli
|
||||
Import-Package: org.slf4j
|
||||
@@ -0,0 +1,5 @@
|
||||
source.. = src/
|
||||
output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
plugin.xml
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?eclipse version="3.2"?>
|
||||
|
||||
<plugin>
|
||||
|
||||
</plugin>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>io.cloudbeaver</groupId>
|
||||
<artifactId>bundles</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model.cli</artifactId>
|
||||
<version>1.0.11-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
+25
-15
@@ -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.
|
||||
@@ -18,29 +18,36 @@ package io.cloudbeaver.model.cli;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.cli.*;
|
||||
import org.jkiss.dbeaver.model.cli.model.CommandLineAuthenticator;
|
||||
import 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;
|
||||
|
||||
@Nullable
|
||||
private final CommandLineAuthenticator authenticator;
|
||||
|
||||
public CloudBeaverCommandLine(@Nullable CommandLineAuthenticator authenticator) {
|
||||
public CloudBeaverCommandLine() {
|
||||
super();
|
||||
this.authenticator = authenticator;
|
||||
this.mixin = new CloudBeaverMixin();
|
||||
}
|
||||
|
||||
public CloudBeaverCommandLine(
|
||||
@NotNull CloudBeaverMixin mixin
|
||||
) {
|
||||
super();
|
||||
this.mixin = mixin;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CloudBeaverTopLevelCommand createTopLevelCommand(
|
||||
@Nullable ApplicationInstanceController applicationInstanceController,
|
||||
@NotNull CommandLineContext context,
|
||||
@NotNull CLIContextImpl context,
|
||||
@NotNull CLIRunMeta runMeta
|
||||
) {
|
||||
if (authenticator != null) {
|
||||
context.setContextParameter(CLIConstants.CONTEXT_PARAM_AUTHENTICATOR, authenticator);
|
||||
}
|
||||
return new CloudBeaverTopLevelCommand(applicationInstanceController, context, runMeta);
|
||||
}
|
||||
|
||||
@@ -48,9 +55,12 @@ public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationIn
|
||||
@Override
|
||||
protected CommandLine initCommandLine(
|
||||
@Nullable ApplicationInstanceController applicationInstanceController,
|
||||
@NotNull CommandLineContext context,
|
||||
@NotNull CLIRunMeta runMeta
|
||||
@NotNull CLIContextImpl context,
|
||||
@NotNull CLIRunMeta runMeta,
|
||||
@NotNull List<CLICommandDescriptor> commandsToExecute
|
||||
) {
|
||||
return super.initCommandLine(applicationInstanceController, context, runMeta);
|
||||
var cmd = super.initCommandLine(applicationInstanceController, context, runMeta, commandsToExecute);
|
||||
cmd.addMixin("cloudbeaver", mixin);
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -29,10 +29,10 @@ public class CloudBeaverInstanceServer extends ApplicationInstanceServer<Applica
|
||||
|
||||
public CloudBeaverInstanceServer() throws IOException {
|
||||
super(ApplicationInstanceController.class);
|
||||
this.commandLine = new CloudBeaverCommandLine(null);
|
||||
this.commandLine = new CloudBeaverCommandLine();
|
||||
}
|
||||
|
||||
protected CloudBeaverInstanceServer(@NotNull CloudBeaverCommandLine commandLine) throws IOException {
|
||||
public CloudBeaverInstanceServer(@NotNull CloudBeaverCommandLine commandLine) throws IOException {
|
||||
super(ApplicationInstanceController.class);
|
||||
this.commandLine = commandLine;
|
||||
}
|
||||
+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.cli;
|
||||
|
||||
import org.jkiss.dbeaver.model.cli.model.NonExecutableOption;
|
||||
import picocli.CommandLine;
|
||||
|
||||
/**
|
||||
* CloudBeaver specific mixin options
|
||||
*/
|
||||
public class CloudBeaverMixin {
|
||||
@NonExecutableOption
|
||||
@CommandLine.Option(
|
||||
names = {"-web-config"},
|
||||
arity = "1",
|
||||
hidden = true,
|
||||
scope = CommandLine.ScopeType.INHERIT
|
||||
)
|
||||
private String webConfig;
|
||||
}
|
||||
+13
-4
@@ -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,16 +19,25 @@ package io.cloudbeaver.model.cli;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
|
||||
import org.jkiss.dbeaver.model.cli.CLIContextImpl;
|
||||
import org.jkiss.dbeaver.model.cli.CLIRunMeta;
|
||||
import org.jkiss.dbeaver.model.cli.CommandLineContext;
|
||||
import org.jkiss.dbeaver.model.cli.command.AbstractTopLevelCommand;
|
||||
import org.jkiss.dbeaver.model.cli.model.NonExecutableOption;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@CommandLine.Command(name = "cbvr", description = "CloudBeaver commands", mixinStandardHelpOptions = true)
|
||||
@CommandLine.Command(name = "cbvr", description = "CloudBeaver commands")
|
||||
public class CloudBeaverTopLevelCommand extends AbstractTopLevelCommand {
|
||||
@NonExecutableOption
|
||||
@CommandLine.Option(
|
||||
names = {NOSPASH_OPTION},
|
||||
hidden = true,
|
||||
scope = CommandLine.ScopeType.INHERIT
|
||||
)
|
||||
private boolean noSplash;
|
||||
|
||||
protected CloudBeaverTopLevelCommand(
|
||||
@Nullable ApplicationInstanceController controller,
|
||||
@NotNull CommandLineContext context,
|
||||
@NotNull CLIContextImpl context,
|
||||
@NotNull CLIRunMeta meta
|
||||
|
||||
) {
|
||||
@@ -3,21 +3,20 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Model
|
||||
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
|
||||
Bundle-Version: 1.0.89.qualifier
|
||||
Bundle-Release-Date: 20251117
|
||||
Bundle-Version: 1.0.102.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.cli;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.sm;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.event;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.nio;visibility:=reexport,
|
||||
org.jkiss.dbeaver.registry;visibility:=reexport,
|
||||
org.jkiss.dbeaver.net.ssh,
|
||||
org.jkiss.bundle.logback;visibility:=reexport,
|
||||
org.jkiss.bundle.graphql;visibility:=reexport,
|
||||
org.jkiss.bundle.apache.dbcp,
|
||||
com.google.gson;visibility:=reexport,
|
||||
jakarta.servlet-api;bundle-version:="6.0.0";visibility:=reexport
|
||||
Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.auth,
|
||||
@@ -26,17 +25,21 @@ Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.auth.provider.local,
|
||||
io.cloudbeaver.auth.provisioning,
|
||||
io.cloudbeaver.websocket,
|
||||
io.cloudbeaver.websocket.event.client,
|
||||
io.cloudbeaver.websocket.event.task,
|
||||
io.cloudbeaver.model,
|
||||
io.cloudbeaver.model.api.annotation,
|
||||
io.cloudbeaver.model.api.annotation.enums,
|
||||
io.cloudbeaver.model.api.annotation.media,
|
||||
io.cloudbeaver.model.api.annotation.responses,
|
||||
io.cloudbeaver.model.api.annotation.tags,
|
||||
io.cloudbeaver.model.app,
|
||||
io.cloudbeaver.model.config,
|
||||
io.cloudbeaver.model.apilog,
|
||||
io.cloudbeaver.model.cli,
|
||||
io.cloudbeaver.model.events,
|
||||
io.cloudbeaver.model.fs,
|
||||
io.cloudbeaver.model.log,
|
||||
io.cloudbeaver.model.rm,
|
||||
io.cloudbeaver.model.rm.local,
|
||||
io.cloudbeaver.model.rm.lock,
|
||||
io.cloudbeaver.model.session,
|
||||
io.cloudbeaver.model.user,
|
||||
io.cloudbeaver.model.utils,
|
||||
|
||||
@@ -25,4 +25,34 @@
|
||||
label="Default secret controller" description="Default void secret controller"/>
|
||||
</extension>
|
||||
|
||||
<extension point="io.cloudbeaver.feature">
|
||||
<feature id="ssh" label="SSH tunnels for databases" description="Enables SSH tunnels for secure database connections, allowing access to remote databases through encrypted channels" enabledByDefault="true"/>
|
||||
</extension>
|
||||
|
||||
|
||||
<extension point="org.jkiss.dbeaver.ws.event">
|
||||
<event topicId="cb_session_task" id="cb_session_task_info_updated"
|
||||
class="io.cloudbeaver.websocket.event.task.WSSessionTaskInfoEvent"/>
|
||||
|
||||
<event topicId="cb_session_task" id="cb_session_task_confirmation_request"
|
||||
class="io.cloudbeaver.websocket.event.task.WSSessionTaskConfirmationRequestEvent"/>
|
||||
<event topicId="cb_session_task" id="cb_session_task_query_confirmation_request"
|
||||
class="io.cloudbeaver.websocket.event.task.WSSessionTaskQueryConfirmationRequestEvent"/>
|
||||
<event topicId="cb_session_task" id="cb_session_task_query_params_confirmation_request"
|
||||
class="io.cloudbeaver.websocket.event.task.WSSessionTaskQueryParamsConfirmationEvent"/>
|
||||
|
||||
<clientEvent id="cb_client_session_ping" class="io.cloudbeaver.websocket.event.client.WSSessionPingClientEvent"/>
|
||||
<clientEvent id="cb_client_topic_subscribe" class="io.cloudbeaver.websocket.event.client.WSSubscribeOnTopicClientEvent"/>
|
||||
<clientEvent id="cb_client_topic_unsubscribe"
|
||||
class="io.cloudbeaver.websocket.event.client.WSUnsubscribeFromTopicClientEvent"/>
|
||||
<clientEvent id="cb_client_projects_active"
|
||||
class="io.cloudbeaver.websocket.event.client.WSUpdateActiveProjectsClientEvent"/>
|
||||
<clientEvent id="cb_client_session_task_confirmation"
|
||||
class="io.cloudbeaver.websocket.event.client.WSSessionTaskConfirmationEvent"/>
|
||||
<clientEvent id="cb_client_session_task_with_parameters_confirmation"
|
||||
class="io.cloudbeaver.websocket.event.client.WSSessionTaskWithParametersConfirmationEvent"/>
|
||||
<clientEvent id="cb_client_cancel_action"
|
||||
class="io.cloudbeaver.websocket.event.client.WSSessionCancelActionClientEvent"/>
|
||||
</extension>
|
||||
|
||||
</plugin>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model</artifactId>
|
||||
<version>1.0.89-SNAPSHOT</version>
|
||||
<version>1.0.102-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -146,6 +146,8 @@ public class DBWebException extends DBException implements GraphQLError {
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
} else if (cause.getMessage() == null && cause.getCause() != null) {
|
||||
return message + ":\n" + cause.getCause().getClass().getSimpleName();
|
||||
}
|
||||
if (CommonUtils.isEmpty(message)) {
|
||||
if (cause.getMessage() != null) {
|
||||
|
||||
+12
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package io.cloudbeaver;
|
||||
|
||||
import io.cloudbeaver.model.WebConnectionConfig;
|
||||
import io.cloudbeaver.model.app.WebAppConfiguration;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
@@ -26,14 +27,17 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.registry.DataSourcePreferenceStore;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C extends DataSourceDescriptor> {
|
||||
private static final Log log = Log.getLog(WebConnectionConfigInputHandler.class);
|
||||
protected final T input;
|
||||
protected final DBPDataSourceRegistry registry;
|
||||
protected final WebSession webSession;
|
||||
|
||||
public WebConnectionConfigInputHandler(@NotNull DBPDataSourceRegistry registry, T configInput) {
|
||||
public WebConnectionConfigInputHandler(@NotNull WebSession webSession, @NotNull DBPDataSourceRegistry registry, T configInput) {
|
||||
this.webSession = webSession;
|
||||
this.registry = registry;
|
||||
this.input = configInput;
|
||||
}
|
||||
@@ -52,6 +56,7 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
|
||||
}
|
||||
|
||||
WebDataSourceUtils.saveAuthProperties(
|
||||
webSession.getProgressMonitor(),
|
||||
newDataSource,
|
||||
newDataSource.getConnectionConfiguration(),
|
||||
input.getCredentials(),
|
||||
@@ -73,7 +78,7 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
|
||||
|
||||
dataSource.setFolder(input.getFolder() != null ? registry.getFolder(input.getFolder()) : null);
|
||||
if (input.isDefaultAutoCommit() != null) {
|
||||
dataSource.setDefaultAutoCommit(input.isDefaultAutoCommit());
|
||||
dataSource.getConnectionConfiguration().getBootstrap().setDefaultAutoCommit(input.isDefaultAutoCommit());
|
||||
}
|
||||
WebDataSourceUtils.setConnectionConfiguration(
|
||||
dataSource.getDriver(),
|
||||
@@ -81,6 +86,7 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
|
||||
input
|
||||
);
|
||||
WebDataSourceUtils.saveAuthProperties(
|
||||
webSession.getProgressMonitor(),
|
||||
dataSource,
|
||||
dataSource.getConnectionConfiguration(),
|
||||
input.getCredentials(),
|
||||
@@ -88,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
|
||||
|
||||
+8
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -377,6 +377,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 +412,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,53 +24,46 @@ import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPAdaptable;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBPObjectSettingsProvider;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistryCache;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNModel;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.dbeaver.model.security.SMControllerUtils;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.registry.DataSourceRegistry;
|
||||
import org.jkiss.dbeaver.registry.project.BaseProjectSettings;
|
||||
import org.jkiss.dbeaver.runtime.jobs.DisconnectJob;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.*;
|
||||
|
||||
public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptable {
|
||||
private static final Log log = Log.getLog(WebSessionProjectImpl.class);
|
||||
|
||||
protected final WebSession webSession;
|
||||
private final Map<String, WebConnectionInfo> connections = new HashMap<>();
|
||||
private final Map<String, Object> projectSettings = new ConcurrentHashMap<>();
|
||||
private final BaseProjectSettings projectSettings;
|
||||
private boolean registryIsLoaded = false;
|
||||
|
||||
public WebSessionProjectImpl(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull RMProject project
|
||||
) {
|
||||
super(
|
||||
webSession.getWorkspace(),
|
||||
webSession.getRmController(),
|
||||
webSession.getSessionContext(),
|
||||
project,
|
||||
webSession.getUserPreferenceStore(),
|
||||
RMUtils.getProjectPath(project)
|
||||
);
|
||||
this.webSession = webSession;
|
||||
this(webSession, project, null);
|
||||
}
|
||||
|
||||
public WebSessionProjectImpl(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull RMProject project,
|
||||
@NotNull Path path
|
||||
@Nullable Path path
|
||||
) {
|
||||
super(
|
||||
webSession.getWorkspace(),
|
||||
@@ -78,9 +71,36 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
webSession.getSessionContext(),
|
||||
project,
|
||||
webSession.getUserPreferenceStore(),
|
||||
path
|
||||
path == null ? RMUtils.getProjectPath(project) : path
|
||||
);
|
||||
this.webSession = webSession;
|
||||
this.projectSettings = new BaseProjectSettings(this) {
|
||||
@NotNull
|
||||
@Override
|
||||
protected Map<SMObjectType, Map<String, Map<String, String>>> loadAllProjectSettings() throws DBException {
|
||||
if (webSession.getUser() == null) {
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
return SMControllerUtils.getObjectSettingsMap(WebSessionProjectImpl.this, webSession.getSecurityController());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveProjectSettings(
|
||||
@NotNull SMObjectType objectType,
|
||||
@NotNull String objectId,
|
||||
@NotNull Map<String, String> settings
|
||||
) throws DBException {
|
||||
if (webSession.getUserContext().isNonAnonymousUserAuthorizedInSM()) {
|
||||
webSession.getSecurityController().setObjectSettings(getId(), objectType, objectId, settings);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public BaseProjectSettings getProjectSettings() {
|
||||
return projectSettings;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -89,6 +109,14 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
return webSession.getNavigatorModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getAdapter(@NotNull Class<T> adapter) {
|
||||
if (adapter == DBPObjectSettingsProvider.class) {
|
||||
return adapter.cast(projectSettings);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected DBPDataSourceRegistry createDataSourceRegistry() {
|
||||
@@ -190,6 +218,7 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
*
|
||||
* @return connections from cache.
|
||||
*/
|
||||
@NotNull
|
||||
public List<WebConnectionInfo> getConnections() {
|
||||
if (!registryIsLoaded) {
|
||||
addDataSourcesToCache();
|
||||
@@ -223,14 +252,14 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -244,20 +273,6 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
return sendDataSourceUpdatedEvent;
|
||||
}
|
||||
|
||||
public void refreshProjectSettings() throws DBException {
|
||||
if (webSession.getUser() == null) {
|
||||
projectSettings.clear();
|
||||
return;
|
||||
}
|
||||
Map<String, Object> loadedSettings = webSession.getSecurityController().getObjectSettings(
|
||||
getId(),
|
||||
SMObjectType.project,
|
||||
null
|
||||
);
|
||||
projectSettings.clear();
|
||||
projectSettings.putAll(loadedSettings);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebConnectionInfo createConnectionInfo(@NotNull DBPDataSourceContainer dataSourceDescriptor) {
|
||||
return new WebConnectionInfo(webSession, dataSourceDescriptor);
|
||||
@@ -268,7 +283,7 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
if (CommonUtils.isEmpty(configMap)) {
|
||||
throw new DBWebException("Connection configuration parameters are missing");
|
||||
}
|
||||
DBPDataSourceContainer newDataSource = getDataSourceContainerFromInput(configMap);
|
||||
DBPDataSourceContainer newDataSource = getDataSourceContainerFromInput(getConnectionConfigInput(configMap));
|
||||
return addDataSourceToProject(newDataSource);
|
||||
}
|
||||
|
||||
@@ -302,7 +317,7 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
webSession.addInfoMessage("Update connection - " + WebDataSourceUtils.getConnectionContainerInfo(dataSource));
|
||||
|
||||
DBPDataSourceRegistry registry = getDataSourceRegistry();
|
||||
getInputConfigHandler(configMap).updateDataSource(dataSource);
|
||||
getInputConfigHandler(config).updateDataSource(dataSource);
|
||||
connectionInfo.setCredentialsSavedInSession(null);
|
||||
try {
|
||||
registry.updateDataSource(dataSource);
|
||||
@@ -330,26 +345,16 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
@NotNull
|
||||
private WebConnectionInfo closeAndDeleteConnection(@NotNull WebConnectionInfo connectionInfo) throws DBWebException {
|
||||
DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer();
|
||||
boolean disconnected = WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer);
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer, false);
|
||||
DBPDataSourceRegistry registry = getDataSourceRegistry();
|
||||
registry.removeDataSource(dataSourceContainer);
|
||||
try {
|
||||
registry.checkForErrors();
|
||||
} catch (DBException e) {
|
||||
try {
|
||||
registry.addDataSource(dataSourceContainer);
|
||||
} catch (DBException ex) {
|
||||
log.error("Error re-adding after delete attempt", e);
|
||||
}
|
||||
throw new DBWebException("Failed to delete connection", e);
|
||||
}
|
||||
removeConnection(dataSourceContainer);
|
||||
return connectionInfo;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DataSourceDescriptor getDataSourceContainerFromInput(@NotNull Map<String, Object> configMap) throws DBWebException {
|
||||
return getInputConfigHandler(configMap).createDataSourceContainer();
|
||||
public DataSourceDescriptor getDataSourceContainerFromInput(@NotNull WebConnectionConfig configInput) throws DBWebException {
|
||||
return getInputConfigHandler(configInput).createDataSourceContainer();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -358,8 +363,8 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected WebConnectionConfigInputHandler getInputConfigHandler(@NotNull Map<String, Object> configMap) {
|
||||
return new WebConnectionConfigInputHandler<>(getDataSourceRegistry(), getConnectionConfigInput(configMap));
|
||||
protected WebConnectionConfigInputHandler getInputConfigHandler(@NotNull WebConnectionConfig configInput) {
|
||||
return new WebConnectionConfigInputHandler<>(webSession, getDataSourceRegistry(), configInput);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.runtime.AbstractJob;
|
||||
|
||||
public abstract class AbstractCancelableJob extends AbstractJob implements CustomCancelableJob {
|
||||
protected AbstractCancelableJob(@NotNull String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo taskInfo) {
|
||||
cancelJob(webSession, taskInfo, "Canceled by the user");
|
||||
}
|
||||
|
||||
public void cancelJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo taskInfo, @NotNull String errorMessage) {
|
||||
taskInfo.setRunning(false);
|
||||
taskInfo.setJobError(new DBException(errorMessage));
|
||||
WebEventUtils.sendAsyncTaskEvent(webSession, taskInfo);
|
||||
}
|
||||
}
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
public class WebBasicEnumObjectInfo {
|
||||
|
||||
@NotNull
|
||||
private String displayName;
|
||||
@NotNull
|
||||
private String value;
|
||||
|
||||
public WebBasicEnumObjectInfo(@NotNull String displayName, @NotNull String value) {
|
||||
this.displayName = displayName;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(@NotNull String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(@NotNull String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
+129
-7
@@ -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;
|
||||
|
||||
@@ -42,7 +43,7 @@ public class WebConnectionConfig {
|
||||
private String databaseName;
|
||||
private String url;
|
||||
|
||||
private int keepAliveInterval;
|
||||
private Integer keepAliveInterval;
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
@@ -53,17 +54,19 @@ public class WebConnectionConfig {
|
||||
|
||||
private String authModelId;
|
||||
private Map<String, Object> credentials;
|
||||
private boolean saveCredentials;
|
||||
private boolean sharedCredentials;
|
||||
private Boolean saveCredentials;
|
||||
private Boolean sharedCredentials;
|
||||
private Map<String, Object> mainPropertyValues;
|
||||
private Map<String, Object> expertSettingsValues;
|
||||
private Map<String, Object> providerProperties;
|
||||
private List<WebNetworkHandlerConfigInput> networkHandlersConfig;
|
||||
private DBPDriverConfigurationType configurationType;
|
||||
private String selectedSecretId;
|
||||
private boolean defaultAutoCommit;
|
||||
private Boolean defaultAutoCommit;
|
||||
private String defaultCatalogName;
|
||||
private String defaultSchemaName;
|
||||
@NotNull
|
||||
private Map<String, String> defaultUserPreferences = new LinkedHashMap<>();
|
||||
|
||||
public WebConnectionConfig() {
|
||||
}
|
||||
@@ -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));
|
||||
@@ -122,102 +127,178 @@ public class WebConnectionConfig {
|
||||
return connectionId;
|
||||
}
|
||||
|
||||
public void setConnectionId(String connectionId) {
|
||||
this.connectionId = connectionId;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getDriverId() {
|
||||
return driverId;
|
||||
}
|
||||
|
||||
public void setDriverId(String driverId) {
|
||||
this.driverId = driverId;
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getFolder() {
|
||||
return folder;
|
||||
}
|
||||
|
||||
public void setFolder(String folder) {
|
||||
this.folder = folder;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(String port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
public void setServerName(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getDatabaseName() {
|
||||
return databaseName;
|
||||
}
|
||||
|
||||
public void setDatabaseName(String databaseName) {
|
||||
this.databaseName = databaseName;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(Map<String, Object> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getUserPassword() {
|
||||
return userPassword;
|
||||
}
|
||||
|
||||
public void setUserPassword(String userPassword) {
|
||||
this.userPassword = userPassword;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getAuthModelId() {
|
||||
return authModelId;
|
||||
}
|
||||
|
||||
public void setAuthModelId(String authModelId) {
|
||||
this.authModelId = authModelId;
|
||||
}
|
||||
|
||||
@Property
|
||||
public DBPDriverConfigurationType getConfigurationType() {
|
||||
return configurationType;
|
||||
}
|
||||
|
||||
public void setConfigurationType(DBPDriverConfigurationType configurationType) {
|
||||
this.configurationType = configurationType;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getCredentials() {
|
||||
return credentials;
|
||||
}
|
||||
|
||||
public void setCredentials(Map<String, Object> credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
public List<WebNetworkHandlerConfigInput> getNetworkHandlersConfig() {
|
||||
return networkHandlersConfig;
|
||||
}
|
||||
|
||||
public void setNetworkHandlersConfig(List<WebNetworkHandlerConfigInput> networkHandlersConfig) {
|
||||
this.networkHandlersConfig = networkHandlersConfig;
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isSaveCredentials() {
|
||||
return saveCredentials;
|
||||
}
|
||||
|
||||
public void setSaveCredentials(boolean saveCredentials) {
|
||||
this.saveCredentials = saveCredentials;
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isSharedCredentials() {
|
||||
return sharedCredentials;
|
||||
}
|
||||
|
||||
public void setSaveCredentials(boolean saveCredentials) {
|
||||
this.saveCredentials = saveCredentials;
|
||||
public void setSharedCredentials(boolean sharedCredentials) {
|
||||
this.sharedCredentials = sharedCredentials;
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -225,38 +306,79 @@ public class WebConnectionConfig {
|
||||
return mainPropertyValues;
|
||||
}
|
||||
|
||||
public void setMainPropertyValues(Map<String, Object> mainPropertyValues) {
|
||||
this.mainPropertyValues = mainPropertyValues;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getExpertSettingsValues() {
|
||||
return expertSettingsValues;
|
||||
}
|
||||
|
||||
public void setExpertSettingsValues(Map<String, Object> expertSettingsValues) {
|
||||
this.expertSettingsValues = expertSettingsValues;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getProviderProperties() {
|
||||
return providerProperties;
|
||||
}
|
||||
|
||||
public void setProviderProperties(Map<String, Object> providerProperties) {
|
||||
this.providerProperties = providerProperties;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Integer getKeepAliveInterval() {
|
||||
return keepAliveInterval;
|
||||
}
|
||||
|
||||
public void setKeepAliveInterval(int keepAliveInterval) {
|
||||
this.keepAliveInterval = keepAliveInterval;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Boolean isDefaultAutoCommit() {
|
||||
return defaultAutoCommit;
|
||||
}
|
||||
|
||||
public void setDefaultAutoCommit(boolean defaultAutoCommit) {
|
||||
this.defaultAutoCommit = defaultAutoCommit;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getSelectedSecretId() {
|
||||
return selectedSecretId;
|
||||
}
|
||||
|
||||
public void setSelectedSecretId(String selectedSecretId) {
|
||||
this.selectedSecretId = selectedSecretId;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getDefaultCatalogName() {
|
||||
return defaultCatalogName;
|
||||
}
|
||||
|
||||
public void setDefaultCatalogName(String defaultCatalogName) {
|
||||
this.defaultCatalogName = defaultCatalogName;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getDefaultSchemaName() {
|
||||
return defaultSchemaName;
|
||||
}
|
||||
|
||||
public void setDefaultSchemaName(String defaultSchemaName) {
|
||||
this.defaultSchemaName = defaultSchemaName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, String> getDefaultUserPreferences() {
|
||||
return defaultUserPreferences;
|
||||
}
|
||||
|
||||
public void setDefaultUserPreferences(@NotNull Map<String, String> defaultUserPreferences) {
|
||||
this.defaultUserPreferences = defaultUserPreferences;
|
||||
}
|
||||
}
|
||||
|
||||
+35
-6
@@ -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,6 +20,7 @@ import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.model.app.BaseWebAppConfiguration;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import io.cloudbeaver.service.security.SMUtils;
|
||||
import io.cloudbeaver.service.sql.WebDataFormat;
|
||||
import io.cloudbeaver.utils.CBModelConstants;
|
||||
@@ -39,11 +40,13 @@ 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.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;
|
||||
@@ -59,7 +62,6 @@ import java.util.stream.Collectors;
|
||||
public class WebConnectionInfo {
|
||||
|
||||
private static final Log log = Log.getLog(WebConnectionInfo.class);
|
||||
public static final String SECURED_VALUE = "********";
|
||||
|
||||
private static final String FEATURE_HAS_TOOLS = "hasTools";
|
||||
private static final String FEATURE_CONNECTED = "connected";
|
||||
@@ -127,7 +129,7 @@ public class WebConnectionInfo {
|
||||
if (canViewReadOnlyConnections()) {
|
||||
return dataSourceContainer.getConnectionConfiguration().getHostName();
|
||||
}
|
||||
return SECURED_VALUE;
|
||||
return CBConstants.SECURED_VALUE;
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -135,7 +137,7 @@ public class WebConnectionInfo {
|
||||
if (canViewReadOnlyConnections()) {
|
||||
return dataSourceContainer.getConnectionConfiguration().getHostPort();
|
||||
}
|
||||
return SECURED_VALUE;
|
||||
return CBConstants.SECURED_VALUE;
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -148,7 +150,7 @@ public class WebConnectionInfo {
|
||||
if (canViewReadOnlyConnections()) {
|
||||
return dataSourceContainer.getConnectionConfiguration().getDatabaseName();
|
||||
}
|
||||
return SECURED_VALUE;
|
||||
return CBConstants.SECURED_VALUE;
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -156,7 +158,7 @@ public class WebConnectionInfo {
|
||||
if (canViewReadOnlyConnections()) {
|
||||
return dataSourceContainer.getConnectionConfiguration().getUrl();
|
||||
}
|
||||
return SECURED_VALUE;
|
||||
return CBConstants.SECURED_VALUE;
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -287,6 +289,22 @@ public class WebConnectionInfo {
|
||||
return dataSourceContainer.getNavigatorSettings();
|
||||
}
|
||||
|
||||
@Property
|
||||
@NotNull
|
||||
public DBNBrowseSettings getDefaultNavigatorSettings() {
|
||||
return dataSourceContainer.getNavigatorSettings().getOriginalSettings();
|
||||
}
|
||||
|
||||
@Property
|
||||
@NotNull
|
||||
public Map<String, String> getDefaultUserPreferences() {
|
||||
DBPPreferenceStore preferenceStore = dataSourceContainer.getPreferenceStore();
|
||||
if (preferenceStore instanceof DataSourcePreferenceStore dataSourcePreferenceStore) {
|
||||
return dataSourcePreferenceStore.getProperties();
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Property
|
||||
@NotNull
|
||||
public List<WebDataFormat> getSupportedDataFormats() {
|
||||
@@ -341,6 +359,16 @@ public class WebConnectionInfo {
|
||||
if (CommonUtils.isEmpty(authModelId)) {
|
||||
authModelId = AuthModelDatabaseNative.ID;
|
||||
}
|
||||
|
||||
// Patch auth model
|
||||
DBPAuthModelDescriptor authModel = DBWorkbench.getPlatform().getDataSourceProviderRegistry().getAuthModel(authModelId);
|
||||
if (authModel != null) {
|
||||
DBPAuthModelDescriptor amReplace = authModel.getReplacedBy(dataSourceContainer.getDriver());
|
||||
if (amReplace != null && amReplace != authModel) {
|
||||
return amReplace.getId();
|
||||
}
|
||||
}
|
||||
|
||||
return authModelId;
|
||||
}
|
||||
|
||||
@@ -562,4 +590,5 @@ public class WebConnectionInfo {
|
||||
public void setCredentialsSavedInSession(@Nullable Boolean credentialsSavedInSession) {
|
||||
this.credentialsSavedInSession = credentialsSavedInSession;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-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.
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerType;
|
||||
@@ -88,7 +89,9 @@ public class WebNetworkHandlerConfig {
|
||||
DBPPropertyDescriptor[] descriptor = configuration.getHandlerDescriptor().getHandlerProperties();
|
||||
for (DBPPropertyDescriptor p : descriptor) {
|
||||
if (p.hasFeature(RegistryConstants.ATTR_PASSWORD)) {
|
||||
secureProperties.computeIfPresent(p.getId(), (k, v) -> CommonUtils.isEmpty(v) ? null : WebConnectionInfo.SECURED_VALUE);
|
||||
secureProperties.computeIfPresent(p.getId(), (k, v) ->
|
||||
CommonUtils.isEmpty(v) ? null : CBConstants.SECURED_VALUE
|
||||
);
|
||||
}
|
||||
}
|
||||
return secureProperties;
|
||||
|
||||
+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) {
|
||||
|
||||
@@ -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,11 +17,13 @@
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.*;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriverConfigurationType;
|
||||
import org.jkiss.dbeaver.model.impl.PropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.impl.ProviderPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.meta.IPropertyValueListProvider;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
@@ -30,6 +32,7 @@ import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertySource;
|
||||
import org.jkiss.dbeaver.registry.settings.ProductSettingDescriptor;
|
||||
import org.jkiss.dbeaver.runtime.properties.ObjectPropertyDescriptor;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
@@ -136,13 +139,14 @@ public class WebPropertyInfo {
|
||||
@Property
|
||||
public Object getValue() throws DBException {
|
||||
Object value = propertySource == null ? null : propertySource.getPropertyValue(session.getProgressMonitor(), property.getId());
|
||||
if (property instanceof ObjectPropertyDescriptor) {
|
||||
ObjectPropertyDescriptor opd = (ObjectPropertyDescriptor) property;
|
||||
if (!showProtected && opd.isPassword() || opd.isHidden()) {
|
||||
if (value == null || value.toString().isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return "******";
|
||||
if (property instanceof ObjectPropertyDescriptor opd &&
|
||||
(!showProtected && opd.isPassword() || opd.isHidden())
|
||||
) {
|
||||
return maskValue(value);
|
||||
} else if (property instanceof PropertyDescriptor pd) {
|
||||
String[] features = pd.getFeatures();
|
||||
if (features != null && ArrayUtils.contains(features, DBConstants.PROP_FEATURE_PASSWORD)) {
|
||||
return maskValue(value);
|
||||
}
|
||||
}
|
||||
return value == null ? null : makePropertyValue(value);
|
||||
@@ -243,9 +247,11 @@ public class WebPropertyInfo {
|
||||
List<Object> result = new ArrayList<>();
|
||||
int length = Array.getLength(value);
|
||||
for (int i = 0; i < length; i++) {
|
||||
result.add(Array.get(value, i));
|
||||
result.add(makePropertyValue(Array.get(value, i)));
|
||||
}
|
||||
return result;
|
||||
} else if (value instanceof Enum<?> enumValue) {
|
||||
return new WebBasicEnumObjectInfo(enumValue.toString(), enumValue.name());
|
||||
}
|
||||
Class<?> dataType = property.getDataType();
|
||||
if (dataType == Boolean.class || dataType == Boolean.TYPE) {
|
||||
@@ -305,4 +311,13 @@ public class WebPropertyInfo {
|
||||
READ_ONLY
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String maskValue(@Nullable Object value) {
|
||||
if (value == null || value.toString().isEmpty()) {
|
||||
return "";
|
||||
} else {
|
||||
return CBConstants.SECURED_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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 "";
|
||||
}
|
||||
+13
-25
@@ -20,13 +20,11 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.registry.fs.FileSystemProviderRegistry;
|
||||
import org.jkiss.dbeaver.model.fs.DBFUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
@@ -65,30 +63,20 @@ public abstract class BaseServerConfigurationController<T extends ServletServerC
|
||||
if (CommonUtils.isEmpty(workspaceLocation)) {
|
||||
return defaultWorkspaceLocation;
|
||||
}
|
||||
URI workspaceUri = URI.create(workspaceLocation);
|
||||
if (workspaceUri.getScheme() == null) {
|
||||
// default filesystem
|
||||
return getHomeDirectory().resolve(workspaceLocation);
|
||||
} else {
|
||||
var externalFsProvider =
|
||||
FileSystemProviderRegistry.getInstance().getFileSystemProviderBySchema(workspaceUri.getScheme());
|
||||
if (externalFsProvider == null) {
|
||||
log.error("File system not found for scheme: " + workspaceUri.getScheme() + " default workspace " +
|
||||
"location will be used");
|
||||
try {
|
||||
Path externalFsPath = DBFUtils.getPathFromURI(workspaceLocation);
|
||||
if (externalFsPath == null) {
|
||||
log.warn("Failed to detect workspace path from URI: " + workspaceLocation +
|
||||
" default workspace location will be used");
|
||||
return defaultWorkspaceLocation;
|
||||
}
|
||||
ClassLoader fsClassloader = externalFsProvider.getInstance().getClass().getClassLoader();
|
||||
try (FileSystem externalFileSystem = FileSystems.newFileSystem(workspaceUri,
|
||||
System.getenv(),
|
||||
fsClassloader)) {
|
||||
log.info("Path from external filesystem used for workspace: " + workspaceUri);
|
||||
return externalFileSystem.provider().getPath(workspaceUri);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to initialize workspace path: " + workspaceUri + " default workspace " +
|
||||
"location will be used", e);
|
||||
}
|
||||
log.info("Path from external filesystem used for workspace: " + externalFsPath);
|
||||
return externalFsPath;
|
||||
} catch (DBException e) {
|
||||
log.error("Failed to detect workspace path from URI: " + workspaceLocation +
|
||||
" default workspace location will be used", e);
|
||||
return defaultWorkspaceLocation;
|
||||
}
|
||||
return defaultWorkspaceLocation;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+30
-21
@@ -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,8 +16,8 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import io.cloudbeaver.model.cli.CloudBeaverInstanceServer;
|
||||
import io.cloudbeaver.model.log.SLF4JLogHandler;
|
||||
import io.cloudbeaver.registry.WebFeatureRegistry;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.equinox.app.IApplicationContext;
|
||||
import org.jkiss.code.NotNull;
|
||||
@@ -25,11 +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.cli.ApplicationInstanceController;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockManager;
|
||||
import org.jkiss.dbeaver.model.fs.lock.shared.SharedFileLockManager;
|
||||
import org.jkiss.dbeaver.model.impl.app.ApplicationRegistry;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
|
||||
@@ -41,7 +43,6 @@ import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.dbeaver.utils.RuntimeUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
@@ -50,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";
|
||||
@@ -60,7 +61,8 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
private static final Log log = Log.getLog(BaseServletApplication.class);
|
||||
|
||||
private String instanceId;
|
||||
private CloudBeaverInstanceServer instanceServer;
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public RMController createResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@@ -75,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() {
|
||||
@@ -163,6 +177,7 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
* Method returns VoidSecretController instance.
|
||||
* Advanced apps may implement it differently.
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public DBSSecretController getSecretController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@@ -204,11 +219,6 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
public Object start(IApplicationContext context) {
|
||||
initializeApplicationServices();
|
||||
try {
|
||||
try {
|
||||
this.instanceServer = createInstanceServer();
|
||||
} catch (Exception e) {
|
||||
log.error("Error initializing instance server", e);
|
||||
}
|
||||
startServer();
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
@@ -217,12 +227,9 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
protected CloudBeaverInstanceServer createInstanceServer() throws IOException {
|
||||
return new CloudBeaverInstanceServer();
|
||||
}
|
||||
|
||||
protected abstract void startServer() throws DBException;
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public synchronized String getApplicationInstanceId() throws DBException {
|
||||
if (instanceId == null) {
|
||||
@@ -247,6 +254,7 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
return BaseWorkspaceImpl.readWorkspaceIdProperty();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Path getWorkspaceDirectory() {
|
||||
return getServerConfigurationController().getWorkspacePath();
|
||||
@@ -261,6 +269,7 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WSEventController getEventController() {
|
||||
return null;
|
||||
@@ -281,14 +290,14 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ApplicationInstanceController getInstanceServer() {
|
||||
return instanceServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnonymousAccessEnabled() {
|
||||
return getAppConfiguration().isAnonymousAccessEnabled();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WebFeatureRegistry getFeatureRegistry() {
|
||||
return WebFeatureRegistry.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -17,7 +17,6 @@
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import io.cloudbeaver.DBWFeatureSet;
|
||||
import io.cloudbeaver.registry.WebFeatureRegistry;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.DBConstants;
|
||||
@@ -115,7 +114,7 @@ public abstract class BaseWebAppConfiguration implements ServletAppConfiguration
|
||||
public String[] getEnabledFeatures() {
|
||||
if (enabledFeatures == null) {
|
||||
// No config - enable all features (+backward compatibility)
|
||||
return WebFeatureRegistry.getInstance().getWebFeatures()
|
||||
return ServletAppUtils.getServletApplication().getFeatureRegistry().getWebFeatures()
|
||||
.stream().map(DBWFeatureSet::getId).toArray(String[]::new);
|
||||
}
|
||||
return enabledFeatures;
|
||||
|
||||
+23
-5
@@ -16,7 +16,9 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import io.cloudbeaver.registry.WebFeatureRegistry;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.DBFileController;
|
||||
import org.jkiss.dbeaver.model.app.DBPApplication;
|
||||
@@ -42,14 +44,19 @@ public interface ServletApplication extends DBPApplication {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
ServletAppConfiguration getAppConfiguration();
|
||||
|
||||
@NotNull
|
||||
ServletServerConfiguration getServerConfiguration();
|
||||
|
||||
@NotNull
|
||||
Path getDataDirectory(boolean create);
|
||||
|
||||
@NotNull
|
||||
Path getWorkspaceDirectory();
|
||||
|
||||
@NotNull
|
||||
Path getHomeDirectory();
|
||||
|
||||
boolean isMultiNode();
|
||||
@@ -58,30 +65,35 @@ public interface ServletApplication extends DBPApplication {
|
||||
|
||||
SMAdminController getAdminSecurityController(@NotNull SMCredentialsProvider credentialsProvider) throws DBException;
|
||||
|
||||
@NotNull
|
||||
DBSSecretController getSecretController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
SMSessionContext smSessionContext
|
||||
) throws DBException;
|
||||
|
||||
@NotNull
|
||||
RMController createResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace
|
||||
) throws DBException;
|
||||
|
||||
@NotNull
|
||||
DBFileController createFileController(@NotNull SMCredentialsProvider credentialsProvider);
|
||||
|
||||
String getServerURL();
|
||||
|
||||
@NotNull
|
||||
default String getServicesURI() {
|
||||
return "/";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
default String getRootURI() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
String getApplicationInstanceId() throws DBException;
|
||||
|
||||
@NotNull
|
||||
WSEventController getEventController();
|
||||
|
||||
/**
|
||||
@@ -94,12 +106,18 @@ public interface ServletApplication extends DBPApplication {
|
||||
/**
|
||||
* Collector that contains information about system.
|
||||
*/
|
||||
@NotNull
|
||||
ServletSystemInformationCollector getSystemInformationCollector();
|
||||
@Nullable
|
||||
ServletSystemInformationCollector<?> getSystemInformationCollector();
|
||||
|
||||
default void getStatusInfo(Map<String, Object> infoMap) {
|
||||
default void getStatusInfo(@NotNull Map<String, Object> infoMap) {
|
||||
|
||||
}
|
||||
|
||||
boolean isAnonymousAccessEnabled();
|
||||
|
||||
/**
|
||||
* Web feature registry
|
||||
*/
|
||||
@NotNull
|
||||
WebFeatureRegistry getFeatureRegistry();
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,7 +28,7 @@ import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class FSUtils {
|
||||
public class WebFSUtils {
|
||||
@NotNull
|
||||
public static String makeUniqueFsId(@NotNull DBFVirtualFileSystem fileSystem) {
|
||||
return fileSystem.getType() + "://" + fileSystem.getId();
|
||||
+6
-5
@@ -31,8 +31,6 @@ import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMResource;
|
||||
import org.jkiss.dbeaver.model.rm.RMResourceType;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.registry.ResourceTypeDescriptor;
|
||||
import org.jkiss.dbeaver.registry.ResourceTypeRegistry;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
@@ -44,7 +42,7 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
|
||||
|
||||
private final RMResource resource;
|
||||
|
||||
DBNResourceManagerResource(DBNNode parentNode, RMResource resource) {
|
||||
DBNResourceManagerResource(@NotNull DBNNode parentNode, @NotNull RMResource resource) {
|
||||
super(parentNode);
|
||||
this.resource = resource;
|
||||
}
|
||||
@@ -71,6 +69,7 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
|
||||
@Override
|
||||
public DBPImage getNodeIcon() {
|
||||
if (resource.isFolder()) {
|
||||
/*
|
||||
if (getParentNode() instanceof DBNResourceManagerResource) {
|
||||
return DBIcon.TREE_FOLDER;
|
||||
}
|
||||
@@ -82,11 +81,12 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
|
||||
if (folderResType != null) {
|
||||
return folderResType.getFolderIcon();
|
||||
}
|
||||
*/
|
||||
return DBIcon.TREE_FOLDER;
|
||||
} else {
|
||||
var fileExtension = IOUtils.getFileExtension(getNodeDisplayName());
|
||||
if (!CommonUtils.isEmpty(fileExtension)) {
|
||||
RMProject project = getProjectNode();
|
||||
RMProject project = getRmProject();
|
||||
if (project != null) {
|
||||
for (RMResourceType rt : project.getResourceTypes()) {
|
||||
if (ArrayUtils.contains(rt.getFileExtensions(), fileExtension)) {
|
||||
@@ -99,7 +99,8 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
|
||||
}
|
||||
}
|
||||
|
||||
private RMProject getProjectNode() {
|
||||
@Nullable
|
||||
public RMProject getRmProject() {
|
||||
for (DBNNode node = this; node != null; node = node.getParentNode()) {
|
||||
if (node instanceof DBNResourceManagerProject) {
|
||||
return ((DBNResourceManagerProject) node).getProject();
|
||||
|
||||
+28
-50
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.rm.local;
|
||||
|
||||
import io.cloudbeaver.BaseWebProjectImpl;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
@@ -27,8 +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.impl.auth.SessionContextImpl;
|
||||
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;
|
||||
@@ -51,22 +51,21 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
private static final Log log = Log.getLog(BaseLocalResourceController.class);
|
||||
|
||||
public static final String DEFAULT_CHANGE_ID = "0";
|
||||
private static final String FILE_REGEX = "(?U)[\\w.$()@/\\\\ -]+";
|
||||
private static final String PROJECT_REGEX = "(?U)[\\w.$()@ -]+"; // slash not allowed in project name
|
||||
|
||||
@NotNull
|
||||
protected final DBPWorkspace workspace;
|
||||
@NotNull
|
||||
protected final FileLockController lockController;
|
||||
protected final LockManager lockController;
|
||||
|
||||
protected BaseLocalResourceController(
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull FileLockController lockController
|
||||
@NotNull LockManager lockController
|
||||
) {
|
||||
this.workspace = workspace;
|
||||
this.lockController = lockController;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RMProject getProject(@NotNull String projectId, boolean readResources, boolean readProperties)
|
||||
throws DBException {
|
||||
@@ -85,6 +84,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
return project;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getProjectProperty(@NotNull String projectId, @NotNull String propName) throws DBException {
|
||||
var project = getWebProject(projectId, false);
|
||||
@@ -97,7 +97,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
public void setProjectProperty(
|
||||
@NotNull String projectId,
|
||||
@NotNull String propName,
|
||||
@NotNull Object propValue
|
||||
@Nullable Object propValue
|
||||
) throws DBException {
|
||||
RMLocalProject webProject = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
|
||||
@@ -159,7 +159,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String configuration,
|
||||
@Nullable List<String> dataSourceIds
|
||||
) throws DBException {
|
||||
try (var lock = 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),
|
||||
@@ -190,7 +190,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String projectId,
|
||||
@NotNull String[] dataSourceIds
|
||||
) throws DBException {
|
||||
try (var projectLock = 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();
|
||||
@@ -215,7 +215,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String projectId,
|
||||
@NotNull String folderPath
|
||||
) throws DBException {
|
||||
try (var projectLock = 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),
|
||||
@@ -226,8 +226,10 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
GeneralUtils.validateResourceName(newName);
|
||||
var parent = result.getParent();
|
||||
var parentFolder = parent == null ? null : registry.getFolder(parent.toString().replace("\\", "/"));
|
||||
DBPDataSourceFolder newFolder = registry.addFolder(parentFolder, newName);
|
||||
((DataSourcePersistentRegistry) registry).saveDataSources();
|
||||
registry.addFolder(parentFolder, newName);
|
||||
if (registry instanceof DataSourcePersistentRegistry pr) {
|
||||
pr.saveDataSources();
|
||||
}
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
}
|
||||
@@ -241,21 +243,19 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String[] folderPaths,
|
||||
boolean dropContents
|
||||
) throws DBException {
|
||||
try (var projectLock = 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),
|
||||
() -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
for (String folderPath : folderPaths) {
|
||||
DBPDataSourceFolder folder = registry.getFolder(folderPath);
|
||||
if (folder != null) {
|
||||
log.debug("Deleting data source folder '" + folderPath + "' in project '" + projectId + "'");
|
||||
registry.removeFolder(folder, dropContents);
|
||||
} else {
|
||||
log.warn("Can not find folder by path [" + folderPath + "] for deletion");
|
||||
}
|
||||
log.debug("Deleting data source folder '" + folderPath + "' in project '" + projectId + "'");
|
||||
registry.removeFolder(folder, dropContents);
|
||||
}
|
||||
if (registry instanceof DataSourcePersistentRegistry pr) {
|
||||
pr.saveDataSources();
|
||||
}
|
||||
((DataSourcePersistentRegistry) registry).saveDataSources();
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
}
|
||||
@@ -269,7 +269,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String oldPath,
|
||||
@NotNull String newPath
|
||||
) throws DBException {
|
||||
try (var projectLock = 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),
|
||||
@@ -284,19 +284,19 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract RMLocalProject getWebProject(String projectId, boolean refresh) throws DBException;
|
||||
protected abstract RMLocalProject getWebProject(@NotNull String projectId, boolean refresh) throws DBException;
|
||||
|
||||
protected abstract <T> T doFileWriteOperation(String projectId, Path file, RMFileOperation<T> operation)
|
||||
protected abstract <T> T doFileWriteOperation(@NotNull String projectId, @NotNull Path file, @NotNull RMFileOperation<T> operation)
|
||||
throws DBException;
|
||||
|
||||
protected abstract <T> T doFileReadOperation(String projectId, Path file, RMFileOperation<T> operation)
|
||||
protected abstract <T> T doFileReadOperation(@NotNull String projectId, @NotNull Path file, @NotNull RMFileOperation<T> operation)
|
||||
throws DBException;
|
||||
|
||||
protected abstract <T> T doProjectOperation(String projectId, RMFileOperation<T> operation) throws DBException;
|
||||
protected abstract <T> T doProjectOperation(@NotNull String projectId, @NotNull RMFileOperation<T> operation) throws DBException;
|
||||
|
||||
protected abstract RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException;
|
||||
protected abstract RMProject makeProjectFromId(@NotNull String projectId, boolean loadPermissions) throws DBException;
|
||||
|
||||
protected void validateResourcePath(String resourcePath) throws DBException {
|
||||
protected void validateResourcePath(@NotNull String resourcePath) throws DBException {
|
||||
var fullPath = Paths.get(resourcePath);
|
||||
for (Path path : fullPath) {
|
||||
String fileName = IOUtils.getFileNameWithoutExtension(path);
|
||||
@@ -314,28 +314,6 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
}
|
||||
}
|
||||
|
||||
protected class InternalWebProjectImpl extends BaseWebProjectImpl {
|
||||
public InternalWebProjectImpl(
|
||||
@NotNull SessionContextImpl sessionContext,
|
||||
@NotNull RMProject rmProject,
|
||||
@NotNull Path projectPath
|
||||
) {
|
||||
super(
|
||||
BaseLocalResourceController.this.workspace,
|
||||
BaseLocalResourceController.this,
|
||||
sessionContext,
|
||||
rmProject,
|
||||
projectPath
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected DBPDataSourceRegistry createDataSourceRegistry() {
|
||||
return new DataSourceRegistry(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected void fireRmResourceAddEvent(@NotNull String projectId, @NotNull String resourcePath) throws DBException {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_ADD,
|
||||
|
||||
+126
-79
@@ -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,12 +33,14 @@ 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.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.SMController;
|
||||
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;
|
||||
@@ -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;
|
||||
@@ -78,20 +81,22 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
private final Path userProjectsPath;
|
||||
private final Path sharedProjectsPath;
|
||||
private final String globalProjectName;
|
||||
private Supplier<SMController> smControllerSupplier;
|
||||
private Supplier<SMAdminController> smControllerSupplier;
|
||||
protected final List<RMFileOperationHandler> fileHandlers;
|
||||
|
||||
private final Map<String, RMLocalProject> projectRegistries = new LinkedHashMap<>();
|
||||
private final ProjectsMetadataInfo sharedProjectsMetadataInfo;
|
||||
|
||||
public LocalResourceController(
|
||||
DBPWorkspace workspace,
|
||||
SMCredentialsProvider credentialsProvider,
|
||||
Path rootPath,
|
||||
Path userProjectsPath,
|
||||
Path sharedProjectsPath,
|
||||
Supplier<SMController> smControllerSupplier
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull Path rootPath,
|
||||
@NotNull Path userProjectsPath,
|
||||
@NotNull Path sharedProjectsPath,
|
||||
@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;
|
||||
@@ -100,27 +105,34 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName();
|
||||
this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers();
|
||||
this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath, lockController);
|
||||
}
|
||||
|
||||
protected SMController getSecurityController() {
|
||||
@NotNull
|
||||
protected SMAdminController getSecurityController() {
|
||||
return smControllerSupplier.get();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Path getGlobalProjectPath() {
|
||||
return globalProjectName == null ? null : this.rootPath.resolve(this.globalProjectName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Path getPrivateProjectPath() {
|
||||
SMCredentials activeUserCredentials = credentialsProvider.getActiveUserCredentials();
|
||||
String userId = activeUserCredentials == null ? null : activeUserCredentials.getUserId();
|
||||
return userId == null ? null : this.userProjectsPath.resolve(userId);
|
||||
}
|
||||
|
||||
protected RMLocalProject getWebProject(String projectId, boolean refresh) throws DBException {
|
||||
protected RMLocalProject getWebProject(@NotNull String projectId, boolean refresh) throws DBException {
|
||||
synchronized (projectRegistries) {
|
||||
RMLocalProject project = projectRegistries.get(projectId);
|
||||
if (project == null || refresh) {
|
||||
project = createWebProjectImpl(projectId, new SessionContextImpl(null));
|
||||
if (project.getProjectType() == RMProjectType.SHARED) {
|
||||
project.setProjectInfo(sharedProjectsMetadataInfo.getProjectInfo(projectId));
|
||||
}
|
||||
projectRegistries.put(projectId, project);
|
||||
}
|
||||
return project;
|
||||
@@ -167,11 +179,6 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
projects.add(0, userProject);
|
||||
}
|
||||
}
|
||||
if (ServletAppUtils.getServletApplication().isMultiNode()) {
|
||||
for (RMProject rmProject : projects) {
|
||||
handleProjectOpened(rmProject.getId());
|
||||
}
|
||||
}
|
||||
|
||||
projects.sort(Comparator.comparing(RMProject::getDisplayName));
|
||||
return projects.toArray(new RMProject[0]);
|
||||
@@ -277,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)) {
|
||||
@@ -289,9 +296,6 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
try {
|
||||
log.debug("Creating project '" + rmProject.getId() + "'");
|
||||
Files.createDirectories(projectPath);
|
||||
if (ServletAppUtils.getServletApplication().isMultiNode()) {
|
||||
createResourceTypeFolders(projectPath);
|
||||
}
|
||||
fireRmProjectAddEvent(rmProject);
|
||||
return rmProject;
|
||||
} catch (IOException e) {
|
||||
@@ -299,10 +303,11 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public RMProject updateProject(@NotNull String projectId, @NotNull RMProjectInfo projectInfo) throws DBException {
|
||||
validateProjectName(projectId, projectInfo.getName());
|
||||
try (var projectLock = 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)) {
|
||||
@@ -312,6 +317,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
throw new DBException("Project '" + projectId + "' is not shared");
|
||||
}
|
||||
project.updateProject(projectInfo.getName(), projectInfo.getDescription());
|
||||
sharedProjectsMetadataInfo.updateProjectInfo(projectId, projectInfo);
|
||||
return WebRMUtils.createRmProjectFromWebProject(project);
|
||||
}
|
||||
}
|
||||
@@ -328,26 +334,28 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
@Override
|
||||
public void deleteProject(@NotNull String projectId) throws DBException {
|
||||
try (var projectLock = lockController.lock(projectId, "deleteProject")) {
|
||||
RMProject project = makeProjectFromId(projectId, false);
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteProject"))) {
|
||||
Path targetPath = getProjectPath(projectId);
|
||||
if (!Files.exists(targetPath)) {
|
||||
log.debug(MessageFormat.format("Project folder ''{0}'' is not found", projectId));
|
||||
log.error(MessageFormat.format("Project folder ''{0}'' is not found", projectId));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
log.debug("Deleting project '" + projectId + "'");
|
||||
log.debug("Deleting project '" + projectId + "' in persistence storage");
|
||||
getSecurityController().deleteObject(projectId, projectId, SMObjectType.project);
|
||||
log.debug("Deleting project '" + projectId + "' folder");
|
||||
IOUtils.deleteDirectory(targetPath);
|
||||
getSecurityController().deleteAllObjectPermissions(projectId, SMObjectType.project);
|
||||
synchronized (projectRegistries) {
|
||||
projectRegistries.remove(projectId);
|
||||
}
|
||||
sharedProjectsMetadataInfo.updateProjectInfo(projectId, null);
|
||||
} catch (IOException e) {
|
||||
throw new DBException("Error deleting project '" + project.getName() + "'", e);
|
||||
throw new DBException("Error deleting project '" + projectId + "'", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RMProject getProject(@NotNull String projectId, boolean readResources, boolean readProperties) throws DBException {
|
||||
RMProject project = makeProjectFromId(projectId, true);
|
||||
@@ -407,6 +415,15 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@Override
|
||||
public void deleteProjectDataSources(@NotNull String projectId, @NotNull String[] dataSourceIds) throws DBException {
|
||||
super.deleteProjectDataSources(projectId, dataSourceIds);
|
||||
|
||||
log.debug("Delete datasources '" + Arrays.toString(dataSourceIds) + "' from security controller");
|
||||
for (String dsId : dataSourceIds) {
|
||||
try {
|
||||
getSecurityController().deleteObject(projectId, dsId, SMObjectType.datasource);
|
||||
} catch (DBException e) {
|
||||
log.error("Error deleting datasource '" + dsId + "' from database");
|
||||
}
|
||||
}
|
||||
if (credentialsProvider.getActiveUserCredentials() != null && dataSourceIds.length > 0) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDataSourceEvent.delete(
|
||||
@@ -614,7 +631,8 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
}
|
||||
return doProjectOperation(projectId, () -> {
|
||||
try {
|
||||
Path folderPath = CommonUtils.isEmpty(folder) ?
|
||||
boolean projectFolder = CommonUtils.isEmpty(folder);
|
||||
Path folderPath = projectFolder ?
|
||||
projectPath :
|
||||
projectPath.resolve(folder);
|
||||
folderPath = folderPath.normalize();
|
||||
@@ -623,7 +641,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
throw new DBException("Invalid folder path");
|
||||
}
|
||||
createFolder(folderPath);
|
||||
return readChildResources(projectId, folderPath, nameMask, readProperties, readHistory, recursive);
|
||||
return readChildResources(projectId, folderPath, nameMask, projectFolder, readProperties, readHistory, recursive);
|
||||
} catch (NoSuchFileException e) {
|
||||
throw new DBException("Invalid resource folder " + folder);
|
||||
} catch (IOException e) {
|
||||
@@ -637,29 +655,55 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull String projectId,
|
||||
@NotNull Path folderPath,
|
||||
@Nullable String nameMask,
|
||||
boolean projectFolder,
|
||||
boolean readProperties,
|
||||
boolean readHistory,
|
||||
boolean recursive
|
||||
) throws IOException {
|
||||
try (Stream<Path> files = Files.list(folderPath)) {
|
||||
return files.filter(path -> {
|
||||
Set<RMResource> children = files.filter(path -> {
|
||||
String fileName = path.getFileName().toString();
|
||||
return (nameMask == null || nameMask.equals(fileName)) && !fileName.startsWith(".");
|
||||
}) // skip hidden files
|
||||
.sorted(Comparator.comparing(path -> path.getFileName().toString(), String.CASE_INSENSITIVE_ORDER))
|
||||
.map((Path path) -> makeResourceFromPath(projectId, path, nameMask, readProperties, readHistory, recursive))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
if (ServletAppUtils.getServletApplication().isMultiNode() && projectFolder) {
|
||||
addResourceTypeFolders(folderPath, children);
|
||||
}
|
||||
return children.stream()
|
||||
.sorted(Comparator.comparing(RMResource::getName, String.CASE_INSENSITIVE_ORDER))
|
||||
.toArray(RMResource[]::new);
|
||||
}
|
||||
}
|
||||
|
||||
private void addResourceTypeFolders(@NotNull Path projectPath, @NotNull Set<RMResource> children) {
|
||||
Set<String> folderNames = children.stream()
|
||||
.filter(RMResource::isFolder)
|
||||
.map(RMResource::getName)
|
||||
.collect(Collectors.toSet());
|
||||
// fake resources for default resource type folders
|
||||
for (ResourceTypeDescriptor type : ResourceTypeRegistry.getInstance().getResourceTypes()) {
|
||||
String defaultRoot = type.getDefaultRoot(null);
|
||||
if (CommonUtils.isEmpty(defaultRoot)) {
|
||||
continue;
|
||||
}
|
||||
Path resourceFolder = projectPath.resolve(defaultRoot);
|
||||
if (!folderNames.contains(resourceFolder.getFileName().toString())) {
|
||||
RMResource resource = new RMResource(resourceFolder.getFileName().toString());
|
||||
resource.setFolder(true);
|
||||
children.add(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createResource(
|
||||
@NotNull String projectId,
|
||||
@NotNull String resourcePath,
|
||||
boolean isFolder
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "createResource")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createResource"))) {
|
||||
validateResourcePath(resourcePath);
|
||||
Path targetPath = getTargetPath(projectId, resourcePath);
|
||||
if (Files.exists(targetPath)) {
|
||||
@@ -691,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()) {
|
||||
@@ -763,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" : ""));
|
||||
}
|
||||
@@ -851,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()
|
||||
@@ -896,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(),
|
||||
@@ -917,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(),
|
||||
@@ -954,7 +998,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException {
|
||||
protected RMProject makeProjectFromId(@NotNull String projectId, boolean loadPermissions) throws DBException {
|
||||
var projectName = WebRMUtils.parseProjectName(projectId);
|
||||
var projectPath = getProjectPath(projectId);
|
||||
if (!Files.exists(projectPath)) {
|
||||
@@ -993,29 +1037,13 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
.toArray(String[]::new);
|
||||
|
||||
RMLocalProject webProject = new RMLocalProject(workspace, new SessionContextImpl(null), path, type);
|
||||
if (type == RMProjectType.SHARED) {
|
||||
webProject.setProjectInfo(sharedProjectsMetadataInfo.getProjectInfo(webProject.getId()));
|
||||
}
|
||||
return createRmProjectFromWebProject(path, webProject, allProjectPermissions);
|
||||
}
|
||||
|
||||
private void createResourceTypeFolders(Path path) {
|
||||
// FIXME: do not create folders by force!!!
|
||||
var resourceTypes = ResourceTypeRegistry.getInstance().getResourceTypes();
|
||||
for (var resourceType : resourceTypes) {
|
||||
var defaultRoot = resourceType.getDefaultRoot(null);
|
||||
if (defaultRoot == null) {
|
||||
continue;
|
||||
}
|
||||
var typeFolder = path.resolve(defaultRoot);
|
||||
try {
|
||||
if (!Files.exists(typeFolder)) {
|
||||
createFolder(typeFolder);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Resource folder " + typeFolder + " is not created", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> T doProjectOperation(String projectId, RMFileOperation<T> operation) throws DBException {
|
||||
protected <T> T doProjectOperation(@NotNull String projectId, @NotNull RMFileOperation<T> operation) throws DBException {
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
try {
|
||||
fileHandler.projectOpened(projectId);
|
||||
@@ -1033,7 +1061,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
return operation.doOperation();
|
||||
}
|
||||
|
||||
protected <T> T doFileReadOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
protected <T> T doFileReadOperation(@NotNull String projectId, @NotNull Path file, @NotNull RMFileOperation<T> operation) throws DBException {
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
try {
|
||||
fileHandler.beforeFileRead(projectId, file);
|
||||
@@ -1052,7 +1080,11 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
return operation.doOperation();
|
||||
}
|
||||
|
||||
protected <T> T doFileWriteOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
protected <T> T doFileWriteOperation(
|
||||
@NotNull String projectId,
|
||||
@NotNull Path file,
|
||||
@NotNull RMFileOperation<T> operation
|
||||
) throws DBException {
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.beforeFileChange(projectId, file);
|
||||
}
|
||||
@@ -1071,10 +1103,11 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Path getProjectPath(String projectId) throws DBException {
|
||||
protected Path getProjectPath(@NotNull String projectId) throws DBException {
|
||||
RMProjectName project = WebRMUtils.parseProjectName(projectId);
|
||||
RMProjectType type = project.getType();
|
||||
String projectName = project.getName();
|
||||
GeneralUtils.validateResourceNameUnconditionally(projectName);
|
||||
switch (type) {
|
||||
case GLOBAL:
|
||||
if (!projectName.equals(globalProjectName)) {
|
||||
@@ -1162,7 +1195,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
if (recursive && resource.isFolder()) {
|
||||
try {
|
||||
resource.setChildren(readChildResources(projectId, path, nameMask, readProperties, readHistory, true));
|
||||
resource.setChildren(readChildResources(projectId, path, nameMask, false, readProperties, readHistory, true));
|
||||
} catch (IOException e) {
|
||||
log.error(e);
|
||||
}
|
||||
@@ -1176,16 +1209,14 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
return getProjectPath(projectId).toAbsolutePath().relativize(path).toString().replace('\\', IPath.SEPARATOR);
|
||||
}
|
||||
|
||||
protected void handleProjectOpened(String projectId) throws DBException {
|
||||
createResourceTypeFolders(getProjectPath(projectId));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Builder builder(
|
||||
SMCredentialsProvider credentialsProvider,
|
||||
DBPWorkspace workspace,
|
||||
Supplier<SMController> 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
|
||||
@@ -1195,16 +1226,19 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
public static class Builder {
|
||||
protected final SMCredentialsProvider credentialsProvider;
|
||||
protected final Supplier<SMController> smController;
|
||||
protected final Supplier<SMAdminController> smController;
|
||||
protected final DBPWorkspace workspace;
|
||||
|
||||
protected Path rootPath;
|
||||
protected Path userProjectsPath;
|
||||
protected Path sharedProjectsPath;
|
||||
protected LockManager lockController;
|
||||
|
||||
protected Builder(
|
||||
DBPWorkspace workspace, SMCredentialsProvider credentialsProvider,
|
||||
Supplier<SMController> smControllerSupplier
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull LockManager lockController,
|
||||
@NotNull Supplier<SMAdminController> smControllerSupplier
|
||||
) {
|
||||
this.workspace = workspace;
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
@@ -1212,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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.rm.local;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockManager;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectInfo;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ProjectsMetadataInfo {
|
||||
private static final Log log = Log.getLog(ProjectsMetadataInfo.class);
|
||||
private static final String PROJECTS_INFO_FILE_NAME = "projects-info.json";
|
||||
|
||||
private final Map<String, RMProjectInfo> projectsInfo = new LinkedHashMap<>();
|
||||
private final Path projectsPath;
|
||||
private final LockManager lockController;
|
||||
|
||||
|
||||
public ProjectsMetadataInfo(@NotNull Path projectsPath, @NotNull LockManager lockController) throws DBException {
|
||||
this.projectsPath = projectsPath;
|
||||
this.lockController = lockController;
|
||||
readProjectInfos(projectsPath);
|
||||
}
|
||||
|
||||
public RMProjectInfo getProjectInfo(@NotNull String projectId) {
|
||||
return projectsInfo.computeIfAbsent(projectId, key -> new RMProjectInfo());
|
||||
}
|
||||
|
||||
public void updateProjectInfo(@NotNull String projectId, @Nullable RMProjectInfo projectInfo) {
|
||||
if (projectInfo != null) {
|
||||
projectsInfo.put(projectId, projectInfo);
|
||||
} else {
|
||||
projectsInfo.remove(projectId);
|
||||
}
|
||||
saveProjectsInfo();
|
||||
}
|
||||
|
||||
private void readProjectInfos(@NotNull Path path) {
|
||||
if (!Files.exists(path)) {
|
||||
return;
|
||||
}
|
||||
Path projectInfoFile = path.resolve(PROJECTS_INFO_FILE_NAME);
|
||||
if (!Files.exists(projectInfoFile)) {
|
||||
loadAndMigrateProjectDataToCommonFile(path);
|
||||
saveProjectsInfo();
|
||||
return;
|
||||
}
|
||||
readProjectInfosFromFile(projectInfoFile);
|
||||
}
|
||||
|
||||
private void readProjectInfosFromFile(@NotNull Path projectInfoFile) {
|
||||
try {
|
||||
log.info("Reading project metadata information");
|
||||
String content = Files.readString(projectInfoFile, StandardCharsets.UTF_8);
|
||||
Map<String, RMProjectInfo> loaded = JSONUtils.GSON.fromJson(
|
||||
content,
|
||||
TypeToken.getParameterized(Map.class, String.class, RMProjectInfo.class).getType()
|
||||
);
|
||||
projectsInfo.clear();
|
||||
if (loaded != null) {
|
||||
projectsInfo.putAll(loaded);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Error reading existing " + PROJECTS_INFO_FILE_NAME, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAndMigrateProjectDataToCommonFile(@NotNull Path path) {
|
||||
log.info("Migrating shared project information to the common place");
|
||||
Map<String, RMProjectInfo> infos = new LinkedHashMap<>();
|
||||
try (Stream<Path> stream = Files.list(path)) {
|
||||
stream.filter(Files::isDirectory).forEach(projectDir -> {
|
||||
RMProjectInfo info = getProjectInfoFromProjectSettings(projectDir);
|
||||
String projectId = RMUtils.makeProjectIdFromPath(projectDir, RMProjectType.SHARED);
|
||||
infos.put(projectId, info);
|
||||
});
|
||||
} catch (IOException e) {
|
||||
log.error("Error listing shared projects path", e);
|
||||
}
|
||||
log.info("Migration for project information completed");
|
||||
projectsInfo.clear();
|
||||
projectsInfo.putAll(infos);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private RMProjectInfo getProjectInfoFromProjectSettings(@NotNull Path projectPath) {
|
||||
String name = null;
|
||||
String description = null;
|
||||
|
||||
Path settings = projectPath.resolve(DBPProject.METADATA_FOLDER).resolve(BaseProjectImpl.SETTINGS_STORAGE_FILE);
|
||||
if (Files.exists(settings) && Files.isRegularFile(settings)) {
|
||||
try {
|
||||
String json = Files.readString(settings, StandardCharsets.UTF_8);
|
||||
Map<String, Object> map = JSONUtils.GSON.fromJson(json, JSONUtils.MAP_TYPE_TOKEN);
|
||||
name = JSONUtils.getString(map, BaseProjectImpl.PROP_PROJECT_NAME);
|
||||
description = JSONUtils.getString(map, BaseProjectImpl.PROP_PROJECT_DESCRIPTION);
|
||||
} catch (IOException e) {
|
||||
log.warn("Failed to read project settings for " + projectPath + ": " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to parse project settings for " + projectPath + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
RMProjectInfo info = new RMProjectInfo();
|
||||
info.setName(CommonUtils.isEmpty(name) ? projectPath.getFileName().toString() : name);
|
||||
info.setDescription(description);
|
||||
return info;
|
||||
}
|
||||
|
||||
private void saveProjectsInfo() {
|
||||
try (var lock = lockController.lock(LockTarget.of(PROJECTS_INFO_FILE_NAME), LockOptions.of("saveProjectsInfo"))) {
|
||||
log.info("Saving project information");
|
||||
Files.writeString(projectsPath.resolve(PROJECTS_INFO_FILE_NAME), JSONUtils.GSON.toJson(projectsInfo));
|
||||
} catch (IOException e) {
|
||||
log.error("Error writing " + PROJECTS_INFO_FILE_NAME, e);
|
||||
} catch (DBException e) {
|
||||
log.error("Error locking file " + PROJECTS_INFO_FILE_NAME + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -18,8 +18,10 @@ package io.cloudbeaver.model.rm.local;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectInfo;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.dbeaver.registry.project.LocalProjectImpl;
|
||||
@@ -29,6 +31,8 @@ import java.nio.file.Path;
|
||||
public class RMLocalProject extends LocalProjectImpl {
|
||||
@NotNull
|
||||
private final RMProjectType projectType;
|
||||
@Nullable
|
||||
private RMProjectInfo projectInfo;
|
||||
|
||||
public RMLocalProject(
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@@ -51,6 +55,39 @@ public class RMLocalProject extends LocalProjectImpl {
|
||||
return RMUtils.makeProjectIdFromPath(projectPath, projectType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getDescription() {
|
||||
if (projectInfo == null) {
|
||||
return null;
|
||||
}
|
||||
return projectInfo.getDescription();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
if (projectInfo == null || projectInfo.getName() == null) {
|
||||
return projectPath.getFileName().toString();
|
||||
}
|
||||
return projectInfo.getName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public RMProjectType getProjectType() {
|
||||
return projectType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProject(@Nullable String newName, @Nullable String description) throws DBException {
|
||||
this.projectInfo = new RMProjectInfo();
|
||||
projectInfo.setName(newName);
|
||||
projectInfo.setDescription(description);
|
||||
}
|
||||
|
||||
public void setProjectInfo(@NotNull RMProjectInfo projectInfo) {
|
||||
this.projectInfo = projectInfo;
|
||||
}
|
||||
|
||||
public boolean canUpdateProjectName() {
|
||||
return RMProjectType.SHARED.equals(projectType);
|
||||
|
||||
+26
-14
@@ -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.
|
||||
@@ -21,7 +21,6 @@ import io.cloudbeaver.model.app.ServletApplication;
|
||||
import io.cloudbeaver.model.app.ServletAuthApplication;
|
||||
import io.cloudbeaver.websocket.CBWebSessionEventHandler;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.auth.SMAuthInfo;
|
||||
@@ -56,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();
|
||||
@@ -103,7 +109,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void addSessionError(Throwable exception);
|
||||
public abstract void addSessionError(@NotNull Throwable exception);
|
||||
|
||||
public void addEventHandler(@NotNull CBWebSessionEventHandler handler) {
|
||||
synchronized (sessionEventHandlers) {
|
||||
@@ -117,11 +123,11 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
|
||||
public boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
|
||||
return userContext.refresh(smAuthInfo);
|
||||
}
|
||||
|
||||
public synchronized void refreshUserData() {
|
||||
public void refreshUserData() {
|
||||
try {
|
||||
userContext.refreshPermissions();
|
||||
if (userContext.isAuthorizedInSecurityManager()) {
|
||||
@@ -151,8 +157,8 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
return workspace.getAuthContext();
|
||||
}
|
||||
|
||||
protected void clearSessionContext() {
|
||||
this.workspace.getAuthContext().clear();
|
||||
protected synchronized void clearSessionContext() {
|
||||
this.workspace.getAuthContext().clearContext(false);
|
||||
this.workspace.getAuthContext().addSession(this);
|
||||
}
|
||||
|
||||
@@ -177,23 +183,29 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
return lastAccessTime;
|
||||
}
|
||||
|
||||
public synchronized void touchSession() {
|
||||
@NotNull
|
||||
public SessionType getSessionType() {
|
||||
return sessionType;
|
||||
}
|
||||
|
||||
public void touchSession() {
|
||||
this.lastAccessTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public synchronized WebUserContext getUserContext() {
|
||||
public WebUserContext getUserContext() {
|
||||
return userContext;
|
||||
}
|
||||
|
||||
@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) {
|
||||
@@ -227,7 +239,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
this.eventsFilter = eventsFilter;
|
||||
}
|
||||
|
||||
public boolean isProjectAccessible(String projectId) {
|
||||
public boolean isProjectAccessible(@NotNull String projectId) {
|
||||
return userContext.getAccessibleProjectIds().contains(projectId);
|
||||
}
|
||||
|
||||
@@ -240,11 +252,11 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
}
|
||||
|
||||
|
||||
public void removeSessionProject(@Nullable String projectId) throws DBException {
|
||||
public void removeSessionProject(@NotNull String projectId) throws DBException {
|
||||
userContext.getAccessibleProjectIds().remove(projectId);
|
||||
}
|
||||
|
||||
public abstract void addSessionMessage(WebServerMessage message);
|
||||
public abstract void addSessionMessage(@NotNull WebServerMessage message);
|
||||
|
||||
@Property
|
||||
public boolean isValid() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
+10
-7
@@ -41,6 +41,7 @@ public class WebAuthInfo implements SMSessionPrincipal, WebUserAuthToken {
|
||||
|
||||
private final WebSession session;
|
||||
private final WebUser user;
|
||||
@NotNull
|
||||
private final WebAuthProviderDescriptor authProvider;
|
||||
private String authProviderConfigurationId;
|
||||
@NotNull
|
||||
@@ -71,6 +72,7 @@ public class WebAuthInfo implements SMSessionPrincipal, WebUserAuthToken {
|
||||
this.loginTime = loginTime;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public OffsetDateTime getLoginTime() {
|
||||
return loginTime;
|
||||
@@ -91,16 +93,19 @@ public class WebAuthInfo implements SMSessionPrincipal, WebUserAuthToken {
|
||||
return new WebUserOriginInfo(session, user, authProvider);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public String getUserId() {
|
||||
return userIdentity.getId();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public String getDisplayName() {
|
||||
return userIdentity.getDisplayName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public String getAuthProvider() {
|
||||
return authProvider.getId();
|
||||
@@ -138,13 +143,11 @@ public class WebAuthInfo implements SMSessionPrincipal, WebUserAuthToken {
|
||||
}
|
||||
|
||||
void closeAuth() {
|
||||
if (authProvider != null && authSession != null) {
|
||||
try {
|
||||
SMAuthProvider authProviderInstance = this.authProvider.getInstance();
|
||||
authProviderInstance.closeSession(session, authSession);
|
||||
} catch (Exception e) {
|
||||
log.error(e);
|
||||
}
|
||||
try {
|
||||
SMAuthProvider authProviderInstance = this.authProvider.getInstance();
|
||||
authProviderInstance.closeSession(session, authSession);
|
||||
} catch (Exception e) {
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -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,16 +31,16 @@ public class WebHeadlessSession extends BaseWebSession {
|
||||
@NotNull String id,
|
||||
@NotNull ServletAuthApplication application
|
||||
) throws DBException {
|
||||
super(id, application);
|
||||
super(id, application, SessionType.HEADLESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSessionError(Throwable exception) {
|
||||
public void addSessionError(@NotNull Throwable exception) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSessionMessage(WebServerMessage message) {
|
||||
public void addSessionMessage(@NotNull WebServerMessage message) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
+26
-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,13 +49,25 @@ public class WebHttpRequestInfo {
|
||||
@Nullable Object locale,
|
||||
@Nullable String lastRemoteAddress,
|
||||
@Nullable String lastRemoteUserAgent
|
||||
) {
|
||||
this(id, locale, lastRemoteAddress, lastRemoteUserAgent, SessionType.WEB);
|
||||
}
|
||||
|
||||
public WebHttpRequestInfo(
|
||||
@Nullable String id,
|
||||
@Nullable Object locale,
|
||||
@Nullable String lastRemoteAddress,
|
||||
@Nullable String lastRemoteUserAgent,
|
||||
@NotNull SessionType sessionType
|
||||
) {
|
||||
this.id = id;
|
||||
this.locale = locale;
|
||||
this.lastRemoteAddress = lastRemoteAddress;
|
||||
this.lastRemoteUserAgent = lastRemoteUserAgent;
|
||||
this.sessionType = sessionType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -69,4 +86,9 @@ public class WebHttpRequestInfo {
|
||||
public String getLastRemoteUserAgent() {
|
||||
return lastRemoteUserAgent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SessionType getSessionType() {
|
||||
return sessionType;
|
||||
}
|
||||
}
|
||||
|
||||
+199
-168
@@ -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,10 +17,7 @@
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.model.CustomCancelableJob;
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.WebServerMessage;
|
||||
import io.cloudbeaver.model.*;
|
||||
import io.cloudbeaver.model.app.ServletApplication;
|
||||
import io.cloudbeaver.model.app.ServletAuthApplication;
|
||||
import io.cloudbeaver.model.session.monitor.TaskProgressMonitor;
|
||||
@@ -30,7 +27,6 @@ import io.cloudbeaver.service.sql.WebSQLConstants;
|
||||
import io.cloudbeaver.utils.CBModelConstants;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.jkiss.code.NotNull;
|
||||
@@ -38,6 +34,7 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBFileController;
|
||||
import org.jkiss.dbeaver.model.DBPAdaptable;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBPEventListener;
|
||||
import org.jkiss.dbeaver.model.access.DBAAuthCredentials;
|
||||
@@ -45,7 +42,6 @@ import org.jkiss.dbeaver.model.access.DBACredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.auth.*;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.exec.DBCException;
|
||||
import org.jkiss.dbeaver.model.fs.DBFFileSystemManager;
|
||||
import org.jkiss.dbeaver.model.meta.Association;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNModel;
|
||||
@@ -68,7 +64,8 @@ import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.stream.Collectors;
|
||||
/**
|
||||
* Web session.
|
||||
@@ -76,16 +73,16 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
//TODO: split to authenticated and non authenticated context
|
||||
public class WebSession extends BaseWebSession
|
||||
implements SMSessionWithAuth, SMCredentialsProvider, DBACredentialsProvider, IAdaptable {
|
||||
implements SMSessionWithAuth, SMCredentialsProvider, DBACredentialsProvider, DBPAdaptable {
|
||||
|
||||
private static final Log log = Log.getLog(WebSession.class);
|
||||
|
||||
public static final SMSessionType CB_SESSION_TYPE = new SMSessionType("CloudBeaver");
|
||||
private static final String WEB_SESSION_AUTH_CONTEXT_TYPE = "web-session";
|
||||
private static final String ATTR_LOCALE = "locale";
|
||||
private static final AtomicInteger TASK_ID = new AtomicInteger();
|
||||
|
||||
public static String RUNTIME_PARAM_AUTH_INFOS = "auth-infos";
|
||||
public static String RUNTIME_PARAM_CLIENT_ORIGIN = "client-origin";
|
||||
private final AtomicInteger taskCount = new AtomicInteger();
|
||||
|
||||
private final String lastRemoteAddr;
|
||||
@@ -93,31 +90,34 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
private String locale;
|
||||
private boolean cacheExpired;
|
||||
private String clientOrigin;
|
||||
|
||||
protected WebSessionGlobalProjectImpl globalProject;
|
||||
private final List<WebServerMessage> sessionMessages = new ArrayList<>();
|
||||
|
||||
private final Map<String, WebAsyncTaskInfo> asyncTasks = new HashMap<>();
|
||||
private final Map<String, Function<Object, Object>> attributeDisposers = new HashMap<>();
|
||||
|
||||
// Map of auth tokens. Key is authentication provider
|
||||
private final List<WebAuthInfo> authTokens = new ArrayList<>();
|
||||
|
||||
private DBNModel navigatorModel;
|
||||
private final ReadWriteLock navigatorModelLock = new ReentrantReadWriteLock(true);
|
||||
private final DBRProgressMonitor progressMonitor = new SessionProgressMonitor();
|
||||
private final Map<String, DBWSessionHandler> sessionHandlers;
|
||||
private final Map<String, DBWSessionHandler<WebSession>> sessionHandlers;
|
||||
private final WebDataSourceConnectEventListener connectListener = new WebDataSourceConnectEventListener(this);
|
||||
|
||||
public WebSession(
|
||||
@NotNull WebHttpRequestInfo requestInfo,
|
||||
@NotNull ServletAuthApplication application,
|
||||
@NotNull Map<String, DBWSessionHandler> sessionHandlers
|
||||
@NotNull Map<String, DBWSessionHandler<WebSession>> sessionHandlers
|
||||
) throws DBException {
|
||||
this(requestInfo.getId(),
|
||||
this(
|
||||
Objects.requireNonNull(requestInfo.getId()),
|
||||
CommonUtils.toString(requestInfo.getLocale()),
|
||||
application,
|
||||
sessionHandlers,
|
||||
requestInfo.getLastRemoteAddress()
|
||||
requestInfo.getLastRemoteAddress(),
|
||||
requestInfo.getSessionType()
|
||||
);
|
||||
updateSessionParameters(requestInfo);
|
||||
}
|
||||
@@ -126,10 +126,21 @@ public class WebSession extends BaseWebSession
|
||||
@NotNull String id,
|
||||
@Nullable String locale,
|
||||
@NotNull ServletApplication application,
|
||||
@NotNull Map<String, DBWSessionHandler> sessionHandlers,
|
||||
@NotNull String remoteAddr
|
||||
@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");
|
||||
}
|
||||
@@ -152,7 +163,7 @@ public class WebSession extends BaseWebSession
|
||||
if (authTokens.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return authTokens.get(0);
|
||||
return authTokens.getFirst();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,11 +200,12 @@ public class WebSession extends BaseWebSession
|
||||
this.cacheExpired = cacheExpired;
|
||||
}
|
||||
|
||||
public synchronized WebUser getUser() {
|
||||
public WebUser getUser() {
|
||||
return this.userContext.getUser();
|
||||
}
|
||||
|
||||
public synchronized Map<String, String> getUserMetaParameters() {
|
||||
@NotNull
|
||||
public Map<String, String> getUserMetaParameters() {
|
||||
var user = getUser();
|
||||
if (user == null) {
|
||||
return Map.of();
|
||||
@@ -205,20 +217,20 @@ public class WebSession extends BaseWebSession
|
||||
return allMetaParams;
|
||||
}
|
||||
|
||||
public synchronized String getUserId() {
|
||||
public String getUserId() {
|
||||
return userContext.getUserId();
|
||||
}
|
||||
|
||||
public synchronized boolean hasPermission(String perm) {
|
||||
return getSessionPermissions().contains(DBWConstants.PERMISSION_ADMIN) ||
|
||||
getSessionPermissions().contains(perm);
|
||||
Set<String> sessionPermissions = getSessionPermissions();
|
||||
return sessionPermissions.contains(DBWConstants.PERMISSION_ADMIN) || sessionPermissions.contains(perm);
|
||||
}
|
||||
|
||||
public synchronized boolean isAuthorizedInSecurityManager() {
|
||||
public boolean isAuthorizedInSecurityManager() {
|
||||
return userContext.isAuthorizedInSecurityManager();
|
||||
}
|
||||
|
||||
public synchronized Set<String> getSessionPermissions() {
|
||||
public Set<String> getSessionPermissions() {
|
||||
if (userContext.getUserPermissions() == null) {
|
||||
refreshSessionAuth();
|
||||
}
|
||||
@@ -226,28 +238,30 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public synchronized SMController getSecurityController() {
|
||||
public SMController getSecurityController() {
|
||||
return userContext.getSecurityController();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public synchronized SMAdminController getAdminSecurityController() throws DBException {
|
||||
public SMAdminController getAdminSecurityController() throws DBException {
|
||||
if (!hasPermission(DBWConstants.PERMISSION_ADMIN)) {
|
||||
throw new DBException("Admin permissions required");
|
||||
}
|
||||
return userContext.getAdminSecurityController();
|
||||
}
|
||||
|
||||
public synchronized RMController getRmController() {
|
||||
@NotNull
|
||||
public RMController getRmController() {
|
||||
return userContext.getRmController();
|
||||
}
|
||||
|
||||
public synchronized DBFileController getFileController() {
|
||||
@NotNull
|
||||
public DBFileController getFileController() {
|
||||
return userContext.getFileController();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void refreshUserData() {
|
||||
public void refreshUserData() {
|
||||
super.refreshUserData();
|
||||
refreshSessionAuth();
|
||||
|
||||
@@ -255,39 +269,58 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
// Note: for admin use only
|
||||
public synchronized void resetUserState() throws DBException {
|
||||
public void resetUserState(boolean needResetUserCache) throws DBException {
|
||||
clearAuthTokens();
|
||||
try {
|
||||
resetSessionCache();
|
||||
} catch (DBCException e) {
|
||||
addSessionError(e);
|
||||
log.error(e);
|
||||
if (needResetUserCache) {
|
||||
try {
|
||||
resetSessionCache();
|
||||
} catch (DBCException e) {
|
||||
addSessionError(e);
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
refreshUserData();
|
||||
clearSessionContext();
|
||||
}
|
||||
|
||||
public void resetUserState() throws DBException {
|
||||
resetUserState(true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBPEventListener getDataSourceConnectListener() {
|
||||
return connectListener;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getClientOrigin() {
|
||||
return clientOrigin;
|
||||
}
|
||||
|
||||
public void updateClientOrigin(@NotNull String originFromRequest) {
|
||||
this.clientOrigin = originFromRequest;
|
||||
}
|
||||
|
||||
private void initNavigatorModel() {
|
||||
navigatorModelLock.writeLock().lock();
|
||||
try {
|
||||
// Cleanup current data
|
||||
if (this.navigatorModel != null) {
|
||||
this.navigatorModel.dispose();
|
||||
this.navigatorModel = null;
|
||||
}
|
||||
this.globalProject = null;
|
||||
|
||||
// Cleanup current data
|
||||
if (this.navigatorModel != null) {
|
||||
this.navigatorModel.dispose();
|
||||
this.navigatorModel = null;
|
||||
loadProjects();
|
||||
|
||||
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), getWorkspace().getProjects());
|
||||
this.navigatorModel.setModelAuthContext(getWorkspace().getAuthContext());
|
||||
this.navigatorModel.initialize();
|
||||
|
||||
this.locale = Locale.getDefault().getLanguage();
|
||||
} finally {
|
||||
navigatorModelLock.writeLock().unlock();
|
||||
}
|
||||
this.globalProject = null;
|
||||
|
||||
loadProjects();
|
||||
|
||||
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), getWorkspace().getProjects());
|
||||
this.navigatorModel.setModelAuthContext(getWorkspace().getAuthContext());
|
||||
this.navigatorModel.initialize();
|
||||
|
||||
this.locale = Locale.getDefault().getLanguage();
|
||||
}
|
||||
|
||||
private void loadProjects() {
|
||||
@@ -310,7 +343,7 @@ public class WebSession extends BaseWebSession
|
||||
anonymousProject.setInMemory(true);
|
||||
}
|
||||
if (workspace.getActiveProject() == null && !workspace.getProjects().isEmpty()) {
|
||||
workspace.setActiveProject(workspace.getProjects().get(0));
|
||||
workspace.setActiveProject(workspace.getProjects().getFirst());
|
||||
}
|
||||
} catch (DBException e) {
|
||||
addSessionError(e);
|
||||
@@ -318,14 +351,14 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
private WebSessionProjectImpl createWebProject(RMProject project) throws DBException {
|
||||
@NotNull
|
||||
private WebSessionProjectImpl createWebProject(@NotNull RMProject project) throws DBException {
|
||||
WebSessionProjectImpl sessionProject;
|
||||
if (project.isGlobal()) {
|
||||
sessionProject = createGlobalProject(project);
|
||||
} else {
|
||||
sessionProject = createSessionProject(project);
|
||||
}
|
||||
sessionProject.refreshProjectSettings();
|
||||
// do not load data sources for anonymous project
|
||||
if (project.getType() == RMProjectType.USER && userContext.getUser() == null) {
|
||||
sessionProject.setInMemory(true);
|
||||
@@ -334,13 +367,10 @@ public class WebSession extends BaseWebSession
|
||||
if (!project.isShared() || application.isConfigurationMode()) {
|
||||
getWorkspace().setActiveProject(sessionProject);
|
||||
}
|
||||
log.info(String.format(
|
||||
"Project created: [ID=%s, Name=%s, Type=%s, Creator=%s]",
|
||||
project.getId(), project.getName(), project.getType(), project.getCreator()
|
||||
));
|
||||
return sessionProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected WebSessionProjectImpl createSessionProject(@NotNull RMProject project) throws DBException {
|
||||
return new WebSessionProjectImpl(this, project, getProjectPath(project));
|
||||
}
|
||||
@@ -350,32 +380,24 @@ public class WebSession extends BaseWebSession
|
||||
return RMUtils.getProjectPath(project);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected WebSessionProjectImpl createGlobalProject(RMProject project) {
|
||||
globalProject = new WebSessionGlobalProjectImpl(this, project);
|
||||
globalProject.refreshAccessibleConnectionIds();
|
||||
return globalProject;
|
||||
}
|
||||
|
||||
private void resetSessionCache() throws DBCException {
|
||||
// Clear attributes
|
||||
synchronized (attributes) {
|
||||
for (Map.Entry<String, Function<Object, Object>> attrDisposer : attributeDisposers.entrySet()) {
|
||||
Object attrValue = attributes.get(attrDisposer.getKey());
|
||||
attrDisposer.getValue().apply(attrValue);
|
||||
}
|
||||
attributeDisposers.clear();
|
||||
// Remove all non-persistent attributes
|
||||
attributes.entrySet().removeIf(
|
||||
entry -> !(entry.getValue() instanceof PersistentAttribute));
|
||||
}
|
||||
}
|
||||
|
||||
private void resetNavigationModel() {
|
||||
getWorkspace().getProjects().forEach(WebSessionProjectImpl::dispose);
|
||||
navigatorModelLock.writeLock().lock();
|
||||
try {
|
||||
getWorkspace().getProjects().forEach(WebSessionProjectImpl::dispose);
|
||||
|
||||
if (this.navigatorModel != null) {
|
||||
this.navigatorModel.dispose();
|
||||
this.navigatorModel = null;
|
||||
if (this.navigatorModel != null) {
|
||||
this.navigatorModel.dispose();
|
||||
this.navigatorModel = null;
|
||||
}
|
||||
} finally {
|
||||
navigatorModelLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,15 +440,25 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
@Nullable
|
||||
public DBNModel getNavigatorModel() {
|
||||
return navigatorModel;
|
||||
navigatorModelLock.readLock().lock();
|
||||
try {
|
||||
return navigatorModel;
|
||||
} finally {
|
||||
navigatorModelLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBNModel getNavigatorModelOrThrow() throws DBWebException {
|
||||
if (navigatorModel != null) {
|
||||
return navigatorModel;
|
||||
navigatorModelLock.readLock().lock();
|
||||
try {
|
||||
if (navigatorModel != null) {
|
||||
return navigatorModel;
|
||||
}
|
||||
throw new DBWebException("Navigator model is not found in session");
|
||||
} finally {
|
||||
navigatorModelLock.readLock().unlock();
|
||||
}
|
||||
throw new DBWebException("Navigator model is not found in session");
|
||||
}
|
||||
/**
|
||||
* Returns and clears progress messages
|
||||
@@ -440,8 +472,8 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void updateInfo(boolean isOldHttpSessionUsed) {
|
||||
log.debug("Update session lifetime " + getSessionId() + " for user " + getUserId());
|
||||
public void updateInfo(boolean isOldHttpSessionUsed) {
|
||||
log.trace("Update session lifetime " + getSessionId() + " for user " + getUserId());
|
||||
touchSession();
|
||||
if (isOldHttpSessionUsed) {
|
||||
try {
|
||||
@@ -472,7 +504,6 @@ public class WebSession extends BaseWebSession
|
||||
public void close() {
|
||||
try {
|
||||
resetNavigationModel();
|
||||
resetSessionCache();
|
||||
} catch (Throwable e) {
|
||||
log.error(e);
|
||||
}
|
||||
@@ -489,7 +520,6 @@ public class WebSession extends BaseWebSession
|
||||
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
|
||||
try {
|
||||
resetNavigationModel();
|
||||
resetSessionCache();
|
||||
} catch (Throwable e) {
|
||||
log.error(e);
|
||||
}
|
||||
@@ -504,6 +534,7 @@ public class WebSession extends BaseWebSession
|
||||
super.close(clearTokens, sendSessionExpiredEvent);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<WebAuthInfo> clearAuthTokens() throws DBException {
|
||||
ArrayList<WebAuthInfo> tokensCopy;
|
||||
synchronized (authTokens) {
|
||||
@@ -560,6 +591,10 @@ public class WebSession extends BaseWebSession
|
||||
if (job instanceof CustomCancelableJob cancelableJob) {
|
||||
cancelableJob.cancelJob(this, taskInfo);
|
||||
}
|
||||
CompletableFuture<?> future = getAttribute(getTaskConfirmationAttributeName(taskId));
|
||||
if (future != null) {
|
||||
future.cancel(false);
|
||||
}
|
||||
if (job != null) {
|
||||
job.cancel();
|
||||
}
|
||||
@@ -569,8 +604,7 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
public WebAsyncTaskInfo createAsyncTask(@NotNull String taskName) {
|
||||
int taskId = TASK_ID.incrementAndGet();
|
||||
WebAsyncTaskInfo asyncTask = getAsyncTask(String.valueOf(taskId), taskName, true);
|
||||
return asyncTask;
|
||||
return getAsyncTask(String.valueOf(taskId), taskName, true);
|
||||
}
|
||||
|
||||
public List<WebAsyncTaskInfo> findTasksByJob(@NotNull Class<? extends AbstractJob> jobClass) {
|
||||
@@ -585,15 +619,18 @@ 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 AbstractJob(asyncTask.getName()) {
|
||||
AbstractJob job = new AbstractCancelableJob(asyncTask.getName()) {
|
||||
@NotNull
|
||||
@Override
|
||||
protected IStatus run(DBRProgressMonitor monitor) {
|
||||
protected IStatus run(@NotNull DBRProgressMonitor monitor) {
|
||||
int curTaskCount = taskCount.incrementAndGet();
|
||||
|
||||
DBRProgressMonitor taskMonitor = new TaskProgressMonitor(monitor, WebSession.this, asyncTask);
|
||||
@@ -629,11 +666,11 @@ public class WebSession extends BaseWebSession
|
||||
return asyncTask;
|
||||
}
|
||||
|
||||
public void addSessionError(Throwable exception) {
|
||||
public void addSessionError(@NotNull Throwable exception) {
|
||||
addSessionMessage(new WebServerMessage(exception));
|
||||
}
|
||||
|
||||
public void addSessionMessage(WebServerMessage message) {
|
||||
public void addSessionMessage(@NotNull WebServerMessage message) {
|
||||
synchronized (sessionMessages) {
|
||||
sessionMessages.add(message);
|
||||
}
|
||||
@@ -644,14 +681,15 @@ public class WebSession extends BaseWebSession
|
||||
message.getMessage()));
|
||||
}
|
||||
|
||||
public void addInfoMessage(String message) {
|
||||
public void addInfoMessage(@NotNull String message) {
|
||||
addSessionMessage(new WebServerMessage(MessageType.INFO, message));
|
||||
}
|
||||
|
||||
public void addWarningMessage(String message) {
|
||||
public void addWarningMessage(@NotNull String message) {
|
||||
addSessionMessage(new WebServerMessage(MessageType.WARNING, message));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<WebServerMessage> readLog(Integer maxEntries, Boolean clearLog) {
|
||||
synchronized (sessionMessages) {
|
||||
List<WebServerMessage> messages = new ArrayList<>();
|
||||
@@ -671,42 +709,6 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getAttribute(String name) {
|
||||
synchronized (attributes) {
|
||||
Object value = attributes.get(name);
|
||||
if (value instanceof PersistentAttribute persistentAttribute) {
|
||||
value = persistentAttribute.value();
|
||||
}
|
||||
return (T) value;
|
||||
}
|
||||
}
|
||||
|
||||
public void setAttribute(String name, Object value, boolean persistent) {
|
||||
synchronized (attributes) {
|
||||
attributes.put(name, persistent ? new PersistentAttribute(value) : value);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T getAttribute(String name, Function<T, T> creator, Function<T, T> disposer) {
|
||||
synchronized (attributes) {
|
||||
Object value = attributes.get(name);
|
||||
if (value instanceof PersistentAttribute persistentAttribute) {
|
||||
value = persistentAttribute.value();
|
||||
}
|
||||
if (value == null) {
|
||||
value = creator.apply(null);
|
||||
if (value != null) {
|
||||
attributes.put(name, value);
|
||||
if (disposer != null) {
|
||||
attributeDisposers.put(name, (Function<Object, Object>) disposer);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (T) value;
|
||||
}
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getActionParameters() {
|
||||
WebActionParameters action = WebActionParameters.fromSession(this, true);
|
||||
@@ -724,7 +726,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return authTokens.isEmpty() ? null : authTokens.get(0);
|
||||
return authTokens.isEmpty() ? null : authTokens.getFirst();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -735,7 +737,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public List<WebAuthInfo> getAllAuthInfo() {
|
||||
synchronized (authTokens) {
|
||||
return new ArrayList<>(authTokens);
|
||||
@@ -767,10 +769,7 @@ public class WebSession extends BaseWebSession
|
||||
if (oldAuthInfo != null) {
|
||||
removeAuthInfo(oldAuthInfo);
|
||||
}
|
||||
SMSession authSession = authInfo.getAuthSession();
|
||||
if (authSession != null) {
|
||||
getSessionContext().addSession(authSession);
|
||||
}
|
||||
getSessionContext().addSession(authInfo.getAuthSession());
|
||||
}
|
||||
synchronized (authTokens) {
|
||||
Collections.addAll(authTokens, tokens);
|
||||
@@ -790,7 +789,7 @@ public class WebSession extends BaseWebSession
|
||||
});
|
||||
}
|
||||
|
||||
private void removeAuthInfo(WebAuthInfo oldAuthInfo) {
|
||||
private void removeAuthInfo(@NotNull WebAuthInfo oldAuthInfo) {
|
||||
oldAuthInfo.closeAuth();
|
||||
synchronized (authTokens) {
|
||||
authTokens.remove(oldAuthInfo);
|
||||
@@ -816,6 +815,7 @@ public class WebSession extends BaseWebSession
|
||||
return oldInfo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<DBACredentialsProvider> getContextCredentialsProviders() {
|
||||
return getAdapters(DBACredentialsProvider.class);
|
||||
}
|
||||
@@ -834,6 +834,7 @@ public class WebSession extends BaseWebSession
|
||||
contextCredentialsProvider.provideAuthParameters(monitor, dataSourceContainer, configuration);
|
||||
}
|
||||
configuration.setRuntimeAttribute(RUNTIME_PARAM_AUTH_INFOS, getAllAuthInfo());
|
||||
configuration.setRuntimeAttribute(RUNTIME_PARAM_CLIENT_ORIGIN, this.clientOrigin);
|
||||
|
||||
WebSessionProjectImpl project = getProjectById(dataSourceContainer.getProject().getId());
|
||||
if (project != null) {
|
||||
@@ -846,7 +847,7 @@ public class WebSession extends BaseWebSession
|
||||
// uncommented because we had the problem with non-native auth models
|
||||
// (for example, can't connect to DynamoDB if credentials are not saved)
|
||||
DBAAuthCredentials credentials = configuration.getAuthModel().loadCredentials(dataSourceContainer, configuration);
|
||||
WebDataSourceUtils.updateCredentialsFromProperties(credentials, configuration.getAuthProperties());
|
||||
WebDataSourceUtils.updateCredentialsFromProperties(this.progressMonitor, credentials, configuration.getAuthProperties());
|
||||
|
||||
configuration.getAuthModel().provideCredentials(dataSourceContainer, configuration, credentials);
|
||||
} catch (DBException e) {
|
||||
@@ -864,7 +865,7 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
// May be called to extract auth information from session
|
||||
@Override
|
||||
public <T> T getAdapter(Class<T> adapter) {
|
||||
public <T> T getAdapter(@NotNull Class<T> adapter) {
|
||||
synchronized (authTokens) {
|
||||
for (WebAuthInfo authInfo : authTokens) {
|
||||
if (isAuthInfoInstanceOf(authInfo, adapter)) {
|
||||
@@ -886,7 +887,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
private <T> boolean isAuthInfoInstanceOf(WebAuthInfo authInfo, Class<T> adapter) {
|
||||
if (authInfo != null && authInfo.getAuthSession() != null) {
|
||||
if (authInfo != null) {
|
||||
return adapter.isInstance(authInfo.getAuthSession());
|
||||
}
|
||||
return false;
|
||||
@@ -902,11 +903,11 @@ public class WebSession extends BaseWebSession
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public synchronized void resetAuthToken() throws DBException {
|
||||
public void resetAuthToken() throws DBException {
|
||||
this.userContext.reset();
|
||||
}
|
||||
|
||||
public synchronized boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
|
||||
public boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
|
||||
boolean contextChanged = super.updateSMSession(smAuthInfo);
|
||||
if (contextChanged) {
|
||||
refreshUserData();
|
||||
@@ -925,7 +926,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WebSessionProjectImpl getProjectById(@Nullable String projectId) {
|
||||
public WebSessionProjectImpl getProjectById(@NotNull String projectId) {
|
||||
return getWorkspace().getProjectById(projectId);
|
||||
}
|
||||
|
||||
@@ -934,6 +935,7 @@ public class WebSession extends BaseWebSession
|
||||
*
|
||||
* @throws DBWebException if project with provided id is not found.
|
||||
*/
|
||||
@NotNull
|
||||
public WebSessionProjectImpl getAccessibleProjectById(@Nullable String projectId) throws DBWebException {
|
||||
WebSessionProjectImpl project = null;
|
||||
if (projectId != null) {
|
||||
@@ -945,6 +947,7 @@ public class WebSession extends BaseWebSession
|
||||
return project;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<WebSessionProjectImpl> getAccessibleProjects() {
|
||||
return getWorkspace().getProjects();
|
||||
}
|
||||
@@ -954,26 +957,35 @@ public class WebSession extends BaseWebSession
|
||||
*/
|
||||
public void addSessionProject(@NotNull WebSessionProjectImpl project) {
|
||||
getWorkspace().addProject(project);
|
||||
if (navigatorModel != null) {
|
||||
navigatorModel.getRoot().addProject(project, false);
|
||||
navigatorModelLock.readLock().lock();
|
||||
try {
|
||||
if (navigatorModel != null) {
|
||||
navigatorModel.getRoot().addProject(project, false);
|
||||
}
|
||||
} finally {
|
||||
navigatorModelLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes project from session cache and navigator tree.
|
||||
*/
|
||||
public void deleteSessionProject(@Nullable WebSessionProjectImpl project) {
|
||||
if (project != null) {
|
||||
RMProject rmProject = project.getRMProject();
|
||||
log.info(String.format(
|
||||
"Project deleted: [ID=%s, Name=%s, Type=%s, Creator=%s]",
|
||||
rmProject.getId(), rmProject.getName(), rmProject.getType(), rmProject.getCreator()
|
||||
));
|
||||
project.dispose();
|
||||
}
|
||||
public void deleteSessionProject(@NotNull WebSessionProjectImpl project) {
|
||||
RMProject rmProject = project.getRMProject();
|
||||
log.info(String.format(
|
||||
"Project deleted: [ID=%s, Name=%s, Type=%s, Creator=%s]",
|
||||
rmProject.getId(), rmProject.getName(), rmProject.getType(), rmProject.getCreator()
|
||||
));
|
||||
project.dispose();
|
||||
|
||||
getWorkspace().removeProject(project);
|
||||
if (navigatorModel != null) {
|
||||
navigatorModel.getRoot().removeProject(project);
|
||||
navigatorModelLock.readLock().lock();
|
||||
try {
|
||||
if (navigatorModel != null) {
|
||||
navigatorModel.getRoot().removeProject(project);
|
||||
}
|
||||
} finally {
|
||||
navigatorModelLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,6 +993,10 @@ public class WebSession extends BaseWebSession
|
||||
public void addSessionProject(@NotNull String projectId) throws DBException {
|
||||
super.addSessionProject(projectId);
|
||||
var rmProject = getRmController().getProject(projectId, false, false);
|
||||
if (rmProject == null) {
|
||||
log.error("RM project '" + projectId + "' not found");
|
||||
return;
|
||||
}
|
||||
createWebProject(rmProject);
|
||||
}
|
||||
|
||||
@@ -995,7 +1011,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSessionProject(@Nullable String projectId) throws DBException {
|
||||
public void removeSessionProject(@NotNull String projectId) throws DBException {
|
||||
super.removeSessionProject(projectId);
|
||||
var project = getProjectById(projectId);
|
||||
if (project == null) {
|
||||
@@ -1004,15 +1020,6 @@ public class WebSession extends BaseWebSession
|
||||
deleteSessionProject(project);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBFFileSystemManager getFileSystemManager(String projectId) throws DBException {
|
||||
var project = getProjectById(projectId);
|
||||
if (project == null) {
|
||||
throw new DBException("Project not found: " + projectId);
|
||||
}
|
||||
return project.getFileSystemManager();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebSessionPreferenceStore getUserPreferenceStore() {
|
||||
return getUserContext().getPreferenceStore();
|
||||
@@ -1028,7 +1035,7 @@ public class WebSession extends BaseWebSession
|
||||
boolean confirmed,
|
||||
boolean skipConfirmations
|
||||
) {
|
||||
String attributeName = WebSQLConstants.TASK_CONFIRMATION_ATTR_PREFIX + taskId;
|
||||
String attributeName = getTaskConfirmationAttributeName(taskId);
|
||||
if (confirmed && skipConfirmations) {
|
||||
setAttribute(WebSQLConstants.SKIP_TASK_CONFIRMATIONS_ATTR, Boolean.TRUE);
|
||||
}
|
||||
@@ -1042,14 +1049,40 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
public void handleTaskConfirmationWithParameters(@NotNull String taskId, @NotNull Map<String, Object> parameters) {
|
||||
String attributeName = getTaskConfirmationAttributeName(taskId);
|
||||
CompletableFuture<Map<String, Object>> confirmationFuture = getAttribute(attributeName);
|
||||
if (confirmationFuture != null) {
|
||||
confirmationFuture.complete(parameters);
|
||||
removeAttribute(attributeName);
|
||||
} else {
|
||||
log.error("Received unexpected confirmation event for taskId: " + taskId);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleActionCancelledEvent(@NotNull String actionId) {
|
||||
Object attributeValue = getAttribute(actionId);
|
||||
if (!(attributeValue instanceof CompletableFuture<?> future)) {
|
||||
log.info("Action to be cancelled not found, skipping: " + actionId);
|
||||
return;
|
||||
}
|
||||
future.completeExceptionally(new DBWebException("Action cancelled by user: " + actionId));
|
||||
removeAttribute(actionId);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getTaskConfirmationAttributeName(@NotNull String taskId) {
|
||||
return WebSQLConstants.TASK_CONFIRMATION_ATTR_PREFIX + taskId;
|
||||
}
|
||||
|
||||
private class SessionProgressMonitor extends BaseProgressMonitor {
|
||||
@Override
|
||||
public void beginTask(String name, int totalWork) {
|
||||
public void beginTask(@NotNull String name, int totalWork) {
|
||||
addInfoMessage(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subTask(String name) {
|
||||
public void subTask(@NotNull String name) {
|
||||
addInfoMessage(name);
|
||||
}
|
||||
}
|
||||
@@ -1058,6 +1091,4 @@ public class WebSession extends BaseWebSession
|
||||
return true;
|
||||
}
|
||||
|
||||
private record PersistentAttribute(Object value) {
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ public class WebSessionAuthProcessor {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (resetUserStateOnError) {
|
||||
webSession.resetUserState();
|
||||
webSession.resetUserState(false);
|
||||
}
|
||||
throw new DBException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
+59
-23
@@ -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,11 +17,15 @@
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import io.cloudbeaver.WebSessionProjectImpl;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPAdaptable;
|
||||
import org.jkiss.dbeaver.model.DBPImage;
|
||||
import org.jkiss.dbeaver.model.app.DBPPlatform;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
@@ -36,6 +40,7 @@ import java.util.List;
|
||||
* Web workspace
|
||||
*/
|
||||
public class WebSessionWorkspace implements DBPWorkspace {
|
||||
private static final Log log = Log.getLog(WebSessionWorkspace.class);
|
||||
|
||||
private final BaseWebSession session;
|
||||
private final SessionContextImpl workspaceAuthContext;
|
||||
@@ -87,7 +92,9 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
@NotNull
|
||||
@Override
|
||||
public List<WebSessionProjectImpl> getProjects() {
|
||||
return accessibleProjects;
|
||||
synchronized (accessibleProjects) {
|
||||
return new ArrayList<>(accessibleProjects);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -99,26 +106,30 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
@Nullable
|
||||
@Override
|
||||
public WebSessionProjectImpl getProject(@NotNull String projectName) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
if (project.getName().equals(projectName)) {
|
||||
return project;
|
||||
synchronized (accessibleProjects) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
if (project.getName().equals(projectName)) {
|
||||
return project;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.error("Project name '" + projectName + "' not found in session workspace");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WebSessionProjectImpl getProjectById(@NotNull String projectId) {
|
||||
if (projectId == null) {
|
||||
return activeProject;
|
||||
}
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
if (project.getId().equals(projectId)) {
|
||||
return project;
|
||||
synchronized (accessibleProjects) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
if (project.getId().equals(projectId)) {
|
||||
return project;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
log.error("Project ID '" + projectId + "' not found in session workspace");
|
||||
// FIXME: return null here
|
||||
return activeProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -143,36 +154,61 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setActiveProject(WebSessionProjectImpl activeProject) {
|
||||
@NotNull
|
||||
@Override
|
||||
public WebSessionProjectImpl createProject(@NotNull String name, @Nullable String description) throws DBException {
|
||||
throw new DBException("Project creation is not supported in web session workspace");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProject(@NotNull DBPProject project) throws DBException {
|
||||
throw new DBException("Project removing is not supported in web session workspace");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renameProject(@NotNull DBPProject project, @NotNull String newName) throws DBException {
|
||||
throw new DBException("Project renaming is not supported in web session workspace");
|
||||
}
|
||||
|
||||
public void setActiveProject(@NotNull WebSessionProjectImpl activeProject) {
|
||||
this.activeProject = activeProject;
|
||||
}
|
||||
|
||||
void addProject(WebSessionProjectImpl project) {
|
||||
accessibleProjects.add(project);
|
||||
void addProject(@NotNull WebSessionProjectImpl project) {
|
||||
synchronized (accessibleProjects) {
|
||||
accessibleProjects.add(project);
|
||||
}
|
||||
}
|
||||
|
||||
void removeProject(WebSessionProjectImpl project) {
|
||||
accessibleProjects.remove(project);
|
||||
void removeProject(@NotNull WebSessionProjectImpl project) {
|
||||
synchronized (accessibleProjects) {
|
||||
accessibleProjects.remove(project);
|
||||
}
|
||||
}
|
||||
|
||||
void clearProjects() {
|
||||
if (!this.accessibleProjects.isEmpty()) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
project.dispose();
|
||||
synchronized (accessibleProjects) {
|
||||
if (!this.accessibleProjects.isEmpty()) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
project.dispose();
|
||||
}
|
||||
this.activeProject = null;
|
||||
this.accessibleProjects.clear();
|
||||
}
|
||||
this.activeProject = null;
|
||||
this.accessibleProjects.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRealmPermission(@NotNull String permission) {
|
||||
if (getWebSession() instanceof WebSession webSession) {
|
||||
return webSession.getSessionPermissions().contains(permission);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealmFeature(@NotNull String feature) {
|
||||
return false;
|
||||
return ServletAppUtils.getServletApplication().getAppConfiguration().isFeatureEnabled(feature);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+31
-19
@@ -53,9 +53,9 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
private final ServletApplication application;
|
||||
private final DBPWorkspace workspace;
|
||||
|
||||
private WebUser user;
|
||||
private Set<String> userPermissions;
|
||||
private SMCredentials smCredentials = null;
|
||||
private volatile WebUser user;
|
||||
private volatile Set<String> userPermissions;
|
||||
private volatile SMCredentials smCredentials = null;
|
||||
private String smSessionId;
|
||||
private String refreshToken;
|
||||
|
||||
@@ -63,11 +63,14 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
private SMAdminController adminSecurityController;
|
||||
private DBSSecretController secretController;
|
||||
private RMController rmController;
|
||||
private DBFileController fileController;
|
||||
private Set<String> accessibleProjectIds = new HashSet<>();
|
||||
private final DBFileController fileController;
|
||||
private final Set<String> accessibleProjectIds = new HashSet<>();
|
||||
private final WebSessionPreferenceStore preferenceStore;
|
||||
|
||||
public WebUserContext(ServletApplication application, DBPWorkspace workspace) throws DBException {
|
||||
public WebUserContext(
|
||||
@NotNull ServletApplication application,
|
||||
@NotNull DBPWorkspace workspace
|
||||
) throws DBException {
|
||||
this.application = application;
|
||||
this.workspace = workspace;
|
||||
this.securityController = application.createSecurityController(this);
|
||||
@@ -84,7 +87,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
* @return - true if context changed
|
||||
* @throws DBException - if user already authorized and new token come from another user
|
||||
*/
|
||||
public synchronized boolean refresh(SMAuthInfo smAuthInfo) throws DBException {
|
||||
public boolean refresh(@NotNull SMAuthInfo smAuthInfo) throws DBException {
|
||||
if (smAuthInfo.getAuthPermissions() == null && !isAuthorizedInSecurityManager()) {
|
||||
throw new DBCException("Required information about session permissions is missing");
|
||||
}
|
||||
@@ -172,53 +175,56 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public synchronized SMController getSecurityController() {
|
||||
public SMController getSecurityController() {
|
||||
return securityController;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public synchronized SMCredentials getActiveUserCredentials() {
|
||||
public SMCredentials getActiveUserCredentials() {
|
||||
return smCredentials;
|
||||
}
|
||||
|
||||
public synchronized boolean isAuthorizedInSecurityManager() {
|
||||
public boolean isAuthorizedInSecurityManager() {
|
||||
return smCredentials != null;
|
||||
}
|
||||
|
||||
public synchronized boolean isNonAnonymousUserAuthorizedInSM() {
|
||||
public boolean isNonAnonymousUserAuthorizedInSM() {
|
||||
return isAuthorizedInSecurityManager() && getUser() != null;
|
||||
}
|
||||
|
||||
public synchronized boolean isAnonymousUserAuthorizedInSM() {
|
||||
public boolean isAnonymousUserAuthorizedInSM() {
|
||||
return isAuthorizedInSecurityManager() && getUser() == null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public synchronized WebUser getUser() {
|
||||
public WebUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public synchronized String getUserId() {
|
||||
@Nullable
|
||||
public String getUserId() {
|
||||
return user == null ? null : user.getUserId();
|
||||
}
|
||||
|
||||
protected synchronized void setUser(@Nullable WebUser user) {
|
||||
protected void setUser(@Nullable WebUser user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public synchronized SMAdminController getAdminSecurityController() {
|
||||
@NotNull
|
||||
public SMAdminController getAdminSecurityController() {
|
||||
return adminSecurityController;
|
||||
}
|
||||
|
||||
public synchronized Set<String> getUserPermissions() {
|
||||
@Nullable
|
||||
public Set<String> getUserPermissions() {
|
||||
return userPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* reread the current user's permissions
|
||||
*/
|
||||
public synchronized void refreshPermissions() throws DBException {
|
||||
public void refreshPermissions() throws DBException {
|
||||
if (isAuthorizedInSecurityManager()) {
|
||||
log.debug("refresh permissions " + getUserId() + " " + getSmSessionId());
|
||||
setUserPermissions(securityController.getTokenPermissions().getPermissions());
|
||||
@@ -231,6 +237,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
this.userPermissions = permissions;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBSSecretController getSecretController() throws DBException {
|
||||
if (this.securityController == null) {
|
||||
this.secretController = application.getSecretController(this, workspace.getAuthContext());
|
||||
@@ -238,18 +245,22 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
return secretController;
|
||||
}
|
||||
|
||||
public synchronized String getSmSessionId() {
|
||||
@NotNull
|
||||
public String getSmSessionId() {
|
||||
return smSessionId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Set<String> getDefaultPermissions() {
|
||||
return application.isAnonymousAccessEnabled() ? null : Set.of();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public RMController getRmController() {
|
||||
return rmController;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBFileController getFileController() {
|
||||
return fileController;
|
||||
}
|
||||
@@ -263,6 +274,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebSessionPreferenceStore getPreferenceStore() {
|
||||
return preferenceStore;
|
||||
}
|
||||
|
||||
+2
-2
@@ -40,14 +40,14 @@ public class TaskProgressMonitor extends ProxyProgressMonitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginTask(String name, int totalWork) {
|
||||
public void beginTask(@NotNull String name, int totalWork) {
|
||||
super.beginTask(name, totalWork);
|
||||
asyncTask.setStatus(name);
|
||||
WebEventUtils.sendAsyncTaskEvent(webSession, asyncTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subTask(String name) {
|
||||
public void subTask(@NotNull String name) {
|
||||
super.subTask(name);
|
||||
asyncTask.setStatus(name);
|
||||
WebEventUtils.sendAsyncTaskEvent(webSession, asyncTask);
|
||||
|
||||
+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() {
|
||||
|
||||
+4
-2
@@ -38,8 +38,7 @@ public class WebFeatureDescriptor extends AbstractContextDescriptor implements D
|
||||
private final DBPImage icon;
|
||||
private final boolean enabledByDefault;
|
||||
|
||||
public WebFeatureDescriptor(IConfigurationElement config)
|
||||
{
|
||||
public WebFeatureDescriptor(@NotNull IConfigurationElement config) {
|
||||
super(config);
|
||||
this.id = config.getAttribute("id");
|
||||
this.label = config.getAttribute("label");
|
||||
@@ -77,4 +76,7 @@ public class WebFeatureDescriptor extends AbstractContextDescriptor implements D
|
||||
return enabledByDefault;
|
||||
}
|
||||
|
||||
public boolean isApplicable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-8
@@ -20,10 +20,12 @@ import io.cloudbeaver.DBWFeatureSet;
|
||||
import org.eclipse.core.runtime.IConfigurationElement;
|
||||
import org.eclipse.core.runtime.IExtensionRegistry;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WebFeatureRegistry {
|
||||
|
||||
@@ -33,7 +35,7 @@ public class WebFeatureRegistry {
|
||||
|
||||
private static WebFeatureRegistry instance = null;
|
||||
|
||||
public synchronized static WebFeatureRegistry getInstance() {
|
||||
public static synchronized WebFeatureRegistry getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new WebFeatureRegistry();
|
||||
instance.loadExtensions(Platform.getExtensionRegistry());
|
||||
@@ -41,23 +43,28 @@ public class WebFeatureRegistry {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private final List<DBWFeatureSet> webFeatures = new ArrayList<>();
|
||||
private final List<WebFeatureDescriptor> webFeatures = new ArrayList<>();
|
||||
|
||||
private WebFeatureRegistry() {
|
||||
}
|
||||
|
||||
private void loadExtensions(IExtensionRegistry registry) {
|
||||
protected void loadExtensions(@NotNull IExtensionRegistry registry) {
|
||||
IConfigurationElement[] extConfigs = registry.getConfigurationElementsFor(WebFeatureDescriptor.EXTENSION_ID);
|
||||
for (IConfigurationElement ext : extConfigs) {
|
||||
if (TAG_FEATURE.equals(ext.getName())) {
|
||||
this.webFeatures.add(
|
||||
new WebFeatureDescriptor(ext));
|
||||
this.webFeatures.add(createFeatureDescriptor(ext));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected WebFeatureDescriptor createFeatureDescriptor(@NotNull IConfigurationElement ext) {
|
||||
return new WebFeatureDescriptor(ext);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<DBWFeatureSet> getWebFeatures() {
|
||||
return webFeatures;
|
||||
return webFeatures.stream()
|
||||
.filter(WebFeatureDescriptor::isApplicable)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-3
@@ -41,7 +41,7 @@ public abstract class BaseServletPlatform extends BasePlatformImpl {
|
||||
if (tempFolder == null) {
|
||||
synchronized (this) {
|
||||
if (tempFolder == null) {
|
||||
initTempFolder(monitor);
|
||||
initTempFolder();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,9 +59,8 @@ public abstract class BaseServletPlatform extends BasePlatformImpl {
|
||||
@NotNull
|
||||
public abstract ServletApplication getApplication();
|
||||
|
||||
private void initTempFolder(@NotNull DBRProgressMonitor monitor) {
|
||||
private void initTempFolder() {
|
||||
// Make temp folder
|
||||
monitor.subTask("Create temp folder");
|
||||
String sysTempFolder = System.getProperty(StandardConstants.ENV_TMP_DIR);
|
||||
if (CommonUtils.isNotEmpty(sysTempFolder)) {
|
||||
tempFolder = Path.of(sysTempFolder).resolve(BASE_TEMP_DIR).resolve(DBWConstants.WORK_DATA_FOLDER_NAME);
|
||||
@@ -69,9 +68,11 @@ public abstract class BaseServletPlatform extends BasePlatformImpl {
|
||||
//we do not use workspace because it can be in external file system
|
||||
tempFolder = getApplication().getHomeDirectory().resolve(DBWConstants.WORK_DATA_FOLDER_NAME);
|
||||
}
|
||||
log.debug("Temp folder: " + tempFolder);
|
||||
}
|
||||
|
||||
public synchronized void dispose() {
|
||||
super.dispose();
|
||||
// Remove temp folder
|
||||
if (tempFolder != null) {
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-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.
|
||||
@@ -28,50 +28,34 @@ public class CBConstants {
|
||||
public static final String RUNTIME_PRODUCT_CONFIG_FILE_NAME = ".product.runtime.conf";
|
||||
public static final String AUTO_CONFIG_FILE_NAME = ".cloudbeaver.auto.conf";
|
||||
|
||||
public static final String PARAM_SERVER_CONFIGURATION = "server";
|
||||
public static final String PARAM_SERVER_PORT = "serverPort";
|
||||
public static final String PARAM_SERVER_HOST = "serverHost";
|
||||
public static final String PARAM_SERVER_NAME = "serverName";
|
||||
public static final String PARAM_FORCE_HTTPS = "forceHttps";
|
||||
public static final String PARAM_BIND_SESSION_TO_IP = "bindSessionToIp";
|
||||
public static final String PARAM_SUPPORTED_HOSTS = "supportedHosts";
|
||||
public static final String PARAM_SSL_CONFIGURATION_PATH = "sslConfigurationPath";
|
||||
public static final String PARAM_CONTENT_ROOT = "contentRoot";
|
||||
public static final String PARAM_SERVER_URL = "serverURL";
|
||||
public static final String PARAM_ROOT_URI = "rootURI";
|
||||
public static final String PARAM_SERVICES_URI = "serviceURI";
|
||||
public static final String PARAM_DRIVERS_LOCATION = "driversLocation";
|
||||
public static final String PARAM_WORKSPACE_LOCATION = "workspaceLocation";
|
||||
//legacy path to a product.conf file
|
||||
@Deprecated
|
||||
public static final String PARAM_PRODUCT_CONFIGURATION = "productConfiguration";
|
||||
public static final String PARAM_PRODUCT_SETTINGS = "productSettings";
|
||||
public static final String PARAM_EXTERNAL_PROPERTIES = "externalProperties";
|
||||
public static final String PARAM_STATIC_CONTENT = "staticContent";
|
||||
public static final String PARAM_RESOURCE_QUOTAS = "resourceQuotas";
|
||||
public static final String PARAM_RESOURCE_MANAGER_ENABLED = "resourceManagerEnabled";
|
||||
public static final String PARAM_SECRET_MANAGER_ENABLED = "secretManagerEnabled";
|
||||
public static final String PARAM_SHOW_READ_ONLY_CONN_INFO = "showReadOnlyConnectionInfo";
|
||||
public static final String PARAM_CONN_GRANT_ANON_ACCESS = "grantConnectionsAccessToAnonymousTeam";
|
||||
public static final String PARAM_AUTH_PROVIDERS = "authConfiguration";
|
||||
public static final String PARAM_DB_CONFIGURATION = "database";
|
||||
|
||||
public static final String PARAM_SESSION_EXPIRE_PERIOD = "expireSessionAfterPeriod";
|
||||
|
||||
public static final String PARAM_DEVEL_MODE = "develMode";
|
||||
public static final String PARAM_SECURITY_MANAGER = "enableSecurityManager";
|
||||
public static final String PARAM_SM_CONFIGURATION = "sm";
|
||||
public static final String PARAM_PASSWORD_POLICY_CONFIGURATION = "passwordPolicy";
|
||||
|
||||
public static final int DEFAULT_SERVER_PORT = 8080;
|
||||
//public static final String DEFAULT_SERVER_NAME = "CloudBeaver Web Server";
|
||||
|
||||
public static final String DEFAULT_CONTENT_ROOT = "/var/www/cloudbeaver";
|
||||
public static final String DEFAULT_ROOT_URI = "/";
|
||||
public static final String DEFAULT_SERVICES_URI = "/api/";
|
||||
|
||||
public static final String DEFAULT_DEPLOY_LOCATION = "/opt/cloudbeaver";
|
||||
public static final String DEFAULT_DRIVERS_LOCATION = DEFAULT_DEPLOY_LOCATION + "/drivers";
|
||||
public static final String DEFAULT_WORKSPACE_LOCATION = DEFAULT_DEPLOY_LOCATION + "/workspace";
|
||||
public static final String DEFAULT_PRODUCT_CONFIGURATION = "conf/product.conf";
|
||||
public static final String DEFAULT_DATASOURCE_PERMISSIONS_CONFIGURATION = "data-sources-permissions.json";
|
||||
public static final String DEFAULT_ADMIN_NAME = "cbadmin";
|
||||
@@ -84,14 +68,11 @@ public class CBConstants {
|
||||
public static final String VAR_HOST_DOCKER_INTERNAL = "host.docker.internal";
|
||||
|
||||
public static final String VAR_AUTO_CB_SERVER_NAME = "CB_SERVER_NAME";
|
||||
public static final String VAR_AUTO_CB_SERVER_URL = "CB_SERVER_URL";
|
||||
public static final String VAR_AUTO_CB_ADMIN_NAME = "CB_ADMIN_NAME";
|
||||
public static final String VAR_AUTO_CB_ADMIN_PASSWORD = "CB_ADMIN_PASSWORD";
|
||||
|
||||
public static final String CB_SESSION_COOKIE_NAME = "cb-session-id";
|
||||
|
||||
public static final String APPLICATION_JSON = "application/json";
|
||||
|
||||
public static final String ADMIN_AUTO_GRANT = "auto-grant";
|
||||
public static final String HOST_LOCALHOST = "localhost";
|
||||
public static final String HOST_127_0_0_1 = "127.0.0.1";
|
||||
@@ -99,4 +80,6 @@ public class CBConstants {
|
||||
public static final String BIND_SESSION_ENABLE = "enable";
|
||||
public static final String BIND_SESSION_DISABLE = "disable";
|
||||
public static final String DEFAULT_CLOUD_PROJECT_NAME = "GlobalConfiguration";
|
||||
|
||||
public static final String SECURED_VALUE = "******";
|
||||
}
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
+13
-4
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.sql;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.data.DBDAttributeConstraint;
|
||||
import org.jkiss.dbeaver.model.gis.DBGeometry;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
@@ -27,6 +28,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class WebSQLDataFilterConstraint {
|
||||
|
||||
private String attributeName;
|
||||
private Integer attributePosition;
|
||||
private Integer orderPosition;
|
||||
private Boolean orderAsc;
|
||||
@@ -38,8 +40,9 @@ public class WebSQLDataFilterConstraint {
|
||||
private WebSQLDataFilterConstraint() {
|
||||
}
|
||||
|
||||
public WebSQLDataFilterConstraint(Map<String, Object> map) {
|
||||
this.attributePosition = CommonUtils.toInt(map.get("attributePosition"));
|
||||
public WebSQLDataFilterConstraint(@NotNull Map<String, Object> map) {
|
||||
this.attributeName = CommonUtils.toString(map.get("attributeName"));
|
||||
this.attributePosition = CommonUtils.toInteger(map.get("attributePosition"), null);
|
||||
this.orderPosition = map.containsKey("orderPosition") ?
|
||||
CommonUtils.toInt(map.get("orderPosition")) + 1 : // Use position + 1 because 0 means no ordering (because of legacy compatibility)
|
||||
null;
|
||||
@@ -64,8 +67,10 @@ public class WebSQLDataFilterConstraint {
|
||||
|
||||
}
|
||||
|
||||
public static WebSQLDataFilterConstraint from(DBDAttributeConstraint constraint) {
|
||||
@NotNull
|
||||
public static WebSQLDataFilterConstraint from(@NotNull DBDAttributeConstraint constraint) {
|
||||
var webConstraint = new WebSQLDataFilterConstraint();
|
||||
webConstraint.attributeName = constraint.getAttributeName();
|
||||
webConstraint.orderPosition = constraint.getOrderPosition();
|
||||
webConstraint.orderAsc = !constraint.isOrderDescending();
|
||||
webConstraint.criteria = constraint.getCriteria();
|
||||
@@ -76,6 +81,10 @@ public class WebSQLDataFilterConstraint {
|
||||
return webConstraint;
|
||||
}
|
||||
|
||||
public String getAttributeName() {
|
||||
return attributeName;
|
||||
}
|
||||
|
||||
public Integer getAttributePosition() {
|
||||
return attributePosition;
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.sql;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
|
||||
public record WebSQLQueryParameterRecord(@NotNull String name, @Nullable String value) {
|
||||
}
|
||||
+12
@@ -21,6 +21,7 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.DBPDataSource;
|
||||
import org.jkiss.dbeaver.model.DBUtils;
|
||||
import org.jkiss.dbeaver.model.data.DBDAttributeBinding;
|
||||
import org.jkiss.dbeaver.model.data.DBDDataFilter;
|
||||
import org.jkiss.dbeaver.model.data.DBDRowIdentifier;
|
||||
import org.jkiss.dbeaver.model.exec.trace.DBCTrace;
|
||||
import org.jkiss.dbeaver.model.struct.*;
|
||||
@@ -42,6 +43,8 @@ public class WebSQLResultsInfo {
|
||||
// TODO: find a way to remove isSingleRow and use virtual keys for reading BLOB and string cell values.
|
||||
private boolean isSingleRow;
|
||||
private DBCTrace trace;
|
||||
@Nullable
|
||||
private DBDDataFilter dataFilter;
|
||||
private String queryText;
|
||||
|
||||
public WebSQLResultsInfo(@NotNull DBSDataContainer dataContainer, @NotNull String id) {
|
||||
@@ -151,4 +154,13 @@ public class WebSQLResultsInfo {
|
||||
public void setSingleRow(boolean singleRow) {
|
||||
isSingleRow = singleRow;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DBDDataFilter getDataFilter() {
|
||||
return dataFilter;
|
||||
}
|
||||
|
||||
public void setDataFilter(@Nullable DBDDataFilter dataFilter) {
|
||||
this.dataFilter = dataFilter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,20 @@ package io.cloudbeaver.test;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.utils.HttpConstants;
|
||||
|
||||
import java.net.CookieManager;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -56,10 +58,13 @@ public class WebGQLClient {
|
||||
@NotNull
|
||||
private final HttpClient httpClient;
|
||||
@NotNull
|
||||
private final CookieManager cookieManager;
|
||||
@NotNull
|
||||
private final String apiUrl;
|
||||
|
||||
public WebGQLClient(@NotNull HttpClient httpClient, @NotNull String apiUrl) {
|
||||
public WebGQLClient(@NotNull HttpClient httpClient, @NotNull CookieManager cookieManager, @NotNull String apiUrl) {
|
||||
this.httpClient = httpClient;
|
||||
this.cookieManager = cookieManager;
|
||||
this.apiUrl = apiUrl;
|
||||
}
|
||||
|
||||
@@ -70,9 +75,18 @@ public class WebGQLClient {
|
||||
* @param variables GraphQL query variables
|
||||
* @return GraphQL response
|
||||
*/
|
||||
@NotNull
|
||||
@Nullable
|
||||
public <T> T sendQuery(@NotNull String query, @Nullable Map<String, Object> variables) throws Exception {
|
||||
return sendQueryWithHeaders(query, variables, List.of());
|
||||
return sendQueryWithHeaders(query, variables, Map.of());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, Object> sendQueryAndGetHeaders(
|
||||
@NotNull String query,
|
||||
@Nullable Map<String, Object> variables,
|
||||
@NotNull Map<String, String> requestHeaders
|
||||
) throws Exception {
|
||||
return executeGQLRequest(query, variables, requestHeaders);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,15 +101,36 @@ public class WebGQLClient {
|
||||
public <T> T sendQueryWithHeaders(
|
||||
@NotNull String query,
|
||||
@Nullable Map<String, Object> variables,
|
||||
@NotNull List<String> headers
|
||||
@NotNull Map<String, String> headers
|
||||
) throws Exception {
|
||||
Map<String, Object> parsed = executeGQLRequest(query, variables, headers);
|
||||
// graphql response will be in "data" key
|
||||
return (T) parsed.get("data");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Map<String, Object> executeGQLRequest(
|
||||
@NotNull String query,
|
||||
@Nullable Map<String, Object> variables,
|
||||
@NotNull Map<String, String> requestHeaders
|
||||
) throws Exception {
|
||||
return executeGQLRequest(query, variables, requestHeaders, "result");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, Object> executeGQLRequest(
|
||||
@NotNull String query,
|
||||
@Nullable Map<String, Object> variables,
|
||||
@NotNull Map<String, String> requestHeaders,
|
||||
@NotNull String resultName
|
||||
) throws Exception {
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(apiUrl))
|
||||
.POST(HttpRequest.BodyPublishers.ofString(makeGQLRequest(query, variables)))
|
||||
.setHeader("TE-Client-Version", GeneralUtils.getMajorVersion())
|
||||
.header("Content-Type", "application/json");
|
||||
if (!headers.isEmpty()) {
|
||||
requestBuilder.headers(headers.toArray(String[]::new));
|
||||
.header(HttpConstants.HEADER_CONTENT_TYPE, HttpConstants.CONTENT_TYPE_JSON);
|
||||
if (!requestHeaders.isEmpty()) {
|
||||
requestHeaders.forEach(requestBuilder::header);
|
||||
}
|
||||
HttpRequest request = requestBuilder.build();
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
@@ -107,8 +142,10 @@ public class WebGQLClient {
|
||||
String message = JSONUtils.getString(JSONUtils.getObjectList(body, "errors").get(0), "message");
|
||||
throw new DBException(message);
|
||||
}
|
||||
// graphql response will be in "data" key
|
||||
return (T) JSONUtils.getObject(body, "data").get("result");
|
||||
Map<String, Object> parsed = new LinkedHashMap<>();
|
||||
parsed.put("data", JSONUtils.getObject(body, "data").get(resultName));
|
||||
parsed.put("headers", response.headers().map());
|
||||
return parsed;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -121,4 +158,13 @@ public class WebGQLClient {
|
||||
|
||||
return gson.toJson(request, Map.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getSessionIdCookie() {
|
||||
return cookieManager.getCookieStore().getCookies().stream()
|
||||
.filter(cookie -> cookie.getName().equals(CBConstants.CB_SESSION_COOKIE_NAME))
|
||||
.findFirst()
|
||||
.get()
|
||||
.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-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.
|
||||
@@ -47,6 +47,8 @@ public class ServletAppUtils {
|
||||
private static final String HEADER_REFERER = "Referer";
|
||||
|
||||
private static final String HEADER_FORWARDED_SCHEME = "X-Forwarded-Scheme";
|
||||
private static final String HEADER_FORWARDED_PROTO = "X-Forwarded-Proto";
|
||||
private static final String HEADER_FORWARDED_PORT = "X-Forwarded-Port";
|
||||
private static final String HEADER_FORWARDED_HOST = "X-Forwarded-Host";
|
||||
private static final Set<Integer> DEFAULT_PORTS = Set.of(
|
||||
80,
|
||||
@@ -191,8 +193,12 @@ public class ServletAppUtils {
|
||||
@NotNull String origin
|
||||
) throws DBException {
|
||||
String finalOrigin = webAuthApplication.modifyOrigin(origin);
|
||||
StringBuilder authUriBuilder = new StringBuilder(removeSideSlashes(finalOrigin));
|
||||
String serviceUriSegment = removeSideSlashes(webAuthApplication.getAuthServiceUriSegment());
|
||||
String rootUri = removeSideSlashes(getServletApplication().getRootURI());
|
||||
if (finalOrigin.endsWith("/" + rootUri) && serviceUriSegment.startsWith(rootUri + "/")) {
|
||||
finalOrigin = finalOrigin.substring(0, finalOrigin.length() - rootUri.length());
|
||||
}
|
||||
StringBuilder authUriBuilder = new StringBuilder(removeSideSlashes(finalOrigin));
|
||||
if (CommonUtils.isNotEmpty(serviceUriSegment)) {
|
||||
authUriBuilder.append("/").append(serviceUriSegment);
|
||||
}
|
||||
@@ -294,19 +300,30 @@ public class ServletAppUtils {
|
||||
@NotNull
|
||||
public static String getOriginFromRequest(@NotNull HttpServletRequest request) {
|
||||
String origin = request.getHeader(HEADER_ORIGIN);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Origin header: " + origin);
|
||||
}
|
||||
if (CommonUtils.isEmpty(origin)) {
|
||||
origin = request.getHeader(HEADER_X_ORIGIN);
|
||||
}
|
||||
if (CommonUtils.isEmpty(origin)) {
|
||||
origin = request.getHeader(HEADER_REFERER);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("X-Origin header: " + origin);
|
||||
}
|
||||
}
|
||||
String forwardedScheme = request.getHeader(HEADER_FORWARDED_SCHEME);
|
||||
if (CommonUtils.isEmpty(forwardedScheme)) {
|
||||
forwardedScheme = request.getHeader(HEADER_FORWARDED_PROTO);
|
||||
}
|
||||
String forwardedHost = request.getHeader(HEADER_FORWARDED_HOST);
|
||||
if (CommonUtils.isNotEmpty(forwardedScheme) && CommonUtils.isNotEmpty(forwardedHost)) {
|
||||
origin = forwardedScheme + "://" + forwardedHost;
|
||||
}
|
||||
if (CommonUtils.isEmpty(origin)) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("forwarded origin: " + origin);
|
||||
}
|
||||
} else if (CommonUtils.isEmpty(origin)) {
|
||||
URI requestUrl = URI.create(request.getRequestURL().toString());
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Request URL: " + requestUrl);
|
||||
}
|
||||
origin = getRootUrlFromUri(requestUrl) + "/";
|
||||
}
|
||||
origin = removeSideSlashes(origin);
|
||||
@@ -318,13 +335,42 @@ public class ServletAppUtils {
|
||||
|
||||
origin = removeSideSlashes(origin);
|
||||
URI uri = URI.create(origin);
|
||||
if (DEFAULT_PORTS.contains(uri.getPort())) {
|
||||
int port = uri.getPort();
|
||||
if (CommonUtils.isNotEmpty(request.getHeader(HEADER_FORWARDED_PORT))) {
|
||||
try {
|
||||
port = Integer.parseInt(request.getHeader(HEADER_FORWARDED_PORT));
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("Failed to parse port from header: " + request.getHeader(HEADER_FORWARDED_PORT), e);
|
||||
}
|
||||
}
|
||||
|
||||
String finalScheme = uri.getScheme();
|
||||
String finalHost = uri.getHost();
|
||||
int finalPort = port;
|
||||
boolean changed = false;
|
||||
if (CommonUtils.isNotEmpty(forwardedScheme) && !forwardedScheme.equals(finalScheme)) {
|
||||
finalScheme = forwardedScheme;
|
||||
changed = true;
|
||||
}
|
||||
if (CommonUtils.isNotEmpty(forwardedHost) && !forwardedHost.equals(finalHost)) {
|
||||
finalHost = forwardedHost;
|
||||
changed = true;
|
||||
}
|
||||
if (DEFAULT_PORTS.contains(port)) {
|
||||
finalPort = -1;
|
||||
changed = true;
|
||||
}
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Origin URI: " + origin);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
try {
|
||||
origin = new URI(
|
||||
uri.getScheme(),
|
||||
finalScheme,
|
||||
uri.getUserInfo(),
|
||||
uri.getHost(),
|
||||
-1,
|
||||
finalHost,
|
||||
finalPort,
|
||||
uri.getPath(),
|
||||
uri.getQuery(),
|
||||
uri.getFragment()
|
||||
|
||||
+30
-28
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,10 +16,6 @@
|
||||
*/
|
||||
package io.cloudbeaver.utils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.InstanceCreator;
|
||||
import com.google.gson.Strictness;
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebSessionProjectImpl;
|
||||
@@ -34,7 +30,6 @@ import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBConstants;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.access.DBAAuthCredentials;
|
||||
import org.jkiss.dbeaver.model.access.DBAAuthCredentialsWithComplexProperties;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
@@ -43,10 +38,12 @@ import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNativeCredentials;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration;
|
||||
import org.jkiss.dbeaver.model.net.ssh.SSHConstants;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceDisconnectEvent;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.dbeaver.utils.PropertySerializationUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.*;
|
||||
@@ -165,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(),
|
||||
webSession.getSessionId(),
|
||||
webSession.getUserId()
|
||||
)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
} catch (DBException e) {
|
||||
log.error("Error closing connection", e);
|
||||
@@ -211,31 +214,30 @@ public class WebDataSourceUtils {
|
||||
return webSession.getAccessibleProjectById(projectId).getWebConnectionInfo(connectionId);
|
||||
}
|
||||
|
||||
|
||||
public static void updateCredentialsFromProperties(@NotNull DBAAuthCredentials credentials, @NotNull Map<String, ?> properties) {
|
||||
InstanceCreator<DBAAuthCredentials> credTypeAdapter = type -> credentials;
|
||||
Gson credGson = new GsonBuilder()
|
||||
.setStrictness(Strictness.LENIENT)
|
||||
.registerTypeAdapter(credentials.getClass(), credTypeAdapter)
|
||||
.create();
|
||||
|
||||
if (credentials instanceof DBAAuthCredentialsWithComplexProperties complexProperties) {
|
||||
complexProperties.updateCredentialsFromComplexProperties(properties);
|
||||
public static void updateCredentialsFromProperties(
|
||||
@NotNull DBRProgressMonitor progressMonitor,
|
||||
@NotNull DBAAuthCredentials credentials,
|
||||
@Nullable Map<String, ?> properties
|
||||
) {
|
||||
if (properties == null) {
|
||||
return;
|
||||
}
|
||||
credGson.fromJson(credGson.toJsonTree(properties), credentials.getClass());
|
||||
PropertySerializationUtils.updateCredentialsFromProperties(progressMonitor, credentials, properties);
|
||||
}
|
||||
|
||||
public static void saveAuthProperties(
|
||||
@NotNull DBRProgressMonitor progressMonitor,
|
||||
@NotNull DBPDataSourceContainer dataSourceContainer,
|
||||
@NotNull DBPConnectionConfiguration configuration,
|
||||
@Nullable Map<String, Object> authProperties,
|
||||
boolean saveCredentials,
|
||||
boolean sharedCredentials
|
||||
) {
|
||||
saveAuthProperties(dataSourceContainer, configuration, authProperties, saveCredentials, sharedCredentials, false);
|
||||
saveAuthProperties(progressMonitor, dataSourceContainer, configuration, authProperties, saveCredentials, sharedCredentials, false);
|
||||
}
|
||||
|
||||
public static void saveAuthProperties(
|
||||
@NotNull DBRProgressMonitor progressMonitor,
|
||||
@NotNull DBPDataSourceContainer dataSourceContainer,
|
||||
@NotNull DBPConnectionConfiguration configuration,
|
||||
@Nullable Map<String, Object> authProperties,
|
||||
@@ -271,7 +273,7 @@ public class WebDataSourceUtils {
|
||||
configuration.setAuthProperties(currentAuthProps);
|
||||
}
|
||||
if (!authProperties.isEmpty()) {
|
||||
updateCredentialsFromProperties(credentials, authProperties);
|
||||
updateCredentialsFromProperties(progressMonitor, credentials, authProperties);
|
||||
}
|
||||
|
||||
configuration.getAuthModel().saveCredentials(dataSourceContainer, configuration, credentials);
|
||||
|
||||
@@ -18,6 +18,7 @@ package io.cloudbeaver.utils;
|
||||
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
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;
|
||||
@@ -30,7 +31,6 @@ import org.jkiss.dbeaver.model.websocket.event.datasource.WSDatasourceFolderEven
|
||||
import org.jkiss.dbeaver.model.websocket.event.permissions.WSSubjectPermissionEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceProperty;
|
||||
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceUpdatedEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.session.WSSessionTaskInfoEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
|
||||
package io.cloudbeaver.utils;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
@@ -39,4 +42,30 @@ public class WebTestUtils {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setPrivateField(
|
||||
@NotNull Object target,
|
||||
@NotNull String fieldName,
|
||||
@Nullable Object value
|
||||
) throws Exception {
|
||||
Class<?> cls = target.getClass();
|
||||
Field f = null;
|
||||
while (cls != null) {
|
||||
Field[] fields = cls.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if (field.getName().equals(fieldName)) {
|
||||
f = field;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cls = cls.getSuperclass();
|
||||
}
|
||||
if (f == null) {
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
boolean isAccessible = f.canAccess(target);
|
||||
f.setAccessible(true);
|
||||
f.set(target, value);
|
||||
f.setAccessible(isAccessible);
|
||||
}
|
||||
}
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
public class WSSessionCancelActionClientEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_cancel_action";
|
||||
|
||||
@NotNull
|
||||
private final String actionId;
|
||||
|
||||
public WSSessionCancelActionClientEvent(
|
||||
@NotNull String actionId
|
||||
) {
|
||||
super(ID, WSConstants.TOPIC_SESSION_ACTION);
|
||||
this.actionId = actionId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getActionId() {
|
||||
return actionId;
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
public class WSSessionPingClientEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_session_ping";
|
||||
|
||||
public WSSessionPingClientEvent(@Nullable String topicId) {
|
||||
super(ID, topicId);
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
public class WSSessionTaskConfirmationEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_session_task_confirmation";
|
||||
|
||||
private final String taskId;
|
||||
private final boolean confirmed;
|
||||
private final boolean skipConfirmations;
|
||||
|
||||
public WSSessionTaskConfirmationEvent(
|
||||
@NotNull String taskId,
|
||||
boolean confirmed,
|
||||
boolean skipConfirmations
|
||||
) {
|
||||
super(ID, WSConstants.TOPIC_SESSION_TASK);
|
||||
this.taskId = taskId;
|
||||
this.confirmed = confirmed;
|
||||
this.skipConfirmations = skipConfirmations;
|
||||
}
|
||||
|
||||
public String getTaskId() {
|
||||
return taskId;
|
||||
}
|
||||
|
||||
public boolean isConfirmed() {
|
||||
return confirmed;
|
||||
}
|
||||
|
||||
public boolean isSkipConfirmations() {
|
||||
return skipConfirmations;
|
||||
}
|
||||
}
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class WSSessionTaskWithParametersConfirmationEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_session_task_with_parameters_confirmation";
|
||||
@NotNull
|
||||
private final String taskId;
|
||||
@NotNull
|
||||
private final Map<String, Object> parameters;
|
||||
|
||||
protected WSSessionTaskWithParametersConfirmationEvent(@NotNull String taskId, @NotNull Map<String, Object> parameters) {
|
||||
super(ID, WSConstants.TOPIC_SESSION_TASK);
|
||||
this.taskId = taskId;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getTaskId() {
|
||||
return taskId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, Object> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
/**
|
||||
* Subscribe on event topic
|
||||
*/
|
||||
public class WSSubscribeOnTopicClientEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_topic_subscribe";
|
||||
|
||||
public WSSubscribeOnTopicClientEvent(String topicId) {
|
||||
super(ID, topicId);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
/**
|
||||
* Unsubscribe from event topic
|
||||
*/
|
||||
public class WSUnsubscribeFromTopicClientEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_topic_unsubscribe";
|
||||
|
||||
public WSUnsubscribeFromTopicClientEvent(String topicId) {
|
||||
super(ID, topicId);
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Subscribe on event topic
|
||||
*/
|
||||
public class WSUpdateActiveProjectsClientEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_projects_active";
|
||||
|
||||
@NotNull
|
||||
private final Set<String> projectIds;
|
||||
|
||||
public WSUpdateActiveProjectsClientEvent(@NotNull Set<String> projectIds) {
|
||||
super(ID, WSConstants.TOPIC_PROJECTS);
|
||||
this.projectIds = projectIds;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<String> getProjectIds() {
|
||||
return projectIds;
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.task;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.session.WSAbstractSessionEvent;
|
||||
|
||||
public abstract class WSAbstractSessionTaskEvent extends WSAbstractSessionEvent {
|
||||
@NotNull
|
||||
private final String taskId;
|
||||
@NotNull
|
||||
private final String title;
|
||||
@NotNull
|
||||
private final String message;
|
||||
|
||||
public WSAbstractSessionTaskEvent(
|
||||
@NotNull String eventId,
|
||||
@NotNull String taskId,
|
||||
@NotNull String title,
|
||||
@NotNull String message
|
||||
) {
|
||||
super(eventId, WSConstants.TOPIC_SESSION_TASK);
|
||||
this.taskId = taskId;
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getTaskId() {
|
||||
return taskId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.task;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
public class WSSessionTaskConfirmationRequestEvent extends WSAbstractSessionTaskEvent {
|
||||
private static final String ID = "cb_session_task_confirmation_request";
|
||||
|
||||
public WSSessionTaskConfirmationRequestEvent(
|
||||
@NotNull String taskId,
|
||||
@NotNull String title,
|
||||
@NotNull String message
|
||||
) {
|
||||
super(ID, taskId, title, message);
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.task;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.session.WSAbstractSessionEvent;
|
||||
|
||||
/**
|
||||
* Async task info event.
|
||||
*/
|
||||
public class WSSessionTaskInfoEvent extends WSAbstractSessionEvent {
|
||||
@NotNull
|
||||
private final String taskId;
|
||||
@Nullable
|
||||
private final String statusName;
|
||||
private final boolean running;
|
||||
|
||||
public WSSessionTaskInfoEvent(
|
||||
@NotNull String taskId,
|
||||
@Nullable String statusName,
|
||||
boolean running
|
||||
) {
|
||||
super("cb_session_task_info_updated", WSConstants.TOPIC_SESSION_TASK);
|
||||
this.taskId = taskId;
|
||||
this.statusName = statusName;
|
||||
this.running = running;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getTaskId() {
|
||||
return taskId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getStatusName() {
|
||||
return statusName;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.task;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
public class WSSessionTaskQueryConfirmationRequestEvent extends WSAbstractSessionTaskEvent {
|
||||
private static final String ID = "cb_session_task_query_confirmation_request";
|
||||
|
||||
@NotNull
|
||||
private final String query;
|
||||
|
||||
public WSSessionTaskQueryConfirmationRequestEvent(
|
||||
@NotNull String taskId,
|
||||
@NotNull String title,
|
||||
@NotNull String message,
|
||||
@NotNull String query
|
||||
) {
|
||||
super(ID, taskId, title, message);
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.task;
|
||||
|
||||
import io.cloudbeaver.service.sql.WebSQLQueryParameterRecord;
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WSSessionTaskQueryParamsConfirmationEvent extends WSAbstractSessionTaskEvent {
|
||||
private static final String ID = "cb_session_task_query_params_confirmation_request";
|
||||
@NotNull
|
||||
private final String query;
|
||||
@NotNull
|
||||
private final List<WebSQLQueryParameterRecord> parameters;
|
||||
|
||||
public WSSessionTaskQueryParamsConfirmationEvent(
|
||||
@NotNull String taskId,
|
||||
@NotNull String title,
|
||||
@NotNull String message,
|
||||
@NotNull String query,
|
||||
@NotNull List<WebSQLQueryParameterRecord> parameters
|
||||
) {
|
||||
super(ID, taskId, title, message);
|
||||
this.query = query;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<WebSQLQueryParameterRecord> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Community Product
|
||||
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
|
||||
Bundle-Version: 25.2.5.qualifier
|
||||
Bundle-Release-Date: 20251117
|
||||
Bundle-Version: 26.1.0.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
</extension>
|
||||
|
||||
<extension point="org.eclipse.core.runtime.products" id="product">
|
||||
<product application="io.cloudbeaver.product.ce.application" description="CloudBeaver CE Web UI Application"
|
||||
name="CloudBeaver CE Server">
|
||||
<property name="appName" value="CloudBeaver CE Server"/>
|
||||
<product application="io.cloudbeaver.product.ce.application" name="CloudBeaver Community">
|
||||
<property name="appName" value="CloudBeaver Community"/>
|
||||
<property name="versionUpdateURL" value="https://dbeaver.com/product/cloudbeaver-ce-version.json"/>
|
||||
</product>
|
||||
</extension>
|
||||
@@ -21,8 +20,7 @@
|
||||
<application
|
||||
id="cloudbeaver-ce"
|
||||
family="CB"
|
||||
name="CloudBeaver CE Server"
|
||||
description="CloudBeaver CE Web UI"
|
||||
name="CloudBeaver Community"
|
||||
server="true"/>
|
||||
</extension>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce</artifactId>
|
||||
<version>25.2.5-SNAPSHOT</version>
|
||||
<version>26.1.0-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.134.qualifier
|
||||
Bundle-Release-Date: 20251117
|
||||
Bundle-Version: 1.0.147.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
<resource name="drivers/jaybird"/>
|
||||
<resource name="drivers/h2"/>
|
||||
<resource name="drivers/h2_v2"/>
|
||||
<resource name="drivers/h2_v3"/>
|
||||
<resource name="drivers/sqlite/xerial"/>
|
||||
<resource name="drivers/libsql"/>
|
||||
<resource name="drivers/mssql/new"/>
|
||||
<resource name="drivers/trino"/>
|
||||
<resource name="drivers/kyuubi"/>
|
||||
@@ -35,7 +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 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"/>
|
||||
@@ -55,11 +59,12 @@
|
||||
<driver id="clickhouse:com_clickhouse"/>
|
||||
<driver id="h2:h2_embedded"/>
|
||||
<driver id="h2:h2_embedded_v2"/>
|
||||
<driver id="h2:h2_embedded_v3"/>
|
||||
<driver id="sqlite:sqlite_jdbc"/>
|
||||
<driver id="sqlite:libsql_jdbc"/>
|
||||
<driver id="sqlserver:microsoft"/>
|
||||
<driver id="generic:trino_jdbc"/>
|
||||
<driver id="generic:duckdb_jdbc"/>
|
||||
<driver id="duckdb:duckdb_jdbc"/>
|
||||
<driver id="generic:kyuubi_hive"/>
|
||||
<driver id="databend:databend"/>
|
||||
</extension>
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
|
||||
<version>1.0.134-SNAPSHOT</version>
|
||||
<version>1.0.147-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver CE Server
|
||||
Bundle-Name: CloudBeaver Community
|
||||
Bundle-SymbolicName: io.cloudbeaver.server.ce;singleton:=true
|
||||
Bundle-Version: 25.2.5.qualifier
|
||||
Bundle-Release-Date: 20251117
|
||||
Bundle-Version: 26.1.0.qualifier
|
||||
Bundle-Release-Date: 20260601
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
|
||||
Require-Bundle: io.cloudbeaver.server;visibility:=reexport,
|
||||
io.cloudbeaver.model.cli;visibility:=reexport,
|
||||
com.google.guava
|
||||
Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.model,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server.ce</artifactId>
|
||||
<version>25.2.5-SNAPSHOT</version>
|
||||
<version>26.1.0-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user