Compare commits

...
Author SHA1 Message Date
naumov 0d22898da1 dbeaver/pro#7548 remove limit for the project nodes 2026-06-21 22:37:03 +02:00
Ainur 8eb012670d dbeaver/pro#9640 fix loading driver props for old api (#4404) 2026-06-18 14:09:28 +02:00
Ruslan MusaevandAinur 413d229028 dbeaver/pro#9568 move tests to ce (#4402)
* dbeaver/pro#9568 move tests to ce

* dbeaver/pro#9568 move tests to ce

* dbeaver/pro#9568 fix tests

* dbeaver/pro#9568 fix tests

* dbeaver/pro#9568 fix tests

---------

Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
2026-06-18 11:30:08 +02:00
serge-rider b0fbd41b85 dbeaver/pro#9544 Refactor SM auth model 2026-06-18 06:47:14 +02:00
660c7d9e1f dbeaver/pro#9328 Network profiles model (#4375)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-06-17 17:21:34 +02:00
dbeaver-devops 0f79ef21e7 dbeaver/pro#9630 26.1.2 version bump 2026-06-16 19:25:24 +02:00
AinurandEvgenia d9b5a9a20d dbeaver/pro#9088 api for collecting driver properties with auth (#4396)
* dbeaver/pro#9088 api for collecting driver properties with auth

* dbeaver/pro#9088 fix loading props in desktop and filter secured props

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-06-16 19:04:37 +02:00
Sychev Andreyandmr-anton-t b200148765 dbeaver/pro#9565 fix: lock @codemirror/search dependency to version 6.6.0 (#4401)
in 6.7.0 they have broken the replaceNext functionality and until they fix it we lock on the working version

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-16 18:55:26 +02:00
sergeyteleshevandmr-anton-t 18ecc829d3 dbeaver/pro#9618 removes form-data CVE (#4400)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-16 18:52:21 +02:00
AinurandEvgenia 58f51ee5f6 dbeaver/pro#9222 validate data transfer by using product settings (#4394)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-06-16 17:24:36 +02:00
Sychev Andreyandmr-anton-t 0f7c8fe69f dbeaver/pro#4867 refactor: use aria-labelledby instead of aria-label with the same text (#4366)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-16 11:37:19 +02:00
sergeyteleshev 776a91767d dbeaver/pro#9605 removes esbuild CVE (#4398) 2026-06-16 11:24:15 +02:00
Serge Rider 9f4f543eb3 Update documentation link from Community to GitHub WIKI 2026-06-14 08:08:30 +02:00
Serge Rider e4e009e1ec Update README with documentation links
Added links to official documentation and community WIKI.
2026-06-14 08:07:16 +02:00
serge-rider 895d27eb84 Workflow update 2026-06-13 23:27:08 +02:00
serge-rider a0cbaa89f6 Workflow update 2026-06-13 23:23:12 +02:00
serge-rider 32d1ef9a70 dbeaver/pro#9573 Groom test deps. 2026-06-13 23:17:35 +02:00
8362946990 dbeaver/pro#7128 Remove deprecated API (#3820)
* dbeaver/pro#7128 Remove deprecated API

* dbeaver/pro#7128 Fix nodes

* dbeaver/pro#7128 Return DS id in objectId

* dbeaver/pro#7128 disable test

* dbeaver/pro#7128 fixes connectionId and node Uri

* dbeaver/pro#7128 use new node path patterns

* dbeaver/pro#7128 FS in root nodes

* dbeaver/pro#7128 FS in root nodes

* dbeaver/pro#7128 FS and clouds in root nodes

* dbeaver/pro#7128 fix getting connection part

* dbeaver/pro#7128 use node path by default

* dbeaver/pro#7128 remove unused import

* dbeaver/pro#7128 fix tests

* dbeaver/pro#7128 Fix web cloud explorer

* dbeaver/pro#7128 Path calculation fix. Delete legacy and dead code.

* dbeaver/pro#7128 support folders new path

* dbeaver/pro#7128 add rm prefix variable

* dbeaver/pro#7128 adjust tests

* dbeaver/pro#7128 Remove deprecated node id property

* dbeaver/pro#7128 use uri in db objects

* dbeaver/pro#7128 do not restore tab if wrong format is used

* makes isDatabaseObject less heuristic

* Revert "makes isDatabaseObject less heuristic"

This reverts commit f85acd91f9.

* local fix for folders open action

* disallow to open folders in object viewer

* uses helper to identify node for advanced view correct behavior

* dbeaver/pro#7128 fixes the test which falls from time to time

* dbeaver/pro#7128 fixes the test which falls from time to time

* dbeaver/pro#7128 fixes the test which falls from time to time 2

* dbeaver/pro#7128 fixes the test which falls from time to time 2

* dbeaver/pro#7128 isolates unit tests

* Revert "dbeaver/pro#7128 isolates unit tests"

This reverts commit 5bf6adbdd1.

* dbeaver/pro#7128 tries useTranslateMock to fix the issue

* removes mocks

* skips the tests

---------

Co-authored-by: Ruslan <pupkinvasya7000@gmail.com>
Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com>
Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-06-12 12:30:53 +02:00
AinurandEvgenia d3d71b79f0 dbeaver/pro#9120 cloudbeaver conf generator (#4388)
* dbeaver/pro#9120 cloudbeaver conf generator

* dbeaver/pro#9120 add tests and fix configs

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-06-12 11:34:39 +02:00
Ainurandmr-anton-t c344210b8b dbeaver/pro#7923 fix logging and use sm session id for events (#4374)
* dbeaver/pro#7923 fix logging and use sm session id for events

* dbeaver/pro#7923 nullability and move function

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-12 11:33:13 +02:00
f6aea971ef dbeaver/pro#9124 feat: replace Checkbox and Radio components with CheckboxIndicator and RadioIndicator for improved UI consistency (#4383)
* dbeaver/pro#9124 feat: introduce visual-only checkbox and radio indicators for improved nested interactive elements

* dbeaver/pro#9124 feat: add Checkbox and Radio components stories

* dbeaver/pro#9124 feat: export ControlSize type from controls module

* dbeaver/pro#9124 feat: add CheckboxIndicator and RadioIndicator components

* dbeaver/pro#9124 feat: replace Checkbox and Radio components with CheckboxIndicator and RadioIndicator for improved UI consistency

* dbeaver/pro#9124 refactor: rename CheckboxIndicatorProps to ICheckboxIndicatorProps for consistency

* dbeaver/pro#9124 refactor: rename RadioRootOwnProps to Props for consistency

* dbeaver/pro#9124 refactor: rename theme prop to variant in CheckboxIndicator for consistency

* dbeaver/pro#9124 refactor: rename RadioIndicatorProps to Props for consistency

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-06-12 11:09:22 +02:00
Ruslan Musaevandmr-anton-t 3d9c4391d1 dbeaver/pro#9119 fix code vulnerability (#4380)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-12 11:05:23 +02:00
sergeyteleshevandmr-anton-t 19284d33e7 dbeaver/pro#9550 removes critical CVE for shell-quote dependency (#4391)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-12 10:41:40 +02:00
dc1e865545 dbeaver/pro#9436 feat: add isNumber and replaceMiddle utility functions (#4377)
* dbeaver/pro#9436 feat: add isNumber and replaceMiddle utility functions

* dbeaver/pro#9436 refactor: move uuid helper into @dbeaver/js-helpers

Move uuid() from @cloudbeaver/core-utils into the portable @dbeaver/js-helpers so chart widget code can depend on it without pulling in CloudBeaver. core-utils re-exports uuid/V4Options/V1Options from js-helpers, so existing importers are unaffected; the uuid npm dependency moves to js-helpers.

* dbeaver/pro#9436 style: update copyright year in uuid_ file

* dbeaver/pro#9436 refactor: reexport isNumber and replaceMiddle functions from @dbeaver/js-helpers

* dbeaver/pro#9436 refactor: rename uuid file to uuid_ and update exports

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-06-11 14:20:45 +02:00
Sychev Andrey 4d62ddb5fc dbeaver/pro#9572 ci: add associations and references fields to SQL execute task results (#4393) 2026-06-11 14:08:40 +02:00
Ruslan MusaevandEvgenia 14df3b3120 dbeaver/pro#8981 add api for reference panel in cloudbeaver (#4313)
* dbeaver/pro#8981 add api for reference panel in cloudbeaver

* dbeaver/pro#8981 fixes

* dbeaver/pro#8981 api ref panel

* dbeaver/pro#8981 api ref panel

* dbeaver/pro#8981 api ref panel

* dbeaver/pro#8981 add api ref to table level

* dbeaver/pro#8981 fix tests

* dbeaver/pro#8981 fix review

* dbeaver/pro#8981 lazy loading fix

* dbeaver/pro#8981 separate association and refs

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-06-11 12:15:12 +02:00
Ruslan Musaev 30f5bd3bb3 dbeaver/pro#9482 add last auth date for users (#4389) 2026-06-11 12:14:31 +02:00
04a4c19331 dbeaver/pro#8780 migrate switch component to ui-kit (#4373)
* dbeaver/pro#8780 moves switch to ui-kit component + adds story

* redesigns switch

* fixes dense example

* fixes animations

* removes outline for whole area of switch

* adds tied to the input label

* adds innerChecked

* adds composition to the Switch component

* refactors switch API to make it more composable

* adds specifity to win css race with font weight for label

* fixes dense font weight

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-11 10:40:51 +02:00
Anastasiya 5bc6ea3731 dbeaver/dbeaver-devops#2699 Revise security policy (#4392)
* dbeaver/dbeaver-devops#2699 Revise security policy

Updated the contact email for reporting security vulnerabilities.

* dbeaver/dbeaver-devops#2699 Update email format in SECURITY.md
2026-06-10 12:20:51 +03:00
Alexander Skoblikovandkseniaguzeeva d93d73bb44 dbeaver/pro#9508 use proto header without host (#4379)
* dbeaver/pro#9508 use proto header without host

* dbeaver/pro#9508 review fixes

---------

Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-06-10 09:16:58 +02:00
6cdc6b2ee0 [CB] Render data viewer context menu on the root level (#4363)
* dbeaver/pro#9308 moves context menu from the cell level to table level

* cleanup

* adds DataGridMenuContextProvider

* uses context menu position from the hook useContextMenuPosition

* closes menu if cell is not visible

* adds useViewLeave hook to close menu if it is out of the view

* changes approach for re-opening menus + removes isMenuVisible logic

* removes redundant event prevent default and stopPropogation

* fixes focus stealing issue

* hides menu on scroll

* cleanup

* pr fixes

* cleanup

* uses cell key to open table

* pr fixes

* simplifies the menu api

* adds openMenuAt to reduce casting to mouse event while opening menu via shortcut

* cleanup

* removes isMenuOpened guard

* moves menu to the center of icon when opens from keyboard

* removes onClose handling and focus cell in sync workaround

* adds keyboard event handling for the context menu api

* compares cells with keys utils helper

* removes open menu at

* removes setContext

* cleanup

* simplifies interfaces

* fixes bug with menu not being able to rerender

* restores focus on close

* dbeaver/pro#9308 simplify logic

* adds closing menu on scroll

* pr fixes

* dbeaver/pro#9308 adds context menu sr-only for accessibility

* adds arialabel for icon button

* Revert "adds arialabel for icon button"

This reverts commit a830301f7a.

* adds span to make scrolling work

---------

Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com>
Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-06-10 00:30:31 +02:00
sergeyteleshevandEvgenia 96ba18da83 [CB] Create project form with options tab (#4386)
* dbeaver/pro#9525 register project-info plugin

* adds project info to the context menu

* adds project info form

* adds project options part info tab content

* separates logic with bootstraps

* removes unused locales

* pr fixes

* removes global and shared checkboxes

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-06-09 19:52:04 +02:00
sergeyteleshevandmr-anton-t a16c138bba [CB] cleanup resolutions for package.json in all products (CE,EE, widgets) (#4376)
* dbeaver/pro#9234 removes happy-dom resolution

* removes all resolutions

* updates deps

* removes CVEs

* updates deps

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-09 19:49:24 +02:00
Tsimafei Labanovich f728590b90 dbeaver/pro#8766 Migrage from junit 4 to junit 6 (#4290)
* dbeaver/pro#8766 Boom

* dbeaver/pro#8766 Boom

* dbeaver/pro#8766 Boom

* dbeaver/pro#8766 Boom
2026-06-09 16:26:42 +02:00
Sychev AndreyandEvgenia ba7268bb8f dbeaver/pro#8684 feat: unlock autoscroll when dragging in data grid (#4362)
* dbeaver/pro#8684 feat: unlock autoscroll when dragging in data grid

* dbeaver/pro#8684 fix: don't select value panel and rows input in the toolbar on drag

as a bonus enhance data grid context menu accessibility to improve user selection behavior when we start dragging from the menu button

return selection only when the cell is edited

* dbeaver/pro#8684 refactor: add selection background color for input in editing cell

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-06-09 12:44:54 +02:00
Serge Rider 1b7c81fa24 Update readme 2026-06-09 09:26:58 +02:00
sergeyteleshevandmr-anton-t 98f8380962 [CB] Miragre Form Controls styles to CSS (#4365)
* dbeaver/pro#8782 migrates form controls from scss to css

* migrates all scss to css

* removes all scss mentions

* fixes input colors

* fixes thumb and link colors

* fixes padding & border errors (search panel & where filter)

* fixes disabled state for switches (input type="checkbox")

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-08 19:37:32 +02:00
Nikita Akilov 2d40781960 dbeaver/dbeaver-devops#2313 switch to JDK 25 in base-java (#4382) 2026-06-04 15:41:38 +02:00
Ainur 166efa634f dbeaver/pro#9511 fix unknown struct presentation (#4378) 2026-06-04 14:37:16 +02:00
Sychev Andrey fad07f3d73 dbeaver/pro#9438 fix: fix the export lib version (#4369)
* dbeaver/pro#9438 fix: fix the export lib version

* dbeaver/pro#9438 fix: update modern-screenshot dependency to version 4.6.8
2026-06-01 14:45:15 +02:00
Anastasiya 4ca4976184 dbeaver/pro#9401 Update README.md to fix formatting (#4371) 2026-06-01 11:55:23 +02:00
Daria Marutkina ed4f5e45ed dbeaver/pro#9401 Update README file (#4370)
Updated changelog for version 26.1.0, including new features and bug fixes.
2026-06-01 11:42:10 +02:00
Ainur a333e5841a dbeaver/pro#9447 validate project path fix (#4367)
* dbeaver/pro#9447 validate project path fix

* dbeaver/pro#9447 validate project name on creating project

* dbeaver/pro#9447 validate project using general utils
2026-05-29 10:27:10 +02:00
Serge Rider 2c3afd61f0 dbeaver/pro#9392 Nullability annos. DEad code delete. 2026-05-28 14:10:27 +02:00
Sychev AndreyandEvgenia a817dcd92f dbeaver/pro#9407 fix: long values wrapping (#4364)
we mimic here the old input's behaviour, when we can't see the scroll but can navigate through the 1-line value with keyboard or mouse drag/selection

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-27 20:38:22 +02:00
Serge Riderandkseniaguzeeva c14e8f5ce3 dbeaver/pro#9397 Initialize datasource registry flow fix (#4360)
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-05-26 17:46:12 +02:00
dbeaver-devops cc3cf21890 dbeaver/pro#9401 26.1.1 version bump 2026-05-25 20:54:14 +02:00
618 changed files with 9816 additions and 4358 deletions
+5 -9
View File
@@ -6,6 +6,7 @@ on:
- opened
- synchronize
- reopened
- ready_for_review
push:
branches: [devel]
@@ -24,20 +25,15 @@ jobs:
project-deps: ./cloudbeaver/project.deps
timeout-minutes: 5
# build-server:
# name: Server
# uses: ./.github/workflows/backend-build.yml
# secrets: inherit
build-frontend:
name: Frontend
uses: ./.github/workflows/frontend-build.yml
secrets: inherit
lint-server:
name: Server
uses: dbeaver/dbeaver-common/.github/workflows/java-checkstyle.yml@devel
secrets: inherit
# lint-server:
# name: Server
# uses: dbeaver/dbeaver-common/.github/workflows/java-checkstyle.yml@devel
# secrets: inherit
lint-frontend:
name: Frontend
+1 -2
View File
@@ -14,7 +14,6 @@
"redhat.vscode-yaml",
"streetsidesoftware.code-spell-checker",
"streetsidesoftware.code-spell-checker-russian",
"syler.sass-indented",
"github.copilot-chat",
"yzhang.markdown-all-in-one",
"GraphQL.vscode-graphql-syntax",
@@ -22,4 +21,4 @@
"esbenp.prettier-vscode",
"devnaumov.licensify"
]
}
}
+57 -145
View File
@@ -5,13 +5,16 @@
Cloud Database Manager - Community Edition.
CloudBeaver is a web server that provides a rich web interface. The server itself is a Java application, and the web part is written in TypeScript and React.
It is free to use and open-source (licensed under [Apache 2](https://github.com/dbeaver/cloudbeaver/blob/devel/LICENSE) license).
See our [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
<a><img src="https://github.com/dbeaver/cloudbeaver/wiki/images/connection-creation-demo.png" width="400"/></a>
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/gis-demo.png" width="400"/>
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/data-transfer-demo.png" width="400"/>
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/sql-editor-demo.png" width="400"/>
## Documentation
- [GitHub WIKI](https://github.com/dbeaver/cloudbeaver/wiki)
- [Official documentation](https://dbeaver.com/docs/cloudbeaver/)
## Run in Docker
- [Official Docker repository](https://hub.docker.com/r/dbeaver/cloudbeaver)
@@ -19,181 +22,90 @@ See our [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
## Demo server
You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
You can see a live demo of CloudBeaver server here: https://demo.cloudbeaver.io
[Database access instructions](https://github.com/dbeaver/cloudbeaver/wiki/Demo-Server)
## Changelog
### 26.0.5 2026-05-18
### 26.1.0 2026-06-01
### Changes since 26.0.0:
- Administration:
- Added support for custom startup arguments when running CloudBeaver in a container. Administrators can specify configuration file locations to adapt the application to their environment. (thanks to @MalteHei)
- Updated the manifest.webmanifest file to generate dynamically based on the CLOUDBEAVER_ROOT_URI environment variable. (thanks to @houssemexo26)
- Authorization:
- Added HTTP header authentication support for GraphQL queries. Users can provide API tokens via Authorization or X-Api-Key headers, allowing external data tools to authenticate without modifying request bodies.
- Data Editor:
- Added a DDL generation option to the Data Editor and Result Set. Users can view table schemas directly while editing or reviewing records.
- Fixed the copying of Boolean values in the clipboard in the Data Editor.
- Fixed a bug in the authentication form: the existing password is now required for password changes.
- SQL Editor:
- Updated the SQL Editor to use real tab characters for indentation. The application displayed tabs as four-space gaps with a distinct visual marker.
- Added an ability to view and edit scripts without connecting to the database.
- Added more settings to the Generated SQL window, including the ability to select fully qualified names for query or use compact SQL formatting.
- Added the ability to open generated queries right from the Generate SQL window.
- General:
- Updated the manifest.webmanifest file to dynamically generate the environment variable according to the CLOUDBEAVER_ROOT_URI. (thanks to @houssemexo26 )
- Databases:
- PostgreSQL:
- Updated driver to version 42.7.11.
- Fixed missing INOUT parameters in CHECK generation for procedures and functions.
- Fixed handling of 24:xx values in time columns.
- Snowflake: Updated driver to version 4.1.0.
- SQL Server: Fixed the issue with connecting to the database after the Java update.
- Security:
- Added an administrative setting to restrict SSH tunneling capabilities. Administrators can now limit tunnel configuration to authorized users, reducing the risk of unauthorized network access.
- Fixed the high vulnerability (CVE-2026-42035) in the axios library. The library was updated to version 1.15.1.
- Fixed the medium vulnerability (CVE-2026-41305) in the postcss library. The library was updated to version 8.5.14.
### 26.0.4 2026-05-04
- Added fuzzy search to SQL Editor auto-completion for object names. The feature allows users to find tables and containers even with typos or incorrect letter ordering.
- Added the tooltip about quoting rules for the Bind variables parameters dialog.
- Fixed keyword autocompletion for partially typed keywords to avoid redundant letters.
- Data Editor:
- Added the ability to copy-paste multiple cells at once. Pasted values will be distributed across separate cells, not put in a single cell.
- Added the ability to copy-paste multiple cells at once. Pasted values will be distributed across selected cells.
- Added an ability to add, duplicate, delete, or set values to NULL in multiple highlighted rows via the Data Editor context menu.
- Added the Find and Replace functionality for the Data Editor with the ability to find data by matching case, whole word, or using regular expressions.
- Data Editor started to keep the state of column configurations, such as filters, sorting, and ordering, after the reconnect, page refresh, and re-login.
- SQL Editor:
- Updated the SQL Editor to use real tab characters for indentation. The application displayed tabs as four-space gaps with a distinct visual marker.
- Added the ability to view and edit scripts without connecting to the database.
- Added more settings to the Generated SQL window, including the ability to select fully qualified names for query or use compact SQL formatting.
- Added the ability to open in the SQL Editor generated queries right from the Generate SQL window.
- Accessibility:
- Fixed contrast for elements across different application parts in the light and dark themes to meet WCAG requirements.
- General:
- Removed the "Folders" setting from the available settings for the Navigator Tree.
- Databases:
- DuckDB: Fixed map rendering for GEOMETRY types with CRS parameters.
- Firebird: Expanded database-specific SQL dialect coverage for the SQL Editor. Added more keywords, built-in functions, and SQL generators.
- MySQL: Fixed query boundaries recognition in the SQL Editor for scripts with the DELIMITER keyword.
- SQL Server: Fixed DDL generation for external tables.
- Security:
- Updated session handling to prompt the application to generate a new session identifier for each login.
- Fixed the high vulnerability (CVE-2026-3505) in the bcpg-jdk18on library. The library was updated to version 1.84.0.
### 26.0.3 2026-04-20
- Data Editor:
- Added key column mark for tables with unique keys in the data grid.
- Added the ability to paste a copied cell into multiple cells and to set multiple cells to NULL.
- Added more settings to the Generated SQL window, including the ability to select fully qualified names for query or use compact SQL formatting.
- Added a DDL generation option to the Data Editor and Result Set context menu.
- Fixed copy and paste behavior for multiline and special characters in the Data Editor. Added visual glyphs to indicate line breaks and tabs in the data grid.
- Fixed the ability to copy Boolean values in the clipboard in the Data Editor.
- Fixed the ability to calculate row count for non-standard SELECT statements. (thanks to @fdcastel)
- Navigator Tree:
- Navigator tree:
- Reorganized the context menu on the connection level to make it more compact.
- Added support for special symbols (pipe, comma, and asterisk) for the search field.
- Removed the "Folders" setting from the settings list for the Navigator Tree.
- Accessibility:
- Added the Skip to content option for quick keyboard access to the Navigator Tree, editors, and shortcuts to improve application accessibility.
- Added the Skip to content option for quick keyboard access to the Navigator Tree, editors, and shortcuts list tab to improve application accessibility.
- Improved keyboard navigation for context menu and buttons for Data Editor, SQL Editor, and Navigator tree.
- Added the Ctrl + / keyboard shortcut to open the context menu for selected cells in the Data Editor.
- Fixed the ability to switch tabs in the Data Editor using the keyboard.
- Fixed contrast for elements across different application parts in the light and dark themes to meet WCAG requirements.
- General:
- SQL Editor: Fixed keyword autocompletion for partially typed keywords. There are no extra letters for them after completion.
- Added support for custom startup arguments when running CloudBeaver in a container. Administrators can specify configuration file locations to adapt the application to their environment. (thanks to @MalteHei)
- Marked icons for non-saved (temporary) scripts with the hourglass symbol to add visual distinction from the saved ones.
- Added the ability to set to NULL any value in the Driver Properties.
- Fixed custom driver properties display for the connection page.
- Fixed data export in SQL format for values containing single quotes.
- Databases:
- ClickHouse: Fixed the display of Array(JSON) types in the data grid.
- Databases and drivers:
- ClickHouse:
- Fixed the display of Array(JSON) types in the data grid.
- Updated driver version to 0.9.7.
- Fixed IP address display in the Data Editor by removing the leading slash.
- Added support for the "prompt=false" connection property for DB2 for IBM i to turn off interactive prompts and prevent related errors.
- DuckDB:
- Fixed map rendering for GEOMETRY types with CRS parameters.
- Fixed geometry type display for database versions > 1.5.
- H2 database:
- Added new embedded driver version 2.4.2.
- Updated server driver to version 2.4.2.
- Firebird: Expanded database-specific SQL dialect coverage for the SQL Editor. Added more keywords, built-in functions, and SQL generators.
- MySQL: Fixed query boundaries recognition in the SQL Editor for scripts with the DELIMITER keyword.
- PostgreSQL:
- Updated driver to version 42.7.11
- Fixed missing INOUT parameters in CHECK generation for procedures and functions.
- Fixed handling of 24:xx values in time columns.
- Fixed an error when connecting via URL if the database name contains a hyphen.
- SQL Server:
- Fixed DDL generation for external tables.
- Fixed the database connectivity issue after the Java update.
- Security:
- Added an administrative setting to restrict SSH tunneling capabilities. Administrators can now limit tunnel configuration to authorized users, reducing the risk of unauthorized network access.
- Fixed the critical vulnerability (CVE-2025-62718) in the axios library. The library was updated to version 1.15.0.
- Fixed the high vulnerability (CVE-2026-4800) in the lodash library. The library was updated to version 4.18.0.
- Fixed the high vulnerability (CVE-2026-39363) in the vite library. The library was updated to version 7.3.2.
### 26.0.2 2026-04-06
- UI improvements:
- Marked icons for non-saved (temporary) scripts with the hourglass symbol to add visual distinction from the saved ones.
- Added the tooltip about quoting rules for the Bind variables parameters dialog.
- Improved keyboard navigation for context menu and buttons for Data Editor, SQL Editor, and Navigator tree.
- Authentication:
- Fixed the bug with the authentication form. Now the same password is required for the password change operation.
- Fixed the display of authentication fields for SQL Server with Active Directory - MSI. The unnecessary Password field was removed.
- Databases and drivers:
- DuckDB: Fixed geometry type display for database versions > 1.5. (thanks @EastLord).
- H2 database:
- Added new embedded driver version 2.4.2.
- Updated server driver to the version 2.4.2.
- Security:
- Fixed the high vulnerability (CVE-2026-42035) in the axios library. The library was updated to version 1.15.1.
- Fixed the high vulnerability (CVE-2026-3505) in the bcpg-jdk18on library. The library was updated to version 1.84.0.
- Fixed the high vulnerability (CVE-2026-33671) in the picomatch library. The library was updated to version 2.3.2.
- Fixed the high vulnerability (CVE-2026-33943) in the happy-dom library. The library was updated to version 20.8.8.
- Fixed the high vulnerability (CVE-2026-33228) in the flatted library. The library was updated to version 3.4.2.
### 26.0.1 2026-03-23
- Data Editor:
- Added the Find and Replace functionality for the Data Editor with the ability to find data by matching case, whole word, or using regular expressions.
- Added support for adding, duplicating, deleting, or setting values to NULL in multiple highlighted rows via the Data Editor context menu.
- SQL query generation in the Data Editor is now available for columns containing large object data.
- Improved column drag-and-drop behavior in the Data Editor. Users could drag columns from any area within the header and drop them onto any part of the target column or table body.
- SQL Editor:
- Added fuzzy search to SQL Editor auto-completion for object names. The feature allowed users to find tables and containers even with typos or incorrect letter ordering by matching strings that start with, contain, or resemble the input.
- Fixed data editing for SQL scripts with variables.
- General:
- Added a close button to the right tab when it is the only open tab (ex. Scripts). Users no longer needed to use the main menu to close a single tab.
- Replaced the generic "Something went wrong" message with a specific network error notification when the application cannot connect to the server.
- Null or empty values can now be assigned to the Driver properties for all drivers.
- Fixed an issue where re-selecting an existing SSH key file during connection edits caused problems.
- Fixed the ability to switch tabs in the Data editor using the keyboard.
- Fixed error notification spelling. Thanks to @Malcolm-B-Breaks.
- ClickHouse:
- Updated driver version to 0.9.7.
- Fixed IP address display in the Data Editor by removing the leading slash.
- PostgreSQL: Fixed an error when connecting via URL if the database name contains a hyphen.
- Security:
- Fixed the high vulnerability (CVE-2025-45141) in the tar library. The library was updated to version 7.5.11.
- Fixed the high vulnerability (CVE-2026-27148) in the storybook library. The library was updated to version 10.2.10.
- Fixed the high vulnerability (CVE-2026-27606) in the rollup library. The library was updated to version 4.59.0.
- Fixed the high vulnerability (CVE-2026-27904) in the minimatch library. The library was updated to version 3.1.3.
- Fixed the high vulnerability (CVE-2026-27959) in the koa library. The library was updated to version 2.16.4.
- Fixed the high vulnerability (CVE-2026-32141) in the flatted library. The library was updated to version 4.4.0.
### 26.0.0 2026-03-02
### Changes since 25.3.0:
- Security:
- Enforced complete logout and screen data clearance upon session expiration;
- Fixed the high vulnerability (CVE-2026-25639) in the axios library. The library was updated to version 1.13.5.
- Administration:
- Added support for mapping users to CloudBeaver teams based on LDAP memberOf group membership;
- Change the User list settings in the Administration part to show both active and inactive users by default.
- SQL Editor:
- Added support for parameters and variables in queries. This feature allows queries to be reused by changing parameters at execution time. Enabled by default and configurable in personal preferences;
- Added SQL preview to the Bind parameters/variables dialog to review queries with changed values on the fly;
- Enabled Tab key for autocompletion in the SQL Editor alongside the Enter key;
- Added a new setting in the SQL Editor to highlight spaces, tabs, and other whitespace characters to help users read, debug, and maintain their scripts. It is turned off by default and can be configured in personal preferences;
- Dangerous query confirmation is now shown for all DROP statements, not just for tables.
- Data Editor:
- Added ability to automatically generate INSERT, SELECT, DELETE, and UPDATE statements for the selected values;
- Added undo and redo functionality for cell edits, row operations, and other data modifications. Retains the last 50 actions across the Data Editor, result sets, and related panels;
- Added "Use local formatting" setting. Users can choose how to display numbers and dates: using the OS locale, a custom locale, or keeping values unformatted. This formatting applies only to displayed values. Data in the database remains unchanged;
- Added column pinning to keep key columns (e.g., IDs, names) visible while horizontal scrolling through wide tables;
- Added status indicator icon in the top-left corner with tooltips explaining table editability. Indicates presence of primary keys, read-only connection settings, or read-only columns;
- Added shortcut Ctrl/Cmd + . to cancel operations in Data Editor;
- Fixed application freeze in canceling fetch size requests for large tables.
- Navigator tree:
- Added the ability to duplicate connection configuration in the project navigation tree. The "Clone connection" feature is available in the context menu;
- Added the ability for users to configure the Simple or Advanced view in the Navigation tree for all connections or for each connection separately;
- Added the ability to show table objects, such as columns or keys, in the Navigation tree. The setting is disabled by default and can be turned on in the Navigator settings panel;
- Added the ability to rename connections via context menu in the Navigation Tree;
- Added Connection Info tab to display basic information about the current connection for all users.
- General:
- Added support for long polling as a fallback when WebSockets are unavailable or blocked. Ensures reliable communication for metadata updates and SQL execution;
- Extended browser support to versions up to three years old;
- Redesigned the connection configuration page. Reorganized form fields and sections to provide more input space and reduce visual clutter;
- Expanded pointer target areas for icons in the Navigator, editors, and tabs according to the accessibility standards;
- Fixed a keyboard navigation issue for panels to keep the focus inside;
- Renamed "Database Native" authentication type to "Username/password" in the connection dialog;
- Fixed issue where the missing pg_dump utility caused errors during initialization or deployment updates when PostgreSQL was selected as the inner database.
- Databases:
- ClickHouse:
- Updated driver to version 0.9.5
- Added spatial data support
- Fixed an issue with displaying arrays of UUID, IPv4/IPv6, and Map types
- Fixed JSON data type reading
- Resolved an issue with CTE expressions
- DuckDB:
- Updated driver to version 1.4.4.0;
- Added support for the dollar-quoted string syntax for the SQL Editor
- Oracle: Added a new "Set Username to OS_USER" option in the Misc section of Oracle connection settings. Automatically uses the current database username as the operating system user identifier in session metadata when enabled;
- PostgreSQL: Added DDL display support for PostgreSQL policies.
[We are happy to reward](https://dbeaver.com/help-dbeaver/) our most active contributors every major sprint.
- Fixed the medium vulnerability (CVE-2026-41305) in the postcss library. The library was updated to version 8.5.14.
+1 -13
View File
@@ -1,18 +1,6 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | --------- |
| 22.x | yes |
| 23.x | yes |
| 24.x | yes |
## Reporting a Vulnerability
Please report (suspected) security vulnerabilities to devops@dbeaver.com.
You will receive a response from us within 48 hours.
Please report (suspected) security vulnerabilities to <security@dbeaver.com>.
If the issue is confirmed, we will release a patch as soon as possible, depending on complexity, but historically, within a few days.
+61
View File
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.cloudbeaver</groupId>
<artifactId>config-generator</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.13.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>io.cloudbeaver.ConfigGenerator</mainClass>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<executions>
<execution>
<id>copy-external-config</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.outputDirectory}/config</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/../../config/template</directory>
<includes>
<include>cloudbeaver-base.conf</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,235 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.io.Reader;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
public class ConfigGenerator {
private static final Gson gson = new GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.create();
private static final String EMPTY_STRING = "''";
public static void main(String[] args) throws Exception {
String baseConfigPath = System.getProperty("config.base");
if (baseConfigPath == null || baseConfigPath.isEmpty()) {
URL resource = ConfigGenerator.class.getClassLoader().getResource("config/cloudbeaver-base.conf");
if (resource == null) {
System.out.println("Base config path is required");
return;
}
baseConfigPath = resource.toURI().getPath();
if (System.getProperty("os.name").toLowerCase().contains("win")) {
// Fix for Windows in dev mode. Resource URI starts with / (e.g. /C:/some/folder)
// And this breaks Java NIO
if (baseConfigPath.startsWith("/")) {
baseConfigPath = baseConfigPath.substring(1);
}
}
}
String outputPath = System.getProperty("config.output");
if (outputPath == null || outputPath.isEmpty()) {
System.out.println("Output config path is required");
return;
}
String configPatches = System.getProperty("config.patches");
List<String> overrideConfigs = configPatches == null ? List.of() : Arrays.asList(configPatches.split(","));
Map<String, Object> config = readConfigurationFile(baseConfigPath);
System.out.println("Base config loaded");
for (String overrideConfig : overrideConfigs) {
if (Files.exists(Path.of(overrideConfig))) {
System.out.println("Applying override config: " + overrideConfig);
} else {
System.out.println("Override config not found, skipping: " + overrideConfig);
continue;
}
System.out.println("Processing override config: " + overrideConfig);
Map<String, Object> overrideConfigMap = readConfigurationFile(overrideConfig);
System.out.println("Override config loaded: " + overrideConfig);
applyProductConfig(config, overrideConfigMap);
System.out.println("Override config applied: " + overrideConfig);
}
Map<String, Object> finalMap = convertToOutputFormat(config);
System.out.println("Config converted to output format");
writeConfigurationFile(finalMap, outputPath);
System.out.println("Config written to: " + outputPath);
}
private static Map<String, Object> readConfigurationFile(String filePath) throws Exception {
Path path = Path.of(filePath);
if (!Files.exists(path)) {
throw new IllegalArgumentException("Configuration file not found: " + filePath);
}
try (Reader reader = Files.newBufferedReader(path)) {
return gson.fromJson(reader, TypeToken.getParameterized(Map.class, String.class, Object.class).getType());
}
}
private static void applyProductConfig(Map<String, Object> baseConfig, Map<String, Object> productConfig) {
if (productConfig.containsKey("add")) {
Map<String, Object> add = (Map<String, Object>) productConfig.get("add");
deepMergeMap(baseConfig, add);
}
if (productConfig.containsKey("remove")) {
Map<String, Object> remove = (Map<String, Object>) productConfig.get("remove");
deepRemoveMap(baseConfig, remove);
}
}
private static void deepMergeMap(Map<String, Object> target, Map<String, Object> source) {
for (Map.Entry<String, Object> entry : source.entrySet()) {
Object sourceValue = entry.getValue();
Object targetValue = target.get(entry.getKey());
if (sourceValue instanceof Map && targetValue instanceof Map) {
deepMergeMap((Map<String, Object>) targetValue, (Map<String, Object>) sourceValue);
} else {
target.put(entry.getKey(), sourceValue);
}
}
}
private static void deepRemoveMap(Map<String, Object> target, Map<String, Object> source) {
for (Map.Entry<String, Object> entry : source.entrySet()) {
Object sourceValue = entry.getValue();
Object targetValue = target.get(entry.getKey());
if (sourceValue instanceof Map && targetValue instanceof Map) {
deepRemoveMap((Map<String, Object>) targetValue, (Map<String, Object>) sourceValue);
} else {
target.remove(entry.getKey());
}
}
}
private static void writeConfigurationFile(Map<String, Object> map, String filePath) throws Exception {
JsonElement tree = gson.toJsonTree(map);
StringBuilder sb = new StringBuilder();
writeElement(tree, sb, 0);
Files.write(Path.of(filePath), sb.toString().getBytes());
}
private static void writeElement(JsonElement elem, StringBuilder sb, int count) {
String indent = " ";
if (elem.isJsonObject()) {
sb.append("{\n");
JsonObject obj = elem.getAsJsonObject();
var iterator = obj.entrySet().iterator();
while (iterator.hasNext()) {
var entry = iterator.next();
sb.repeat(indent, count + 1).append(entry.getKey()).append(": ");
writeElement(entry.getValue(), sb, count + 1);
if (iterator.hasNext()) {
sb.append(",");
}
sb.append("\n");
}
sb.repeat(indent, count).append("}");
} else if (elem.isJsonArray()) {
sb.append("[\n");
JsonArray array = elem.getAsJsonArray();
for (int i = 0; i < array.size(); i++) {
sb.repeat(indent, count + 1);
writeElement(array.get(i), sb, count + 1);
if (i < array.size() - 1) {
sb.append(",");
}
sb.append("\n");
}
sb.repeat(indent, count).append("]");
} else {
String valueStr = gson.toJson(elem).replace("\n", "\n" + indent);
sb.append(valueStr);
}
}
private static Map<String, Object> convertToOutputFormat(Map<String, Object> map) {
Map<String, Object> result = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
result.put(entry.getKey(), convertValue(entry.getValue()));
}
return result;
}
private static Object convertValue(Object value) {
if (value instanceof Map) {
Map<String, Object> map = (Map<String, Object>) value;
if (!map.isEmpty() && map.containsKey("value")) {
if (map.containsKey("env")) {
String env = (String) map.get("env");
Object defValue = map.get("value");
return "${" + env + ":" + formatDefaultValue(defValue) + "}";
} else {
return convertValue(map.get("value"));
}
}
Map<String, Object> result = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
result.put(entry.getKey(), convertValue(entry.getValue()));
}
return result;
} else if (value instanceof List) {
List<Object> list = (List<Object>) value;
List<Object> result = new ArrayList<>();
for (Object item : list) {
result.add(convertValue(item));
}
return result;
}
return value;
}
private static String formatDefaultValue(Object value) {
return switch (value) {
case null -> EMPTY_STRING;
case String str -> str.isEmpty() ? EMPTY_STRING : str;
case Boolean b -> value.toString();
case Number number -> {
if (number instanceof Double || number instanceof Float) {
long longVal = number.longValue();
if (longVal == number.doubleValue()) {
yield String.valueOf(longVal);
}
}
yield number.toString();
}
// TODO: handle list elements if needed
default -> value.toString();
};
}
}
-99
View File
@@ -1,99 +0,0 @@
{
server: {
serverPort: "${CLOUDBEAVER_WEB_SERVER_PORT:8978}",
forceHttps: "${CLOUDBEAVER_FORCE_HTTPS:false}",
contentRoot: "web",
driversLocation: "drivers",
sslConfigurationPath:"${CLOUDBEAVER_SSL_CONF_PATH:workspace/.data/ssl-config.xml}",
rootURI: "${CLOUDBEAVER_ROOT_URI:/}",
serviceURI: "/api/",
supportedHosts: [],
productSettings: {
# Global properties
core.theming.theme: "${CLOUDBEAVER_CORE_THEMING_THEME:system}",
core.localization.language: "${CLOUDBEAVER_CORE_LOCALIZATION:en}",
plugin.sql-editor.autoSave: "${CLOUDBEAVER_SQL_EDITOR_AUTOSAVE:true}",
plugin.sql-editor.disabled: "${CLOUDBEAVER_SQL_EDITOR_DISABLED:false}",
# max size of the file that can be uploaded to the editor (in kilobytes)
plugin.sql-editor.maxFileSize: "${CLOUDBEAVER_SQL_EDITOR_MAX_FILE_SIZE:10240}",
plugin.log-viewer.disabled: "${CLOUDBEAVER_LOG_VIEWER_DISABLED:false}",
plugin.log-viewer.logBatchSize: "${CLOUDBEAVER_LOG_VIEWER_LOG_BATCH_SIZE:1000}",
plugin.log-viewer.maxLogRecords: "${CLOUDBEAVER_LOG_VIEWER_MAX_LOG_RECORDS:2000}",
sql.proposals.insert.table.alias: "${CLOUDBEAVER_SQL_PROPOSALS_INSERT_TABLE_ALIAS:PLAIN}",
SQLEditor.ContentAssistant.experimental.mode: "${CLOUDBEAVER_SQL_EDITOR_CONTENT_ASSISTANT_EXPERIMENTAL_MODE:NEW}"
},
expireSessionAfterPeriod: "${CLOUDBEAVER_EXPIRE_SESSION_AFTER_PERIOD:1800000}",
bindSessionToIp: "${CLOUDBEAVER_BIND_SESSION_TO_IP:disable}",
develMode: "${CLOUDBEAVER_DEVEL_MODE:false}",
enableSecurityManager: false,
sm: {
enableBruteForceProtection: "${CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED:true}",
maxFailedLogin: "${CLOUDBEAVER_MAX_FAILED_LOGINS:10}",
minimumLoginTimeout: "${CLOUDBEAVER_MINIMUM_LOGIN_TIMEOUT:1}",
blockLoginPeriod: "${CLOUDBEAVER_BLOCK_PERIOD:300}",
passwordPolicy: {
minLength: "${CLOUDBEAVER_POLICY_MIN_LENGTH:8}",
requireMixedCase: "${CLOUDBEAVER_POLICY_REQUIRE_MIXED_CASE:true}",
minNumberCount: "${CLOUDBEAVER_POLICY_MIN_NUMBER_COUNT:1}",
minSymbolCount: "${CLOUDBEAVER_POLICY_MIN_SYMBOL_COUNT:0}"
}
},
database: {
driver: "${CLOUDBEAVER_DB_DRIVER:h2_embedded_v2}",
url: "${CLOUDBEAVER_DB_URL:jdbc:h2:${workspace}/.data/cb.h2v2.dat}",
schema: "${CLOUDBEAVER_DB_SCHEMA:''}",
user: "${CLOUDBEAVER_DB_USER:''}",
password: "${CLOUDBEAVER_DB_PASSWORD:''}",
initialDataConfiguration: "${CLOUDBEAVER_DB_INITIAL_DATA:conf/initial-data.conf}",
pool: {
minIdleConnections: "${CLOUDBEAVER_DB_MIN_IDLE_CONNECTIONS:4}",
maxIdleConnections: "${CLOUDBEAVER_DB_MAX_IDLE_CONNECTIONS:10}",
maxConnections: "${CLOUDBEAVER_DB_MAX_CONNECTIONS:100}",
validationQuery: "${CLOUDBEAVER_DB_VALIDATION_QUERY:SELECT 1}"
},
backupEnabled: "${CLOUDBEAVER_DB_BACKUP_ENABLED:true}"
}
},
app: {
anonymousAccessEnabled: "${CLOUDBEAVER_APP_ANONYMOUS_ACCESS_ENABLED:true}",
anonymousUserRole: user,
defaultUserTeam: "${CLOUDBEAVER_APP_DEFAULT_USER_TEAM:user}",
grantConnectionsAccessToAnonymousTeam: "${CLOUDBEAVER_APP_GRANT_CONNECTIONS_ACCESS_TO_ANONYMOUS_TEAM:false}",
supportsCustomConnections: "${CLOUDBEAVER_APP_SUPPORTS_CUSTOM_CONNECTIONS:false}",
showReadOnlyConnectionInfo: "${CLOUDBEAVER_APP_READ_ONLY_CONNECTION_INFO:false}",
systemVariablesResolvingEnabled: "${CLOUDBEAVER_SYSTEM_VARIABLES_RESOLVING_ENABLED:false}",
forwardProxy: "${CLOUDBEAVER_APP_FORWARD_PROXY:false}",
publicCredentialsSaveEnabled: "${CLOUDBEAVER_APP_PUBLIC_CREDENTIALS_SAVE_ENABLED:true}",
adminCredentialsSaveEnabled: "${CLOUDBEAVER_APP_ADMIN_CREDENTIALS_SAVE_ENABLED:true}",
resourceManagerEnabled: "${CLOUDBEAVER_APP_RESOURCE_MANAGER_ENABLED:true}",
resourceQuotas: {
resourceManagerFileSizeLimit: "${CLOUDBEAVER_RESOURCE_QUOTA_RESOURCE_MANAGER_FILE_SIZE_LIMIT:500000}",
sqlMaxRunningQueries: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_MAX_RUNNING_QUERIES:100}",
sqlResultSetRowsLimit: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_RESULT_SET_ROWS_LIMIT:100000}",
sqlTextPreviewMaxLength: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_TEXT_PREVIEW_MAX_LENGTH:4096}",
sqlBinaryPreviewMaxLength: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_BINARY_PREVIEW_MAX_LENGTH:261120}"
},
enabledAuthProviders: [
"local"
],
disabledBetaFeatures: [
]
}
}
+271
View File
@@ -0,0 +1,271 @@
{
server: {
serverPort: {
env: "CLOUDBEAVER_WEB_SERVER_PORT",
value: 8978
},
forceHttps: {
env: "CLOUDBEAVER_FORCE_HTTPS",
value: false
},
contentRoot: {
env: "CLOUDBEAVER_CONTENT_ROOT",
value: "web"
},
driversLocation: {
env: "CLOUDBEAVER_DRIVERS_LOCATION",
value: "drivers"
},
sslConfigurationPath: {
env: "CLOUDBEAVER_SSL_CONF_PATH",
value: "workspace/.data/ssl-config.xml"
},
rootURI: {
env: "CLOUDBEAVER_ROOT_URI",
value: "/"
},
serviceURI: {
env: "CLOUDBEAVER_SERVICE_URI",
value: "/api/"
},
supportedHosts: [],
productSettings: {
core.theming.theme: {
env: "CLOUDBEAVER_CORE_THEMING_THEME",
value: "system"
},
core.localization.language: {
env: "CLOUDBEAVER_CORE_LOCALIZATION",
value: "en"
},
plugin.sql-editor.autoSave: {
env: "CLOUDBEAVER_SQL_EDITOR_AUTOSAVE",
value: true
},
plugin.sql-editor.disabled: {
env: "CLOUDBEAVER_SQL_EDITOR_DISABLED",
value: false
},
plugin.sql-editor.maxFileSize: {
env: "CLOUDBEAVER_SQL_EDITOR_MAX_FILE_SIZE",
value: 10240
},
plugin.log-viewer.disabled: {
env: "CLOUDBEAVER_LOG_VIEWER_DISABLED",
value: false
},
plugin.log-viewer.logBatchSize: {
env: "CLOUDBEAVER_LOG_VIEWER_LOG_BATCH_SIZE",
value: 1000
},
plugin.log-viewer.maxLogRecords: {
env: "CLOUDBEAVER_LOG_VIEWER_MAX_LOG_RECORDS",
value: 2000
},
sql.proposals.insert.table.alias: {
env: "CLOUDBEAVER_SQL_PROPOSALS_INSERT_TABLE_ALIAS",
value: "PLAIN"
},
SQLEditor.ContentAssistant.experimental.mode: {
env: "CLOUDBEAVER_SQL_EDITOR_CONTENT_ASSISTANT_EXPERIMENTAL_MODE",
value: "NEW"
}
},
expireSessionAfterPeriod: {
env: "CLOUDBEAVER_EXPIRE_SESSION_AFTER_PERIOD",
value: 1800000
},
bindSessionToIp: {
env: "CLOUDBEAVER_BIND_SESSION_TO_IP",
value: "disable"
},
develMode: {
env: "CLOUDBEAVER_DEVEL_MODE",
value: false
},
sm: {
enableBruteForceProtection: {
env: "CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED",
value: true
},
maxFailedLogin: {
env: "CLOUDBEAVER_MAX_FAILED_LOGINS",
value: 10
},
minimumLoginTimeout: {
env: "CLOUDBEAVER_MINIMUM_LOGIN_TIMEOUT",
value: 1
},
blockLoginPeriod: {
env: "CLOUDBEAVER_BLOCK_PERIOD",
value: 300
},
passwordPolicy: {
minLength: {
env: "CLOUDBEAVER_POLICY_MIN_LENGTH",
value: 8
},
requireMixedCase: {
env: "CLOUDBEAVER_POLICY_REQUIRE_MIXED_CASE",
value: true
},
minNumberCount: {
env: "CLOUDBEAVER_POLICY_MIN_NUMBER_COUNT",
value: 1
},
minSymbolCount: {
env: "CLOUDBEAVER_POLICY_MIN_SYMBOL_COUNT",
value: 0
}
}
},
database: {
driver: {
env: "CLOUDBEAVER_DB_DRIVER",
value: "h2_embedded_v2"
},
url: {
env: "CLOUDBEAVER_DB_URL",
value: "jdbc:h2:${workspace}/.data/cb.h2v2.dat"
},
schema: {
env: "CLOUDBEAVER_DB_SCHEMA",
value: ""
},
user: {
env: "CLOUDBEAVER_DB_USER",
value: ""
},
password: {
env: "CLOUDBEAVER_DB_PASSWORD",
value: ""
},
initialDataConfiguration: {
env: "CLOUDBEAVER_DB_INITIAL_DATA",
value: "conf/initial-data.conf"
},
pool: {
minIdleConnections: {
env: "CLOUDBEAVER_DB_MIN_IDLE_CONNECTIONS",
value: 4
},
maxIdleConnections: {
env: "CLOUDBEAVER_DB_MAX_IDLE_CONNECTIONS",
value: 10
},
maxConnections: {
env: "CLOUDBEAVER_DB_MAX_CONNECTIONS",
value: 100
},
validationQuery: {
env: "CLOUDBEAVER_DB_VALIDATION_QUERY",
value: "SELECT 1"
}
},
backupEnabled: {
env: "CLOUDBEAVER_DB_BACKUP_ENABLED",
value: true
}
}
},
app: {
anonymousAccessEnabled: {
env: "CLOUDBEAVER_APP_ANONYMOUS_ACCESS_ENABLED",
value: true
},
anonymousUserRole: {
env: "CLOUDBEAVER_APP_ANONYMOUS_USER_ROLE",
value: "user"
},
defaultUserTeam: {
env: "CLOUDBEAVER_APP_DEFAULT_USER_TEAM",
value: "user"
},
grantConnectionsAccessToAnonymousTeam: {
env: "CLOUDBEAVER_APP_GRANT_CONNECTIONS_ACCESS_TO_ANONYMOUS_TEAM",
value: false
},
supportsCustomConnections: {
env: "CLOUDBEAVER_APP_SUPPORTS_CUSTOM_CONNECTIONS",
value: false
},
showReadOnlyConnectionInfo: {
env: "CLOUDBEAVER_APP_READ_ONLY_CONNECTION_INFO",
value: false
},
systemVariablesResolvingEnabled: {
env: "CLOUDBEAVER_SYSTEM_VARIABLES_RESOLVING_ENABLED",
value: false
},
forwardProxy: {
env: "CLOUDBEAVER_APP_FORWARD_PROXY",
value: false
},
publicCredentialsSaveEnabled: {
env: "CLOUDBEAVER_APP_PUBLIC_CREDENTIALS_SAVE_ENABLED",
value: true
},
adminCredentialsSaveEnabled: {
env: "CLOUDBEAVER_APP_ADMIN_CREDENTIALS_SAVE_ENABLED",
value: true
},
resourceManagerEnabled: {
env: "CLOUDBEAVER_APP_RESOURCE_MANAGER_ENABLED",
value: true
},
resourceQuotas: {
resourceManagerFileSizeLimit: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_RESOURCE_MANAGER_FILE_SIZE_LIMIT",
value: 500000
},
sqlMaxRunningQueries: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_MAX_RUNNING_QUERIES",
value: 100
},
sqlResultSetRowsLimit: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_RESULT_SET_ROWS_LIMIT",
value: 100000
},
sqlTextPreviewMaxLength: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_TEXT_PREVIEW_MAX_LENGTH",
value: 4096
},
sqlBinaryPreviewMaxLength: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_BINARY_PREVIEW_MAX_LENGTH",
value: 261120
}
},
enabledAuthProviders: ["local"],
disabledBetaFeatures: []
}
}
+3
View File
@@ -31,6 +31,9 @@ if [[ "$?" -ne 0 ]] ; then
fi
cd ../../../deploy
echo "Generate cloudbeaver.conf file"
mvn -f ../apps/config-generator compile exec:java -Dconfig.output="cloudbeaver/conf/cloudbeaver.conf"
echo "Copy server packages"
cp -rp ../server/product/web-server/target/products/io.cloudbeaver.product/all/all/all/* ./cloudbeaver/server
+3
View File
@@ -42,6 +42,9 @@ copy ..\config\DefaultConfiguration\GlobalConfiguration\.dbeaver\data-sources.js
move drivers cloudbeaver >NUL
echo Generate cloudbeaver.conf file
call mvn -f ..\apps\config-generator compile exec:java -Dconfig.output="cloudbeaver\conf\cloudbeaver.conf" || goto :error
echo "Build static content"
mkdir .\cloudbeaver\web
+3 -2
View File
@@ -1,4 +1,5 @@
FROM ubuntu:24.04
# Ubuntu 24.04 (Noble Numbat)
FROM ubuntu:noble
MAINTAINER DBeaver Corp, devops@dbeaver.com
@@ -31,7 +32,7 @@ RUN set -eux; \
ENV LANG=en_US.UTF-8
ENV JAVA_HOME=/opt/java/openjdk
COPY --from=eclipse-temurin:21 $JAVA_HOME $JAVA_HOME
COPY --from=eclipse-temurin:25-jdk-noble $JAVA_HOME $JAVA_HOME
ENV PATH="${JAVA_HOME}/bin:${PATH}"
RUN set -eux; \
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Model
Bundle-SymbolicName: io.cloudbeaver.model.cli;singleton:=true
Bundle-Version: 1.0.11.qualifier
Bundle-Release-Date: 20260601
Bundle-Version: 1.0.13.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.model.cli;visibility:=reexport
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.model.cli</artifactId>
<version>1.0.11-SNAPSHOT</version>
<version>1.0.13-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Model
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
Bundle-Version: 1.0.102.qualifier
Bundle-Release-Date: 20260601
Bundle-Version: 1.0.104.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.model</artifactId>
<version>1.0.102-SNAPSHOT</version>
<version>1.0.104-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package io.cloudbeaver;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.utils.WebEventUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBPEvent;
@@ -46,7 +47,7 @@ public class WebDataSourceConnectEventListener implements DBPEventListener {
new WSDataSourceConnectEvent(
container.getProject().getId(),
container.getId(),
webSession.getSessionId(),
WebEventUtils.getSmSessionId(webSession),
webSession.getUserId()
)
);
@@ -30,6 +30,7 @@ import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.net.DBWNetworkProfile;
import org.jkiss.dbeaver.model.net.DBWNetworkProfileManager;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.secret.DBSSecretController;
@@ -258,28 +259,12 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
dataSourceRegistry.removeSavedFilter(filterName);
}
@Nullable
@Override
public DBWNetworkProfile getNetworkProfile(@Nullable String source, @NotNull String name) {
return dataSourceRegistry.getNetworkProfile(source, name);
}
@NotNull
@Override
public List<DBWNetworkProfile> getNetworkProfiles() {
public DBWNetworkProfileManager getNetworkProfiles() {
return dataSourceRegistry.getNetworkProfiles();
}
@Override
public void updateNetworkProfile(@NotNull DBWNetworkProfile profile) {
dataSourceRegistry.updateNetworkProfile(profile);
}
@Override
public void removeNetworkProfile(@NotNull DBWNetworkProfile profile) {
dataSourceRegistry.removeNetworkProfile(profile);
}
@Nullable
@Override
public DBAAuthProfile getAuthProfile(@NotNull String id) {
@@ -377,6 +362,11 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
return dataSourceRegistry.getPreferenceStore();
}
@Override
public void initializeDataSources() {
dataSourceRegistry.initializeDataSources();
}
@Override
public DataSourceParseResults loadDataSources(
@NotNull List<DBPDataSourceConfigurationStorage> storages,
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,8 +23,8 @@ import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBPEvent;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.security.user.SMObjectPermissions;
import java.util.Set;
@@ -29,11 +29,11 @@ import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBPObjectSettingsProvider;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistryCache;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.navigator.DBNModel;
import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.rm.RMUtils;
import org.jkiss.dbeaver.model.security.SMControllerUtils;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
@@ -39,7 +39,8 @@ import org.jkiss.dbeaver.model.connection.DBPDriverConfigurationType;
import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNative;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
import org.jkiss.dbeaver.model.navigator.DBNDataSource;
import org.jkiss.dbeaver.model.navigator.DBNDatabaseNode;
import org.jkiss.dbeaver.model.navigator.DBNUtils;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
import org.jkiss.dbeaver.model.preferences.DBPPropertySource;
@@ -213,7 +214,8 @@ public class WebConnectionInfo {
@Property
public String getNodePath() {
return DBNDataSource.makeDataSourceItemPath(dataSourceContainer);
DBNDatabaseNode dsNode = DBNUtils.getNodeByObject(dataSourceContainer);
return dsNode == null ? null : dsNode.getNodeUri();
}
@Property
@@ -115,13 +115,6 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
return ((DBNResourceManagerRoot) getParentNode()).getResourceController();
}
@NotNull
@Deprecated
@Override
public String getNodeItemPath() {
return getParentNode().getNodeItemPath() + "/" + getName();
}
@Nullable
@Override
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
@@ -160,13 +160,6 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
throw new DBException("Can't detect resource root node");
}
@NotNull
@Deprecated
@Override
public String getNodeItemPath() {
return getParentNode().getNodeItemPath() + "/" + getNodeDisplayName();
}
@Nullable
@Override
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
@@ -115,14 +115,6 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
return projects;
}
@NotNull
@Deprecated
@Override
public String getNodeItemPath() {
// Path doesn't include project name
return NodePathType.ext.getPrefix() + getNodeDisplayName();
}
@Nullable
@Override
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
@@ -33,6 +33,7 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.SMCredentials;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.fs.lock.LockManager;
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
@@ -41,7 +42,6 @@ import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
import org.jkiss.dbeaver.model.navigator.DBNLocalFolder;
import org.jkiss.dbeaver.model.rm.*;
import org.jkiss.dbeaver.model.security.SMAdminController;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.sql.DBQuotaException;
import org.jkiss.dbeaver.model.websocket.event.MessageType;
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
@@ -53,6 +53,7 @@ import org.jkiss.dbeaver.registry.DataSourceParseResults;
import org.jkiss.dbeaver.registry.ResourceTypeDescriptor;
import org.jkiss.dbeaver.registry.ResourceTypeRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.IOUtils;
import org.jkiss.utils.Pair;
@@ -283,7 +284,7 @@ public class LocalResourceController extends BaseLocalResourceController {
throw new DBException("Error creating shared project path", e);
}
}
validateResourcePath(name);
GeneralUtils.validateResourceNameUnconditionally(name);
validateProjectName(null, name);
var projectPath = sharedProjectsPath.resolve(name);
if (Files.exists(projectPath)) {
@@ -1106,6 +1107,7 @@ public class LocalResourceController extends BaseLocalResourceController {
RMProjectName project = WebRMUtils.parseProjectName(projectId);
RMProjectType type = project.getType();
String projectName = project.getName();
GeneralUtils.validateResourceNameUnconditionally(projectName);
switch (type) {
case GLOBAL:
if (!projectName.equals(globalProjectName)) {
@@ -209,7 +209,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
}
private void cleanUpSession(boolean sendSessionExpiredEvent) {
application.getEventController().addEvent(new WSEventDeleteTempFile(getSessionId()));
application.getEventController().addEvent(new WSEventDeleteTempFile(getUserContext().getSmSessionId()));
synchronized (sessionEventHandlers) {
var sessionExpiredEvent = new WSSessionExpiredEvent();
for (CBWebSessionEventHandler sessionEventHandler : sessionEventHandlers) {
@@ -313,7 +313,7 @@ public class WebSession extends BaseWebSession
loadProjects();
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), getWorkspace().getProjects());
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), getWorkspace());
this.navigatorModel.setModelAuthContext(getWorkspace().getAuthContext());
this.navigatorModel.initialize();
@@ -27,6 +27,7 @@ import org.jkiss.dbeaver.model.DBPImage;
import org.jkiss.dbeaver.model.app.DBPPlatform;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.fs.DBFFileSystemManager;
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
import org.jkiss.dbeaver.model.rm.RMUtils;
import org.jkiss.dbeaver.runtime.DBWorkbench;
@@ -46,6 +47,7 @@ public class WebSessionWorkspace implements DBPWorkspace {
private final SessionContextImpl workspaceAuthContext;
private final List<WebSessionProjectImpl> accessibleProjects = new ArrayList<>();
private WebSessionProjectImpl activeProject;
private DBFFileSystemManager fileSystemManager;
public WebSessionWorkspace(BaseWebSession session) {
this.session = session;
@@ -211,4 +213,17 @@ public class WebSessionWorkspace implements DBPWorkspace {
return ServletAppUtils.getServletApplication().getAppConfiguration().isFeatureEnabled(feature);
}
@NotNull
@Override
public synchronized DBFFileSystemManager getFileSystemManager() {
if (fileSystemManager == null) {
synchronized (this) {
if (fileSystemManager == null) {
fileSystemManager = new DBFFileSystemManager(session.getWorkspace());
}
}
}
return fileSystemManager;
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -245,7 +245,7 @@ public class WebUserContext implements SMCredentialsProvider {
return secretController;
}
@NotNull
@Nullable
public String getSmSessionId() {
return smSessionId;
}
@@ -114,4 +114,9 @@ public class WebUser {
public String getDisableReason() {
return user.getDisableReason();
}
@Nullable
public Instant getLastLoginTime() {
return user.getLastLoginTime();
}
}
@@ -82,4 +82,6 @@ public class CBConstants {
public static final String DEFAULT_CLOUD_PROJECT_NAME = "GlobalConfiguration";
public static final String SECURED_VALUE = "******";
public static final String PREF_DATA_EDITOR_EXPORT_DISABLED_OLD = "plugin.data-viewer.export.disabled";
public static final String PREF_DATA_EDITOR_IMPORT_DISABLED_OLD = "plugin.data-import.disabled";
}
@@ -319,8 +319,7 @@ public class ServletAppUtils {
if (log.isTraceEnabled()) {
log.trace("forwarded origin: " + origin);
}
}
if (CommonUtils.isEmpty(origin)) {
} else if (CommonUtils.isEmpty(origin)) {
URI requestUrl = URI.create(request.getRequestURL().toString());
if (log.isTraceEnabled()) {
log.trace("Request URL: " + requestUrl);
@@ -344,13 +343,34 @@ public class ServletAppUtils {
log.error("Failed to parse port from header: " + request.getHeader(HEADER_FORWARDED_PORT), e);
}
}
String finalScheme = uri.getScheme();
String finalHost = uri.getHost();
int finalPort = port;
boolean changed = false;
if (CommonUtils.isNotEmpty(forwardedScheme) && !forwardedScheme.equals(finalScheme)) {
finalScheme = forwardedScheme;
changed = true;
}
if (CommonUtils.isNotEmpty(forwardedHost) && !forwardedHost.equals(finalHost)) {
finalHost = forwardedHost;
changed = true;
}
if (DEFAULT_PORTS.contains(port)) {
finalPort = -1;
changed = true;
}
if (log.isTraceEnabled()) {
log.trace("Origin URI: " + origin);
}
if (changed) {
try {
origin = new URI(
uri.getScheme(),
finalScheme,
uri.getUserInfo(),
uri.getHost(),
-1,
finalHost,
finalPort,
uri.getPath(),
uri.getQuery(),
uri.getFragment()
@@ -359,9 +379,6 @@ public class ServletAppUtils {
log.error("Failed to create URI without port", e);
}
}
if (log.isTraceEnabled()) {
log.trace("Origin URI: " + origin);
}
return origin;
}
@@ -175,7 +175,7 @@ public class WebDataSourceUtils {
new WSDataSourceDisconnectEvent(
dataSource.getProject().getId(),
dataSource.getId(),
webSession.getSessionId(),
WebEventUtils.getSmSessionId(webSession),
webSession.getUserId()
)
);
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,119 +17,21 @@
package io.cloudbeaver.utils;
import io.cloudbeaver.model.WebAsyncTaskInfo;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.websocket.event.task.WSSessionTaskInfoEvent;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.security.SMSubjectType;
import org.jkiss.dbeaver.model.websocket.WSConstants;
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDatasourceFolderEvent;
import org.jkiss.dbeaver.model.websocket.event.permissions.WSSubjectPermissionEvent;
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceProperty;
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceUpdatedEvent;
import java.util.List;
import java.util.Objects;
public class WebEventUtils {
/**
* adds events of updated datasource to event controller
*
* @param project project of updated database
* @param session web session
* @param datasourceId id of datasource
* @param eventAction type of event
*/
public static void addDataSourceUpdatedEvent(
DBPProject project,
WebSession session,
String datasourceId,
WSConstants.EventAction eventAction,
WSDataSourceProperty property
) {
if (project == null) {
return;
}
WSEvent event = null;
switch (eventAction) {
case CREATE:
event = WSDataSourceEvent.create(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(datasourceId),
property
);
break;
case DELETE:
event = WSDataSourceEvent.delete(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(datasourceId),
property
);
break;
case UPDATE:
event = WSDataSourceEvent.update(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(datasourceId),
property
);
break;
}
if (event == null) {
return;
}
ServletAppUtils.getServletApplication().getEventController().addEvent(event);
}
public static void addNavigatorNodeUpdatedEvent(
DBPProject project,
WebSession session,
String nodePath,
WSConstants.EventAction eventAction
) {
if (project == null) {
return;
}
WSEvent event = null;
switch (eventAction) {
case CREATE:
event = WSDatasourceFolderEvent.create(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(nodePath)
);
break;
case DELETE:
event = WSDatasourceFolderEvent.delete(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(nodePath)
);
break;
case UPDATE:
event = WSDatasourceFolderEvent.update(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(nodePath)
);
break;
}
if (event == null) {
return;
}
ServletAppUtils.getServletApplication().getEventController().addEvent(event);
}
public static void addRmResourceUpdatedEvent(
String projectId,
@@ -153,7 +55,7 @@ public class WebEventUtils {
switch (eventAction) {
case CREATE:
event = WSResourceUpdatedEvent.create(
session.getUserContext().getSmSessionId(),
WebEventUtils.getSmSessionId(session),
session.getUserId(),
projectId,
resourcePath,
@@ -163,7 +65,7 @@ public class WebEventUtils {
break;
case DELETE:
event = WSResourceUpdatedEvent.delete(
session.getUserContext().getSmSessionId(),
WebEventUtils.getSmSessionId(session),
session.getUserId(),
projectId,
resourcePath,
@@ -173,7 +75,7 @@ public class WebEventUtils {
break;
case UPDATE:
event = WSResourceUpdatedEvent.update(
session.getUserContext().getSmSessionId(),
WebEventUtils.getSmSessionId(session),
session.getUserId(),
projectId,
resourcePath,
@@ -213,4 +115,12 @@ public class WebEventUtils {
ServletAppUtils.getServletApplication().getEventController().addEvent(event);
}
@Nullable
public static String getSmSessionId(@NotNull BaseWebSession webSession) {
return webSession.getUserContext().getSmSessionId();
}
public static boolean isSmSessionIdEquals(@NotNull BaseWebSession webSession, @Nullable String smSessionId) {
return Objects.equals(getSmSessionId(webSession), smSessionId);
}
}
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Community Product
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
Bundle-Version: 26.1.0.qualifier
Bundle-Release-Date: 20260601
Bundle-Version: 26.1.2.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.product.ce</artifactId>
<version>26.1.0-SNAPSHOT</version>
<version>26.1.2-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Cloudbeaver Base JDBC drivers
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
Bundle-Version: 1.0.147.qualifier
Bundle-Release-Date: 20260601
Bundle-Version: 1.0.149.qualifier
Bundle-Release-Date: 20260706
Bundle-Vendor: DBeaver Corp
Bundle-ActivationPolicy: lazy
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
@@ -9,6 +9,6 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
<version>1.0.147-SNAPSHOT</version>
<version>1.0.149-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: CloudBeaver Community
Bundle-SymbolicName: io.cloudbeaver.server.ce;singleton:=true
Bundle-Version: 26.1.0.qualifier
Bundle-Release-Date: 20260601
Bundle-Version: 26.1.2.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.server.ce</artifactId>
<version>26.1.0-SNAPSHOT</version>
<version>26.1.2-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -47,14 +47,14 @@ import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBConstants;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.app.DBPPlatform;
import org.jkiss.dbeaver.model.auth.AuthInfo;
import org.jkiss.dbeaver.model.auth.SMAuthConfiguration;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
import org.jkiss.dbeaver.model.security.SMAdminController;
import org.jkiss.dbeaver.model.security.SMConstants;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.websocket.event.WSEventController;
import org.jkiss.dbeaver.model.websocket.event.WSServerConfigurationChangedEvent;
import org.jkiss.dbeaver.runtime.DBWorkbench;
@@ -534,7 +534,7 @@ public abstract class CBApplication<T extends CBServerConfig>
public synchronized void finishConfiguration(
@NotNull String adminName,
@Nullable String adminPassword,
@NotNull List<AuthInfo> authInfoList,
@NotNull List<SMAuthConfiguration> authInfoList,
@NotNull CBServerConfig serverConfig,
@NotNull CBAppConfig appConfig,
@Nullable SMCredentialsProvider credentialsProvider
@@ -591,7 +591,7 @@ public abstract class CBApplication<T extends CBServerConfig>
protected abstract void finishSecurityServiceConfiguration(
@NotNull String adminName,
@Nullable String adminPassword,
@NotNull List<AuthInfo> authInfoList
@NotNull List<SMAuthConfiguration> authInfoList
) throws DBException;
public synchronized void flushConfiguration(SMCredentialsProvider webSession) throws DBException {
@@ -743,7 +743,7 @@ public abstract class CBApplication<T extends CBServerConfig>
sendConfigChangedEvent(credentialsProvider);
}
protected void sendConfigChangedEvent(SMCredentialsProvider credentialsProvider) {
protected void sendConfigChangedEvent(@Nullable SMCredentialsProvider credentialsProvider) {
String sessionId = null;
if (credentialsProvider != null && credentialsProvider.getActiveUserCredentials() != null) {
sessionId = credentialsProvider.getActiveUserCredentials().getSmSessionId();
@@ -27,7 +27,7 @@ import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBFileController;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.AuthInfo;
import org.jkiss.dbeaver.model.auth.SMAuthConfiguration;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.rm.RMController;
import org.jkiss.dbeaver.model.security.SMAdminController;
@@ -101,7 +101,7 @@ public class CBApplicationCE extends CBApplication<CBServerConfig> {
protected void finishSecurityServiceConfiguration(
@NotNull String adminName,
@Nullable String adminPassword,
@NotNull List<AuthInfo> authInfoList
@NotNull List<SMAuthConfiguration> authInfoList
) throws DBException {
if (securityController instanceof CBEmbeddedSecurityController<?> embeddedSecurityController) {
embeddedSecurityController.finishConfiguration(adminName, adminPassword, authInfoList);
@@ -24,6 +24,7 @@ import io.cloudbeaver.server.WebAppUtils;
import io.cloudbeaver.service.security.SMUtils;
import io.cloudbeaver.utils.ServletAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.security.SMAdminController;
@@ -33,7 +34,6 @@ import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.model.websocket.event.permissions.WSObjectPermissionEvent;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -43,10 +43,11 @@ import java.util.stream.Collectors;
public class WSObjectPermissionUpdatedEventHandler extends WSDefaultEventHandler<WSObjectPermissionEvent> {
private static final Log log = Log.getLog(WSObjectPermissionUpdatedEventHandler.class);
@Nullable
@Override
public void handleEvent(@NotNull WSObjectPermissionEvent event) {
protected Consumer<BaseWebSession> getEventConsumer(@NotNull WSObjectPermissionEvent event) {
String objectId = event.getObjectId();
Consumer<BaseWebSession> runnable = switch (event.getSmObjectType()) {
return switch (event.getSmObjectType()) {
case project:
yield getUpdateUserProjectsInfoConsumer(event, objectId);
case datasource:
@@ -61,23 +62,6 @@ public class WSObjectPermissionUpdatedEventHandler extends WSDefaultEventHandler
yield null;
}
};
if (runnable == null) {
return;
}
log.debug(event.getTopicId() + " event handled");
Collection<BaseWebSession> allSessions = CBApplication.getInstance().getSessionManager().getAllActiveSessions();
for (var activeUserSession : allSessions) {
if (!isAcceptableInSession(activeUserSession, event)) {
log.debug("Cannot handle %s event '%s' in session %s".formatted(
event.getTopicId(),
event.getId(),
activeUserSession.getSessionId()
));
continue;
}
log.debug("%s event '%s' handled".formatted(event.getTopicId(), event.getId()));
runnable.accept(activeUserSession);
}
}
@NotNull
@@ -156,10 +156,10 @@ public class RequestHostFilter implements Filter {
if (!availableHosts.contains(requestHost)) {
for (String errorPath : errorPaths) {
if (httpRequest.getServletPath().contains(errorPath)) {
log.warn("Request host '" + requestHost + "' is not allowed. Available hosts: " + availableHosts);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write(
"Request host '" + requestHost + "' is not allowed. Available hosts: " + availableHosts
);
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().write("Request host is not allowed. Available hosts: " + availableHosts);
return false;
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,9 +24,9 @@ import io.cloudbeaver.registry.WebSessionHandlerDescriptor;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.WebAppSessionManager;
import io.cloudbeaver.server.events.WSWebUtils;
import io.cloudbeaver.service.DBWSessionHandler;
import io.cloudbeaver.utils.ServletAppUtils;
import io.cloudbeaver.utils.WebEventUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
@@ -60,6 +60,7 @@ public class CBSessionManager implements WebAppSessionManager {
/**
* Closes Web Session, associated to HttpSession from {@code request}
*/
@Nullable
@Override
public BaseWebSession closeSession(@NotNull HttpServletRequest request) {
HttpSession session = request.getSession();
@@ -69,11 +70,13 @@ public class CBSessionManager implements WebAppSessionManager {
return null;
}
@Nullable
@Override
public BaseWebSession closeSession(@NotNull String sessionId) {
return closeSession(sessionId, true);
}
@Nullable
@Override
public BaseWebSession closeSession(@NotNull String sessionId, boolean sendSessionExpiredEvent) {
BaseWebSession webSession;
@@ -204,7 +207,7 @@ public class CBSessionManager implements WebAppSessionManager {
*
* @return WebSession object or null, if session expired or invalid
*/
@Nullable
@NotNull
public WebSession getOrRestoreWebSession(@NotNull WebHttpRequestInfo requestInfo) {
final var sessionId = requestInfo.getId();
if (sessionId == null) {
@@ -283,7 +286,7 @@ public class CBSessionManager implements WebAppSessionManager {
@Override
@Nullable
public WebSession findWebSession(HttpServletRequest request) {
public WebSession findWebSession(@NotNull HttpServletRequest request) {
String sessionId = getSessionId(request);
synchronized (sessionMap) {
var session = sessionMap.get(sessionId);
@@ -294,8 +297,9 @@ public class CBSessionManager implements WebAppSessionManager {
}
}
@Nullable
@Override
public WebSession findWebSession(HttpServletRequest request, boolean errorOnNoFound) throws DBWebException {
public WebSession findWebSession(@NotNull HttpServletRequest request, boolean errorOnNoFound) throws DBWebException {
WebSession webSession = findWebSession(request);
if (webSession != null) {
return webSession;
@@ -323,6 +327,7 @@ public class CBSessionManager implements WebAppSessionManager {
}
}
@NotNull
@Override
public Collection<BaseWebSession> getAllActiveSessions() {
synchronized (sessionMap) {
@@ -501,7 +506,7 @@ public class CBSessionManager implements WebAppSessionManager {
for (Iterator<BaseWebSession> iterator = sessionMap.values().iterator(); iterator.hasNext(); ) {
var session = iterator.next();
iterator.remove();
session.close(false, !WSWebUtils.isSessionIdEquals(session, initiatorSessionId));
session.close(false, !WebEventUtils.isSmSessionIdEquals(session, initiatorSessionId));
}
}
}
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Server
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
Bundle-Version: 26.1.0.qualifier
Bundle-Release-Date: 20260601
Bundle-Version: 26.1.2.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.model.sql;visibility:=reexport,
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.server</artifactId>
<version>26.1.0-SNAPSHOT</version>
<version>26.1.2-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -787,6 +787,9 @@ extend type Query {
"Reads session log entries"
readSessionLog(maxEntries: Int, clearEntries: Boolean): [ LogEntry! ]!
"Returns connection driver properties based on configuration"
listConnectionDriverProperties( projectId: ID!, config: ConnectionConfig! ): [ObjectPropertyInfo!]!
}
extend type Mutation {
@@ -55,8 +55,6 @@ type DatabaseObjectInfo {
}
type NavigatorNodeInfo {
"Node ID - generally a full path to the node from root of tree"
id: ID!
"Node URI - a unique path to a node including all parent nodes"
uri: ID! @since(version: "23.3.1")
"Node human readable name"
@@ -72,6 +72,17 @@ input SQLDataFilter {
orderBy: String
}
type SQLResultReference @since(version: "26.1.1") {
"Name of the association (foreign key)"
associationName: String!
"Fully qualified name of the entity"
targetEntityName: String
"Navigator node URI of the target entity, suitable for opening it directly in the UI. Null if the entity is not present in the navigator tree"
nodePath: String
"Indices of result-set columns that participate as the source-side attributes of this reference"
columnIndexList: [Int!]!
}
type SQLResultColumn {
position: Int!
name: String
@@ -122,6 +133,11 @@ type SQLResultSet {
"Returns list of rows in the result set. Each row contains data and metadata"
rowsWithMetaData: [SQLResultRowMetaData] @since(version: "23.3.5")
"Forward foreign keys navigable from this result set"
associations: [SQLResultReference!]! @since(version: "26.1.1")
"Reverse references (incoming FKs from other entities) navigable from this result set"
references: [SQLResultReference!]! @since(version: "26.1.1")
# True means that resultset was generated by single entity query
# New rows can be added, old rows can be deleted
singleEntity: Boolean!
@@ -451,6 +467,19 @@ extend type Mutation {
dataFormat: ResultDataFormat
): AsyncTaskInfo!
"Creates async task for navigating a foreign-key reference exposed in SQLResultSet.references. Pass associationName, isReference and any one of the chosen reference's columnIndices (used to locate the source entity)."
asyncSqlNavigateForeignKey(
projectId: ID,
connectionId: ID!,
contextId: ID!,
resultsId: ID!,
row: SQLResultRow!,
columnIndex: Int!,
associationName: String!,
isReference: Boolean!,
dataFormat: ResultDataFormat
): AsyncTaskInfo! @since(version: "26.1.1")
"Returns transaction log info for the specified project, connection and context"
getTransactionLogInfo(
projectId: ID!,
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,7 +38,9 @@ import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.impl.PropertyDescriptor;
import org.jkiss.dbeaver.model.navigator.DBNContainer;
import org.jkiss.dbeaver.model.navigator.DBNDataSource;
import org.jkiss.dbeaver.model.navigator.DBNNode;
@@ -47,6 +49,8 @@ import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
import org.jkiss.dbeaver.model.rm.RMProjectType;
import org.jkiss.dbeaver.registry.DataSourceNavigatorSettings;
import org.jkiss.dbeaver.runtime.properties.PropertyCollector;
import org.jkiss.dbeaver.runtime.properties.PropertySourceCustom;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;
import java.io.InputStream;
@@ -190,4 +194,52 @@ public class WebServiceUtils extends WebCommonUtils {
|| (node instanceof DBNResourceManagerResource
&& ((DBNResourceManagerResource) node).getResource().isFolder());
}
@NotNull
public static WebPropertyInfo[] getDriverProperties(
@NotNull WebSession webSession,
@NotNull DBPDriver driver,
@Nullable DBPDataSourceContainer dataSourceContainer,
@NotNull DBPConnectionConfiguration cfg
) {
try {
DBPPropertyDescriptor[] properties = driver.getDataSourceProvider().getConnectionProperties(
webSession.getProgressMonitor(),
driver,
dataSourceContainer,
cfg
);
Map<String, Object> connectionProperties = driver.getConnectionProperties();
for (Map.Entry<String, Object> connProp : connectionProperties.entrySet()) {
String propName = connProp.getKey();
Object propValue = connProp.getValue();
DBPPropertyDescriptor dbpPropertyDescriptor = new PropertyDescriptor(
null,
propName,
propName,
null,
false,
String.class,
propValue,
null
);
properties = ArrayUtils.add(DBPPropertyDescriptor.class, properties, dbpPropertyDescriptor);
cfg.setProperty(propName, (String) propValue);
}
if (properties == null) {
return new WebPropertyInfo[0];
}
PropertySourceCustom propertySource = new PropertySourceCustom(
properties,
cfg.getProperties()
);
return Arrays.stream(properties)
.map(p -> new WebPropertyInfo(webSession, p, propertySource)).toArray(WebPropertyInfo[]::new);
} catch (DBException e) {
log.error("Error reading driver properties:\n" + e.getMessage());
return new WebPropertyInfo[0];
}
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,11 +19,11 @@ package io.cloudbeaver.model;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.dbeaver.DBException;
import io.cloudbeaver.server.CBConstants;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBConstants;
import org.jkiss.dbeaver.model.connection.*;
import org.jkiss.dbeaver.model.impl.PropertyDescriptor;
import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNative;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
@@ -177,48 +177,16 @@ public class WebDatabaseDriverInfo {
return driver.getDefaultConnectionProperties();
}
@NotNull
@Property
public WebPropertyInfo[] getDriverProperties() throws DBWebException {
try {
DBPConnectionConfiguration cfg = new DBPConnectionConfiguration();
cfg.setUrl(CommonUtils.notEmpty(driver.getSampleURL()));
cfg.setHostName(DBConstants.HOST_LOCALHOST);
cfg.setHostPort(driver.getDefaultPort());
cfg.setDatabaseName(driver.getDefaultDatabase());
cfg.setUrl(driver.getConnectionURL(cfg));
DBPPropertyDescriptor[] properties = driver.getDataSourceProvider().getConnectionProperties(webSession.getProgressMonitor(), driver, cfg);
Map<String, Object> connectionProperties = driver.getConnectionProperties();
for (Map.Entry<String, Object> connProp : connectionProperties.entrySet()) {
String propName = connProp.getKey();
Object propValue = connProp.getValue();
DBPPropertyDescriptor dbpPropertyDescriptor = new PropertyDescriptor(
null,
propName,
propName,
null,
false,
String.class,
propValue,
null
);
properties = ArrayUtils.add(DBPPropertyDescriptor.class, properties, dbpPropertyDescriptor);
cfg.setProperty(propName, (String) propValue);
}
if (properties == null) {
return new WebPropertyInfo[0];
}
PropertySourceCustom propertySource = new PropertySourceCustom(
properties,
cfg.getProperties());
return Arrays.stream(properties)
.map(p -> new WebPropertyInfo(webSession, p, propertySource)).toArray(WebPropertyInfo[]::new);
} catch (DBException e) {
log.error("Error reading driver properties:\n" + e.getMessage());
return new WebPropertyInfo[0];
}
DBPConnectionConfiguration cfg = new DBPConnectionConfiguration();
cfg.setUrl(CommonUtils.notEmpty(driver.getSampleURL()));
cfg.setHostName(CBConstants.HOST_LOCALHOST);
cfg.setHostPort(driver.getDefaultPort());
cfg.setDatabaseName(driver.getDefaultDatabase());
cfg.setUrl(driver.getConnectionURL(cfg));
return WebServiceUtils.getDriverProperties(webSession, driver, null, cfg);
}
@Property
@@ -24,16 +24,20 @@ import io.cloudbeaver.model.session.WebSession;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jkiss.code.NotNull;
import org.jkiss.code.NotNullWhen;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import java.util.Collection;
public interface WebAppSessionManager {
@Nullable
BaseWebSession closeSession(@NotNull HttpServletRequest request);
@Nullable
BaseWebSession closeSession(@NotNull String sessionId);
@Nullable
BaseWebSession closeSession(@NotNull String sessionId, boolean sendSessionExpiredEvent);
@NotNull
@@ -53,21 +57,25 @@ public interface WebAppSessionManager {
BaseWebSession getSession(@NotNull String sessionId);
@Nullable
WebSession findWebSession(HttpServletRequest request);
WebSession findWebSession(@NotNull HttpServletRequest request);
WebSession findWebSession(HttpServletRequest request, boolean errorOnNoFound) throws DBWebException;
@NotNullWhen("errorOnNoFound")
WebSession findWebSession(@NotNull HttpServletRequest request, boolean errorOnNoFound) throws DBWebException;
@NotNull
Collection<BaseWebSession> getAllActiveSessions();
WebSession getOrRestoreWebSession(WebHttpRequestInfo httpRequest);
@NotNull
WebSession getOrRestoreWebSession(@NotNull WebHttpRequestInfo httpRequest);
@NotNullWhen("!create")
WebHeadlessSession getHeadlessSession(
@Nullable String smAccessToken,
@NotNull WebHttpRequestInfo requestInfo,
boolean create
) throws DBException;
boolean touchSession(HttpServletRequest request, HttpServletResponse response) throws DBWebException;
boolean touchSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws DBWebException;
default void expireIdleSessions() {
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import io.cloudbeaver.model.app.WebAppConfiguration;
import io.cloudbeaver.model.app.WebServerConfiguration;
import io.cloudbeaver.registry.WebDriverRegistry;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import java.net.InetAddress;
import java.util.List;
@@ -54,6 +55,7 @@ public interface WebApplication extends ServletApplication {
boolean isLicenseValid();
@Nullable
String getLicenseStatus();
@NotNull
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,14 +18,18 @@ package io.cloudbeaver.server.events;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.server.WebAppUtils;
import io.cloudbeaver.utils.WebEventUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.websocket.WSEventHandler;
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
import java.util.Collection;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class WSDefaultEventHandler<EVENT extends WSEvent> implements WSEventHandler<EVENT> {
public abstract class WSDefaultEventHandler<EVENT extends WSEvent> implements WSEventHandler<EVENT> {
private static final Log log = Log.getLog(WSDefaultEventHandler.class);
@@ -34,19 +38,24 @@ public class WSDefaultEventHandler<EVENT extends WSEvent> implements WSEventHand
if (!WebAppUtils.isWebApplication()) {
return;
}
log.debug(event.getTopicId() + " event handled");
Collection<BaseWebSession> allSessions = WebAppUtils.getWebApplication()
.getSessionManager()
.getAllActiveSessions();
for (var activeUserSession : allSessions) {
if (!isAcceptableInSession(activeUserSession, event)) {
log.debug("Cannot handle " + event.getTopicId() + " event '" + event.getId() +
"' in session " + activeUserSession.getSessionId());
continue;
}
log.debug(event.getTopicId() + " event '" + event.getId() + "' handled");
updateSessionData(activeUserSession, event);
Consumer<BaseWebSession> consumer = getEventConsumer(event);
if (consumer == null) {
return;
}
Set<String> sessionIds = WebAppUtils.getWebApplication().getSessionManager().getAllActiveSessions().stream()
.filter(session -> isAcceptableInSession(session, event))
.peek(consumer)
.map(session -> session.getUserContext().getSmSessionId())
.collect(Collectors.toSet());
if (!sessionIds.isEmpty()) {
log.debug(event.getTopicId() + " event '" + event.getId() + "' handled in sessions: " + String.join(", ", sessionIds));
}
}
@Nullable
protected Consumer<BaseWebSession> getEventConsumer(@NotNull EVENT event) {
return (session) -> updateSessionData(session, event);
}
protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull EVENT event) {
@@ -54,6 +63,6 @@ public class WSDefaultEventHandler<EVENT extends WSEvent> implements WSEventHand
}
protected boolean isAcceptableInSession(@NotNull BaseWebSession activeUserSession, @NotNull EVENT event) {
return !WSWebUtils.isSessionIdEquals(activeUserSession, event.getSessionId()); // skip events from current session
return !WebEventUtils.isSmSessionIdEquals(activeUserSession, event.getSessionId()); // skip events from current session
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package io.cloudbeaver.server.events;
import io.cloudbeaver.model.WebServerMessage;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.utils.WebEventUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
@@ -28,6 +29,6 @@ public class WSLogEventHandler extends WSDefaultEventHandler<WSSessionLogUpdated
}
protected boolean isAcceptableInSession(@NotNull BaseWebSession activeUserSession, @NotNull WSSessionLogUpdatedEvent event) {
return WSWebUtils.isSessionIdEquals(activeUserSession, event.getSessionId());
return WebEventUtils.isSmSessionIdEquals(activeUserSession, event.getSessionId());
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package io.cloudbeaver.server.events;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.utils.WebEventUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
@@ -54,7 +55,7 @@ public class WSProjectUpdatedEventHandler extends WSAbstractProjectEventHandler<
@Override
protected boolean isAcceptableInSession(@NotNull BaseWebSession activeUserSession, @NotNull WSProjectUpdateEvent event) {
return !WSWebUtils.isSessionIdEquals(activeUserSession, event.getSessionId()) &&
return !WebEventUtils.isSmSessionIdEquals(activeUserSession, event.getSessionId()) &&
(!event.getId().equals(WSProjectUpdateEvent.ADDED) ||
activeUserSession.getUserContext().hasPermission(DBWConstants.PERMISSION_ADMIN));
}
@@ -1,39 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2022 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.server.events;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import java.util.Objects;
public class WSWebUtils {
public static boolean isSessionIdEquals(@NotNull BaseWebSession webSession, @Nullable String sessionId) {
if (sessionId == null) {
return false;
}
if (Objects.equals(webSession.getSessionId(), sessionId)) {
return true;
}
if (webSession instanceof WebSession) {
return Objects.equals(webSession.getUserContext().getSmSessionId(), sessionId);
}
return false;
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,17 +19,15 @@ package io.cloudbeaver.server.jobs;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.WebAppSessionManager;
import io.cloudbeaver.utils.WebEventUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.DBPDataSource;
import org.jkiss.dbeaver.model.app.DBPPlatform;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceDisconnectEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.runtime.jobs.DataSourceMonitorJob;
import java.util.Collection;
import java.util.List;
/**
* Web data source monitor job.
@@ -62,7 +60,7 @@ public class WebDataSourceMonitorJob extends DataSourceMonitorJob {
new WSDataSourceDisconnectEvent(
project.getId(),
dataSource.getContainer().getId(),
webSession.getSessionId(),
WebEventUtils.getSmSessionId(webSession),
webSession.getUserId()
)
);
@@ -77,6 +77,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
/**
* Creates proxy for permission checks and other general API calls validation/logging.
*/
@NotNull
protected API_TYPE getService(DataFetchingEnvironment env) {
Object proxyImpl = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{apiInterface}, new ServiceInvocationHandler(serviceImpl, env));
return apiInterface.cast(proxyImpl);
@@ -99,20 +100,23 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
}
}
protected static HttpServletResponse getServletResponse(DataFetchingEnvironment env) {
@NotNull
protected static HttpServletResponse getServletResponse(@NotNull DataFetchingEnvironment env) {
return GraphQLEndpoint.getServletResponse(env);
}
protected static DBWBindingContext getBindingContext(DataFetchingEnvironment env) {
@NotNull
protected static DBWBindingContext getBindingContext(@NotNull DataFetchingEnvironment env) {
return GraphQLEndpoint.getBindingContext(env);
}
protected static WebSession getWebSession(DataFetchingEnvironment env) throws DBWebException {
@NotNull
protected static WebSession getWebSession(@NotNull DataFetchingEnvironment env) throws DBWebException {
return getWebSession(env, true);
}
@Nullable
protected static WebSession getSessionFromContext(DataFetchingEnvironment env) {
protected static WebSession getSessionFromContext(@NotNull DataFetchingEnvironment env) {
return env.getGraphQlContext().get(WebSession.class.getName());
}
@@ -125,6 +129,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
return webSession;
}
@NotNull
protected static WebSession getWebSession(@NotNull DataFetchingEnvironment env, boolean errorOnNotFound) throws DBWebException {
if (env.getGraphQlContext().getBoolean(CloudbeaverCliConstants.CLI_MODE)) {
return getSessionFromContextOrThrow(env);
@@ -133,6 +138,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
GraphQLEndpoint.getServletRequestOrThrow(env), getServletResponse(env), errorOnNotFound);
}
@Nullable
protected static String getProjectReference(@NotNull DataFetchingEnvironment env) {
return env.getArgument("projectId");
}
@@ -154,6 +160,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
GraphQLEndpoint.getServletRequestOrThrow(env));
}
@NotNull
public static WebSession findWebSession(@NotNull DataFetchingEnvironment env, boolean errorOnNotFound) throws DBWebException {
return WebAppUtils.getWebApplication().getSessionManager().findWebSession(
GraphQLEndpoint.getServletRequestOrThrow(env), errorOnNotFound);
@@ -261,7 +268,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
throw new DBException("Web session not instantiated");
}
String projectId = args[objectIdArgumentIndex] == null ? null : String.valueOf(args[objectIdArgumentIndex]);
String projectId = args[objectIdArgumentIndex] == null ? "" : String.valueOf(args[objectIdArgumentIndex]);
// we should always get the project from the session, even if projectId is null - the active project
// will be returned
WebProjectImpl project = webSession.getProjectById(projectId);
@@ -108,6 +108,12 @@ public interface DBWServiceCore extends DBWService {
///////////////////////////////////////////
// Connections
@NotNull
WebPropertyInfo[] getDriverProperties(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull Map<String, Object> connectionConfig
) throws DBWebException;
@WebAction
WebConnectionInfo getConnectionState(WebSession webSession, @Nullable String projectId, String connectionId) throws DBWebException;
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -81,6 +81,13 @@ public class WebServiceBindingCore extends WebServiceBindingBase<DBWServiceCore>
getArgument(env, "maxEntries"),
getArgument(env, "clearEntries"));
})
.dataFetcher(
"listConnectionDriverProperties", env -> getService(env).getDriverProperties(
getWebSession(env),
getArgumentVal(env, "projectId"),
getArgumentVal(env, "config")
)
)
;
model.getMutationType()
@@ -43,6 +43,7 @@ import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBPDataSourceFolder;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.exec.DBCConnectException;
@@ -58,7 +59,6 @@ import org.jkiss.dbeaver.model.rm.RMProjectType;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.secret.DBSSecretController;
import org.jkiss.dbeaver.model.secret.DBSSecretValue;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
import org.jkiss.dbeaver.registry.DataSourceNavigatorSettings;
import org.jkiss.dbeaver.registry.DataSourceNavigatorSettingsUtils;
@@ -296,6 +296,31 @@ public class WebServiceCore implements DBWServiceCore {
return WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, connectionId);
}
@NotNull
@Override
public WebPropertyInfo[] getDriverProperties(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull Map<String, Object> connectionConfig
) throws DBWebException {
WebSessionProjectImpl project = getProjectById(webSession, projectId);
WebConnectionConfig configInput = project.getConnectionConfigInput(connectionConfig);
configInput.setSaveCredentials(true); // It is used in createConnectionFromConfig
DataSourceDescriptor dataSource = (DataSourceDescriptor) WebDataSourceUtils.getLocalOrGlobalDataSource(
webSession, projectId, configInput.getConnectionId());
DataSourceDescriptor testDataSource = getDataSourceDescriptor(webSession, dataSource, configInput, project);
DBPConnectionConfiguration connectionConfiguration = new DBPConnectionConfiguration(testDataSource.getConnectionConfiguration());
return WebServiceUtils.getDriverProperties(
webSession,
testDataSource.getDriver(),
testDataSource,
connectionConfiguration
);
}
@Override
public WebConnectionInfo initConnection(
@@ -515,7 +540,41 @@ public class WebServiceCore implements DBWServiceCore {
DataSourceDescriptor dataSource = (DataSourceDescriptor) WebDataSourceUtils.getLocalOrGlobalDataSource(
webSession, projectId, configInput.getConnectionId());
DataSourceDescriptor testDataSource = getDataSourceDescriptor(webSession, dataSource, configInput, project);
try {
ConnectionTestJob ct = new ConnectionTestJob(
testDataSource, param -> {
}
);
ct.run(webSession.getProgressMonitor());
if (ct.getConnectError() != null) {
if (ct.getConnectError() instanceof DBCConnectException error) {
Throwable rootCause = CommonUtils.getRootCause(error);
if (rootCause instanceof ClassNotFoundException) {
log.error(error);
throwDriverNotFoundException(testDataSource);
}
}
throw new DBWebException("Connection failed", ct.getConnectError());
}
WebConnectionInfo connectionInfo = project.createConnectionInfo(testDataSource);
connectionInfo.setConnectError(ct.getConnectError());
connectionInfo.setServerVersion(ct.getServerVersion());
connectionInfo.setClientVersion(ct.getClientVersion());
connectionInfo.setConnectTime(RuntimeUtils.formatExecutionTime(ct.getConnectTime()));
return connectionInfo;
} catch (DBException e) {
throw new DBWebException("Error connecting to database", e);
}
}
@NotNull
private DataSourceDescriptor getDataSourceDescriptor(
@NotNull WebSession webSession,
@Nullable DataSourceDescriptor dataSource,
@NotNull WebConnectionConfig configInput,
@NotNull WebSessionProjectImpl project
) throws DBWebException {
DataSourceDescriptor testDataSource;
if (dataSource != null) {
try {
@@ -563,31 +622,7 @@ public class WebServiceCore implements DBWServiceCore {
);
testDataSource.setSavePassword(true); // We need for test to avoid password callback
testDataSource.setAccessCheckRequired(!webSession.hasPermission(DBWConstants.PERMISSION_ADMIN));
try {
ConnectionTestJob ct = new ConnectionTestJob(
testDataSource, param -> {
}
);
ct.run(webSession.getProgressMonitor());
if (ct.getConnectError() != null) {
if (ct.getConnectError() instanceof DBCConnectException error) {
Throwable rootCause = CommonUtils.getRootCause(error);
if (rootCause instanceof ClassNotFoundException) {
log.error(error);
throwDriverNotFoundException(testDataSource);
}
}
throw new DBWebException("Connection failed", ct.getConnectError());
}
WebConnectionInfo connectionInfo = project.createConnectionInfo(testDataSource);
connectionInfo.setConnectError(ct.getConnectError());
connectionInfo.setServerVersion(ct.getServerVersion());
connectionInfo.setClientVersion(ct.getClientVersion());
connectionInfo.setConnectTime(RuntimeUtils.formatExecutionTime(ct.getConnectTime()));
return connectionInfo;
} catch (DBException e) {
throw new DBWebException("Error connecting to database", e);
}
return testDataSource;
}
@Override
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -97,7 +97,8 @@ public class WebDefaultFeatureProvider implements DBWFeatureProvider {
} else {
features.add(NODE_FEATURE_CAN_FILTER);
}
isShared = !node.getOwnerProject().getName().equals(webSession.getUserId());
DBPProject nodeProject = node.getOwnerProjectOrNull();
isShared = nodeProject != null && !nodeProject.getName().equals(webSession.getUserId());
} else if (node instanceof DBNLocalFolder dbnLocalFolder) {
DBPDataSourceFolder folder = dbnLocalFolder.getFolder();
DBPProject project = folder.getDataSourceRegistry().getProject();
@@ -168,7 +169,11 @@ public class WebDefaultFeatureProvider implements DBWFeatureProvider {
if (node instanceof DBNResourceManagerResource rmr) {
rmProject = rmr.getRmProject();
} else {
WebProjectImpl project = webSession.getProjectById(node.getOwnerProject().getId());
DBPProject nodeProject = node.getOwnerProjectOrNull();
if (nodeProject == null) {
return false;
}
WebProjectImpl project = webSession.getProjectById(nodeProject.getId());
if (project == null) {
return false;
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -67,12 +67,6 @@ public class WebNavigatorNodeInfo {
// General properties
///////////////////////////////////
@Property
@Deprecated(forRemoval = true)
public String getId() {
return node.getNodeItemPath();
}
@Property
public String getUri() {
return node.getNodeUri();
@@ -209,6 +203,8 @@ public class WebNavigatorNodeInfo {
return DBFUtils.getUriFromPath(dbnPath.getPath()).toString();
} else if (node instanceof DBNFileSystem dbnFs) {
return WebFSUtils.makeUniqueFsId(dbnFs.getFileSystem());
} else if (node instanceof DBNDatabaseNode dbNode) {
return dbNode.getDataSourceContainer().getId();
}
return null;
}
@@ -173,18 +173,14 @@ public class WebServiceNavigator implements DBWServiceNavigator {
throw new DBWebException("Node '" + nodePath + "' not found");
}
boolean shouldSkipProjectNode = nodePath.startsWith(DBNNode.NodePathType.ext.getPrefix());
List<WebNavigatorNodeInfo> nodeParents = new ArrayList<>();
for (DBNNode parent = getLogicalParentNode(node);
parent != null && !(parent instanceof DBNRoot);
parent = getLogicalParentNode(parent)) {
//FIXME remove after node path refactoring
parent = getLogicalParentNode(parent)
) {
if (parent instanceof DBNProjectDatabases) {
continue;
}
if (parent instanceof DBNProject && shouldSkipProjectNode) {
continue;
}
nodeParents.add(new WebNavigatorNodeInfo(session, parent));
}
@@ -462,7 +458,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
} else {
node.rename(session.getProgressMonitor(), newName);
}
return node.getNodeItemPath();
return node.getNodeUri();
}
if (node instanceof DBNDatabaseNode dbNode) {
return renameDatabaseObject(
@@ -723,7 +719,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
commandContext.resetChanges(true);
throw e;
}
return node.getNodeItemPath();
return node.getNodeUri();
}
}
}
@@ -126,6 +126,17 @@ public interface DBWServiceSQL extends DBWService {
@Nullable WebSQLDataFilter filter,
@Nullable WebDataFormat dataFormat) throws DBWebException;
@WebAction
WebAsyncTaskInfo asyncNavigateForeignKey(
@NotNull WebSession webSession,
@NotNull WebSQLContextInfo contextInfo,
@NotNull String resultsId,
@NotNull WebSQLResultsRow row,
int columnIndex,
@NotNull String associationName,
boolean isReference,
@Nullable WebDataFormat dataFormat) throws DBException;
/**
* Reads dynamic trace from provided database results.
*/
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import io.cloudbeaver.model.WebTransactionLogItemInfo;
import io.cloudbeaver.model.session.WebAsyncTaskProcessor;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.session.WebSessionProvider;
import io.cloudbeaver.utils.WebEventUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
@@ -299,6 +300,7 @@ public class WebSQLContextInfo implements WebSessionProvider {
}
@NotNull
public WebAsyncTaskInfo commitTransaction() {
DBCExecutionContext context = processor.getExecutionContext();
DBCTransactionManager txnManager = DBUtils.getTransactionManager(context);
@@ -323,7 +325,7 @@ public class WebSQLContextInfo implements WebSessionProvider {
}
processor.getWebSession().addSessionEvent(
new WSTransactionalCountEvent(
processor.getWebSession().getSessionId(),
WebEventUtils.getSmSessionId(processor.getWebSession()),
processor.getWebSession().getUserId(),
getProjectId(),
getId(),
@@ -338,6 +340,7 @@ public class WebSQLContextInfo implements WebSessionProvider {
}
@NotNull
public WebAsyncTaskInfo rollbackTransaction() {
DBCExecutionContext context = processor.getExecutionContext();
DBCTransactionManager txnManager = DBUtils.getTransactionManager(context);
@@ -361,7 +364,7 @@ public class WebSQLContextInfo implements WebSessionProvider {
);
processor.getWebSession().addSessionEvent(
new WSTransactionalCountEvent(
processor.getWebSession().getSessionId(),
WebEventUtils.getSmSessionId(processor.getWebSession()),
processor.getWebSession().getUserId(),
getProjectId(),
getId(),
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import io.cloudbeaver.model.session.WebSessionProvider;
import io.cloudbeaver.server.WebAppUtils;
import io.cloudbeaver.server.jobs.SqlOutputLogReaderJob;
import io.cloudbeaver.service.sql.messages.WebSQLMessages;
import io.cloudbeaver.utils.WebEventUtils;
import io.cloudbeaver.websocket.event.task.WSSessionTaskConfirmationRequestEvent;
import io.cloudbeaver.websocket.event.task.WSSessionTaskQueryConfirmationRequestEvent;
import org.eclipse.jface.text.Document;
@@ -478,7 +479,7 @@ public class WebSQLProcessor implements WebSessionProvider {
sendTransactionalEvent(contextInfo);
}
WebSQLQueryResultSet updatedResultSet = new WebSQLQueryResultSet();
WebSQLQueryResultSet updatedResultSet = new WebSQLQueryResultSet(webSession);
updatedResultSet.setResultsInfo(resultsInfo);
updatedResultSet.setColumns(resultsInfo.getAttributes());
@@ -494,11 +495,11 @@ public class WebSQLProcessor implements WebSessionProvider {
return result;
}
private void sendTransactionalEvent(WebSQLContextInfo contextInfo) {
private void sendTransactionalEvent(@NotNull WebSQLContextInfo contextInfo) {
int count = QMUtils.getTransactionState(getExecutionContext()).getUpdateCount();
webSession.addSessionEvent(
new WSTransactionalCountEvent(
contextInfo.getWebSession().getSessionId(),
WebEventUtils.getSmSessionId(webSession),
contextInfo.getWebSession().getUserId(),
contextInfo.getProjectId(),
contextInfo.getId(),
@@ -650,7 +651,7 @@ public class WebSQLProcessor implements WebSessionProvider {
DBDAttributeBinding[] allAttributes = resultsInfo.getAttributes();
DBDAttributeBinding[] keyAttributes = rowIdentifier.getAttributes().toArray(new DBDAttributeBinding[0]);
WebSQLQueryResultSet updatedResultSet = new WebSQLQueryResultSet();
WebSQLQueryResultSet updatedResultSet = new WebSQLQueryResultSet(webSession);
updatedResultSet.setResultsInfo(resultsInfo);
updatedResultSet.setColumns(resultsInfo.getAttributes());
@@ -54,7 +54,7 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
private final WebDataFormat dataFormat;
@Nullable
private final DBDDataFilter dataFilter;
private final WebSQLQueryResultSet webResultSet = new WebSQLQueryResultSet();
private final WebSQLQueryResultSet webResultSet;
private DBDAttributeBinding[] bindings;
private DBCTrace trace;
@@ -71,6 +71,7 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
this.dataContainer = dataContainer;
this.dataFormat = dataFormat;
this.dataFilter = dataFilter;
this.webResultSet = new WebSQLQueryResultSet(contextInfo.getProcessor().getWebSession());
rowLimit = ServletAppUtils.getServletApplication()
.getAppConfiguration()
.getResourceQuota(WebSQLConstants.QUOTA_PROP_ROW_LIMIT);
@@ -1,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.
@@ -0,0 +1,112 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.service.sql;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPEvaluationContext;
import org.jkiss.dbeaver.model.DBUtils;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.navigator.DBNNode;
import org.jkiss.dbeaver.model.struct.DBSEntity;
import org.jkiss.dbeaver.model.struct.DBSEntityAssociation;
import org.jkiss.dbeaver.model.struct.DBSEntityConstraint;
import java.util.List;
public class WebSQLQueryResultReference {
private static final Log log = Log.getLog(WebSQLQueryResultReference.class);
@Nullable
private final WebSession session;
@NotNull
private final DBSEntityAssociation association;
private final boolean reverse;
@NotNull
private final List<Integer> columnIndexList;
public WebSQLQueryResultReference(
@Nullable WebSession session,
@NotNull DBSEntityAssociation association,
boolean reverse,
@NotNull List<Integer> columnIndexList
) {
this.session = session;
this.association = association;
this.reverse = reverse;
this.columnIndexList = columnIndexList;
}
@NotNull
@Property
public String getAssociationName() {
return association.getName();
}
@Nullable
@Property
public String getTargetEntityName() {
DBSEntity targetEntity = getTargetEntity();
if (targetEntity == null) {
return null;
}
return DBUtils.getObjectFullName(targetEntity, DBPEvaluationContext.UI);
}
@Nullable
@Property
public String getNodePath() {
if (session == null) {
return null;
}
DBSEntity targetEntity = getTargetEntity();
if (targetEntity == null) {
return null;
}
try {
DBNNode node = session.getNavigatorModelOrThrow()
.getNodeByObject(session.getProgressMonitor(), targetEntity, false);
return node == null ? null : node.getNodeUri();
} catch (DBException e) {
log.debug("Error resolving navigator node for entity " + targetEntity.getName(), e);
return null;
}
}
@NotNull
@Property
public List<Integer> getColumnIndexList() {
return columnIndexList;
}
@Nullable
private DBSEntity getTargetEntity() {
if (reverse) {
return association.getParentObject();
}
DBSEntityConstraint referencedConstraint = association.getReferencedConstraint();
if (referencedConstraint == null) {
return null;
}
return referencedConstraint.getParentObject();
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
*/
package io.cloudbeaver.service.sql;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.service.sql.WebSQLResultSetRowIdentifier.WebSQLResultSetRowIdentifierState;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
@@ -35,6 +36,10 @@ public class WebSQLQueryResultSet {
private static final Log log = Log.getLog(WebSQLQueryResultSet.class);
@Nullable
private final WebSession session;
@Nullable
private DBDAttributeBinding[] referencesBindings;
private WebSQLQueryResultColumn[] columns;
private List<WebSQLQueryResultSetRow> rows = Collections.emptyList();
private boolean hasMoreData;
@@ -50,7 +55,8 @@ public class WebSQLQueryResultSet {
private boolean readOnly;
private String readOnlyStatus;
public WebSQLQueryResultSet() {
public WebSQLQueryResultSet(@Nullable WebSession session) {
this.session = session;
}
@Property
@@ -67,12 +73,29 @@ public class WebSQLQueryResultSet {
this.columns = columns;
}
public void setColumns(DBDAttributeBinding[] bindings) {
public void setColumns(@NotNull DBDAttributeBinding[] bindings) {
WebSQLQueryResultColumn[] columns = new WebSQLQueryResultColumn[bindings.length];
for (int i = 0; i < bindings.length; i++) {
columns[i] = new WebSQLQueryResultColumn(bindings[i]);
}
this.columns = columns;
this.referencesBindings = bindings;
}
@NotNull
@Property
public List<WebSQLQueryResultReference> getAssociations() {
return session != null && referencesBindings != null
? WebSQLUtils.collectAssociations(session, referencesBindings)
: Collections.emptyList();
}
@NotNull
@Property
public List<WebSQLQueryResultReference> getReferences() {
return session != null && referencesBindings != null
? WebSQLUtils.collectReferences(session, referencesBindings)
: Collections.emptyList();
}
@Property
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import io.cloudbeaver.utils.CBModelConstants;
import io.cloudbeaver.utils.ServletAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPEvaluationContext;
import org.jkiss.dbeaver.model.data.*;
@@ -35,8 +36,7 @@ import org.jkiss.dbeaver.model.gis.DBGeometry;
import org.jkiss.dbeaver.model.gis.GisConstants;
import org.jkiss.dbeaver.model.gis.GisTransformUtils;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.struct.DBSAttributeBase;
import org.jkiss.dbeaver.model.struct.DBSTypedObject;
import org.jkiss.dbeaver.model.struct.*;
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
import org.jkiss.dbeaver.utils.ContentUtils;
import org.jkiss.dbeaver.utils.GeneralUtils;
@@ -50,6 +50,7 @@ import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
/**
* Web SQL utils.
@@ -116,8 +117,7 @@ public class WebSQLUtils {
Map<String, Object> map = createMapOfType(WebSQLConstants.VALUE_TYPE_COLLECTION);
map.put("value", items);
return map;
} else if (value instanceof DBDComposite) {
DBDComposite composite = (DBDComposite)value;
} else if (value instanceof DBDComposite composite && composite.getAttributes().length > 0) {
Map<String, Object> struct = new LinkedHashMap<>();
for (DBSAttributeBase attr : composite.getAttributes()) {
struct.put(attr.getName(), makeWebCellValue(session, attr, composite.getAttributeValue(attr), dataFormat));
@@ -329,4 +329,98 @@ public class WebSQLUtils {
webSession.removeAttribute(attributeName);
}
}
@NotNull
public static List<WebSQLQueryResultReference> collectAssociations(
@NotNull WebSession session,
@NotNull DBDAttributeBinding[] bindings
) {
return collectWebSQLQueryResultReference(session, bindings, false);
}
@NotNull
public static List<WebSQLQueryResultReference> collectReferences(
@NotNull WebSession session,
@NotNull DBDAttributeBinding[] bindings
) {
return collectWebSQLQueryResultReference(session, bindings, true);
}
@NotNull
private static List<WebSQLQueryResultReference> collectWebSQLQueryResultReference(
@NotNull WebSession session,
@NotNull DBDAttributeBinding[] bindings,
boolean reverse
) {
Map<DBSEntityAttribute, Integer> attrToIndex = new HashMap<>();
Set<DBSEntity> entities = new LinkedHashSet<>();
for (int i = 0; i < bindings.length; i++) {
DBSEntityAttribute ea = bindings[i].getEntityAttribute();
if (ea == null) {
continue;
}
attrToIndex.putIfAbsent(ea, i);
DBSEntity parent = ea.getParentObject();
entities.add(parent);
}
Function<DBSEntityAttribute, DBDAttributeBinding> attrToBinding = attr -> {
Integer idx = attrToIndex.get(attr);
return idx == null ? null : bindings[idx];
};
List<WebSQLQueryResultReference> result = new ArrayList<>();
DBRProgressMonitor monitor = session.getProgressMonitor();
for (DBSEntity entity : entities) {
try {
List<DBSEntityAssociation> source = reverse
? DBStructUtils.readReferences(monitor, entity, attrToBinding)
: DBStructUtils.readAssociations(monitor, entity, attrToBinding);
for (DBSEntityAssociation association : source) {
List<Integer> columnIndex = collectOwnColumnIndex(monitor, association, reverse, attrToIndex);
if (columnIndex != null) {
result.add(new WebSQLQueryResultReference(session, association, reverse, columnIndex));
}
}
} catch (DBException e) {
log.debug("Error collecting references for entity " + entity.getName(), e);
}
}
return result;
}
@Nullable
private static List<Integer> collectOwnColumnIndex(
@NotNull DBRProgressMonitor monitor,
@NotNull DBSEntityAssociation association,
boolean reverse,
@NotNull Map<DBSEntityAttribute, Integer> attrToIndex
) throws DBException {
DBSEntityReferrer ownSide;
if (reverse) {
DBSEntityConstraint refConstraint = association.getReferencedConstraint();
if (!(refConstraint instanceof DBSEntityReferrer referrer)) {
return null;
}
ownSide = referrer;
} else {
if (!(association instanceof DBSEntityReferrer associationRef)) {
return null;
}
ownSide = associationRef;
}
List<? extends DBSEntityAttributeRef> attrs = ownSide.getAttributeReferences(monitor);
if (attrs == null || attrs.isEmpty()) {
return null;
}
List<Integer> indexList = new ArrayList<>(attrs.size());
for (DBSEntityAttributeRef attrRef : attrs) {
Integer idx = attrToIndex.get(attrRef.getAttribute());
if (idx == null) {
return null;
}
indexList.add(idx);
}
return indexList;
}
}
@@ -232,6 +232,17 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL>
getDataFilter(env),
getDataFormat(env)
))
.dataFetcher("asyncSqlNavigateForeignKey", env ->
getService(env).asyncNavigateForeignKey(
getWebSession(env),
getSQLContext(env),
getArgumentVal(env, "resultsId"),
new WebSQLResultsRow(getArgument(env, "row")),
getArgumentVal(env, "columnIndex"),
getArgumentVal(env, "associationName"),
CommonUtils.toBoolean(env.getArgument("isReference")),
getDataFormat(env)
))
.dataFetcher("asyncSqlExecuteResults", env ->
getService(env).asyncGetQueryResults(
getWebSession(env), getArgumentVal(env, "taskId")
@@ -36,6 +36,8 @@ import org.jkiss.dbeaver.model.DBPDataSource;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBUtils;
import org.jkiss.dbeaver.model.data.DBDAttributeBinding;
import org.jkiss.dbeaver.model.data.DBDReferenceNavigation;
import org.jkiss.dbeaver.model.data.DBDReferenceUtils;
import org.jkiss.dbeaver.model.exec.DBCException;
import org.jkiss.dbeaver.model.exec.DBCLogicalOperator;
import org.jkiss.dbeaver.model.exec.DBExecUtils;
@@ -59,9 +61,7 @@ import org.jkiss.dbeaver.model.sql.registry.SQLGeneratorDescriptor;
import org.jkiss.dbeaver.model.sql.semantics.completion.SQLCompletionProposalComparator;
import org.jkiss.dbeaver.model.sql.semantics.completion.SQLQueryCompletionAnalyzer;
import org.jkiss.dbeaver.model.sql.semantics.completion.SQLQueryCompletionContext;
import org.jkiss.dbeaver.model.struct.DBSDataContainer;
import org.jkiss.dbeaver.model.struct.DBSObject;
import org.jkiss.dbeaver.model.struct.DBSWrapper;
import org.jkiss.dbeaver.model.struct.*;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.dbeaver.utils.RuntimeUtils;
import org.jkiss.utils.CommonUtils;
@@ -603,6 +603,74 @@ public class WebServiceSQL implements DBWServiceSQL {
return contextInfo.getProcessor().getWebSession().createAndRunAsyncTask("Read data from container " + nodePath, runnable);
}
@Override
public WebAsyncTaskInfo asyncNavigateForeignKey(
@NotNull WebSession webSession,
@NotNull WebSQLContextInfo contextInfo,
@NotNull String resultsId,
@NotNull WebSQLResultsRow row,
int columnIndex,
@NotNull String associationName,
boolean isReference,
@Nullable WebDataFormat dataFormat
) {
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
@Override
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
try {
monitor.beginTask("Navigate foreign key", 1);
WebSQLResultsInfo resultsInfo = contextInfo.getResults(resultsId);
DBSEntity sourceEntity = resolveSourceEntity(resultsInfo.getAttributes(), columnIndex);
DBSEntityAssociation association = isReference
? DBStructUtils.findReverseAssociationByName(monitor, sourceEntity, associationName)
: DBStructUtils.findForwardAssociationByName(monitor, sourceEntity, associationName);
WebDBDResultSetDataProvider dataProvider = new WebDBDResultSetDataProvider(
resultsId,
contextInfo,
List.of(row)
);
DBDReferenceNavigation navigation = isReference
? DBDReferenceUtils.resolveReferenceNavigation(monitor, dataProvider, association, dataProvider.getSelectedRows())
: DBDReferenceUtils.resolveAssociationNavigation(monitor, dataProvider, association, dataProvider.getSelectedRows());
if (!(navigation.getTargetEntity() instanceof DBSDataContainer targetDataContainer)) {
throw new DBWebException("Referenced entity '" + navigation.getTargetEntity().getName() + "' is not a data container");
}
WebSQLExecuteInfo executeResults = contextInfo.getProcessor().readDataFromContainer(
contextInfo,
monitor,
targetDataContainer,
null,
WebSQLDataFilter.from(navigation.getTargetFilter()),
dataFormat
);
this.result = executeResults.getStatusMessage();
this.extendedResults = executeResults;
} catch (Throwable e) {
throw new InvocationTargetException(e);
} finally {
monitor.done();
}
}
};
return webSession.createAndRunAsyncTask("Navigate foreign key from results " + resultsId, runnable);
}
@NotNull
private DBSEntity resolveSourceEntity(
@NotNull DBDAttributeBinding[] attributes,
int columnIndex
) throws DBException {
if (columnIndex < 0 || columnIndex >= attributes.length) {
throw new DBWebException("Column index '" + columnIndex + "' is out of range");
}
DBSEntityAttribute entityAttribute = attributes[columnIndex].getEntityAttribute();
if (entityAttribute == null) {
throw new DBException("Column [" + attributes[columnIndex].getName() + "] is not bound to any entity");
}
return entityAttribute.getParentObject();
}
@NotNull
@Override
public List<DBCTraceProperty> readDynamicTrace(
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Administration
Bundle-SymbolicName: io.cloudbeaver.service.admin;singleton:=true
Bundle-Version: 1.0.146.qualifier
Bundle-Release-Date: 20260601
Bundle-Version: 1.0.148.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.admin</artifactId>
<version>1.0.146-SNAPSHOT</version>
<version>1.0.148-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -88,6 +88,8 @@ type AdminUserInfo {
disabledBy: String @since(version: "25.0.2")
"Reason why the user was disabled"
disableReason: String @since(version: "25.0.2")
"Date of the user's last successful login"
lastLoginTime: DateTime @since(version: "26.1.1")
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,9 +18,9 @@ package io.cloudbeaver.service.admin;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.security.SMDataSourceGrant;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.security.SMTeamMemberInfo;
import org.jkiss.dbeaver.model.security.user.SMTeam;
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,9 +26,9 @@ import io.cloudbeaver.utils.CBModelConstants;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.security.SMDataSourceGrant;
import org.jkiss.dbeaver.model.security.SMObjectType;
import java.util.ArrayList;
import java.util.List;
@@ -142,4 +142,9 @@ public class AdminUserInfo {
public String getDisableReason() {
return user.getDisableReason();
}
@Nullable
public String getLastLoginTime() {
return user.getLastLoginTime() == null ? null : CBModelConstants.ISO_DATE_FORMAT.format(user.getLastLoginTime());
}
}
@@ -42,13 +42,17 @@ import org.jkiss.dbeaver.model.DBConstants;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.auth.AuthInfo;
import org.jkiss.dbeaver.model.auth.SMAuthConfiguration;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
import org.jkiss.dbeaver.model.rm.RMProjectType;
import org.jkiss.dbeaver.model.secret.DBSSecretController;
import org.jkiss.dbeaver.model.security.*;
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
import org.jkiss.dbeaver.model.security.SMConstants;
import org.jkiss.dbeaver.model.security.SMDataSourceGrant;
import org.jkiss.dbeaver.model.security.SMSubjectType;
import org.jkiss.dbeaver.model.security.user.SMTeam;
import org.jkiss.dbeaver.model.security.user.SMUser;
import org.jkiss.dbeaver.utils.GeneralUtils;
@@ -642,7 +646,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
adminName = curUser == null ? null : curUser.getUserId();
adminPassword = null;
}
List<AuthInfo> authInfos = new ArrayList<>();
List<SMAuthConfiguration> authInfos = new ArrayList<>();
List<WebAuthInfo> authInfoList = webSession.getAllAuthInfo();
if (CommonUtils.isEmpty(adminName)) {
// Try to get admin name from existing authentications (first one)
@@ -654,7 +658,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
adminName = CBConstants.DEFAULT_ADMIN_NAME;
}
for (WebAuthInfo webAuthInfo : authInfoList) {
authInfos.add(new AuthInfo(
authInfos.add(new SMAuthConfiguration(
webAuthInfo.getAuthProviderDescriptor().getId(),
webAuthInfo.getUserCredentials()));
}
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Authentication
Bundle-SymbolicName: io.cloudbeaver.service.auth;singleton:=true
Bundle-Version: 1.0.146.qualifier
Bundle-Release-Date: 20260601
Bundle-Version: 1.0.148.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.auth</artifactId>
<version>1.0.146-SNAPSHOT</version>
<version>1.0.148-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Data Transfer
Bundle-SymbolicName: io.cloudbeaver.service.data.transfer;singleton:=true
Bundle-Version: 1.0.147.qualifier
Bundle-Release-Date: 20260601
Bundle-Version: 1.0.149.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.data.transfer</artifactId>
<version>1.0.147-SNAPSHOT</version>
<version>1.0.149-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,18 +42,23 @@ import java.util.List;
*/
public interface DBWServiceDataTransfer extends DBWService {
@NotNull
@WebAction
List<WebDataTransferStreamProcessor> getAvailableStreamProcessors(WebSession session) throws DBWebException;
List<WebDataTransferStreamProcessor> getAvailableStreamProcessors(@NotNull WebSession session) throws DBWebException;
@NotNull
@WebAction
List<WebDataTransferStreamProcessor> getAvailableImportStreamProcessors(WebSession session) throws DBWebException;
List<WebDataTransferStreamProcessor> getAvailableImportStreamProcessors(@NotNull WebSession session) throws DBWebException;
@WebAction
@NotNull
@WebAction(requireGlobalPermissions = RMConstants.PERMISSION_DATA_EDITOR_EXPORT)
WebAsyncTaskInfo dataTransferExportDataFromContainer(
WebSQLProcessor sqlProcessor,
String containerNodePath,
WebDataTransferParameters parameters) throws DBWebException;
@NotNull WebSQLProcessor sqlProcessor,
@NotNull String containerNodePath,
@NotNull WebDataTransferParameters parameters
) throws DBWebException;
@NotNull
@WebAction
WebAsyncTaskInfo asyncImportDataContainer(
@NotNull String processorId,
@@ -61,20 +66,24 @@ public interface DBWServiceDataTransfer extends DBWService {
@NotNull WebSQLResultsInfo webSQLResultsInfo,
@NotNull WebSession webSession) throws DBWebException;
@NotNull
@WebAction(requireGlobalPermissions = RMConstants.PERMISSION_DATA_EDITOR_EXPORT)
WebAsyncTaskInfo dataTransferExportDataFromResults(
WebSQLContextInfo sqlContextInfo,
String resultsId,
WebDataTransferParameters parameters) throws DBWebException;
@NotNull WebSQLContextInfo sqlContextInfo,
@NotNull String resultsId,
@NotNull WebDataTransferParameters parameters
) throws DBWebException;
/**
* It's deprecated because now we use streaming file to response directly, and we don't need to clean up any files
* after data transfer.
*/
@NotNull
@WebAction
@Deprecated
Boolean dataTransferRemoveDataFile(WebSession session, String dataFileId) throws DBWebException;
Boolean dataTransferRemoveDataFile(@NotNull WebSession session, @NotNull String dataFileId) throws DBWebException;
@NotNull
WebDataTransferDefaultExportSettings defaultExportSettings();
/**
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import io.cloudbeaver.model.WebAsyncTaskInfo;
import io.cloudbeaver.model.WebConnectionInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.BaseWebPlatform;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.WebAppUtils;
import io.cloudbeaver.server.WebApplication;
import io.cloudbeaver.service.WebServiceServletBase;
@@ -36,6 +37,7 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.runtime.DBWorkbench;
@@ -77,7 +79,7 @@ public class WebDataTransferImportServlet extends WebServiceServletBase {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Permission denied");
return;
}
if (!session.hasGlobalPermission(DBWConstants.GLOBAL_PERMISSION_DATA_EDITOR_IMPORT)) {
if (validateImportPermission(session)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Import is not allowed for this user");
return;
}
@@ -134,4 +136,12 @@ public class WebDataTransferImportServlet extends WebServiceServletBase {
}
}
}
private boolean validateImportPermission(@NotNull WebSession session) {
if (WebAppUtils.getWebApplication().isCommunity()) {
Map<String, Object> productSettings = WebAppUtils.getWebApplication().getServerConfiguration().getProductSettings();
return JSONUtils.getBoolean(productSettings, CBConstants.PREF_DATA_EDITOR_IMPORT_DISABLED_OLD, false);
}
return !session.hasGlobalPermission(DBWConstants.GLOBAL_PERMISSION_DATA_EDITOR_IMPORT);
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,11 +16,14 @@
*/
package io.cloudbeaver.service.data.transfer.impl;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.WebAsyncTaskInfo;
import io.cloudbeaver.model.session.WebAsyncTaskProcessor;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.CBPlatform;
import io.cloudbeaver.server.WebAppUtils;
import io.cloudbeaver.service.data.transfer.DBWServiceDataTransfer;
import io.cloudbeaver.service.sql.WebSQLContextInfo;
import io.cloudbeaver.service.sql.WebSQLProcessor;
@@ -73,8 +76,9 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
}
}
@NotNull
@Override
public List<WebDataTransferStreamProcessor> getAvailableStreamProcessors(WebSession session) {
public List<WebDataTransferStreamProcessor> getAvailableStreamProcessors(@NotNull WebSession session) {
List<DataTransferProcessorDescriptor> processors = DataTransferRegistry.getInstance()
.getAvailableProcessors(StreamTransferConsumer.class, DBSEntity.class);
if (CommonUtils.isEmpty(processors)) {
@@ -84,8 +88,9 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
return processors.stream().map(x -> new WebDataTransferStreamProcessor(session, x)).collect(Collectors.toList());
}
@NotNull
@Override
public List<WebDataTransferStreamProcessor> getAvailableImportStreamProcessors(WebSession session) {
public List<WebDataTransferStreamProcessor> getAvailableImportStreamProcessors(@NotNull WebSession session) {
List<DataTransferProcessorDescriptor> processors =
DataTransferRegistry.getInstance().getAvailableProcessors(StreamTransferProducer.class, DBSEntity.class);
if (CommonUtils.isEmpty(processors)) {
@@ -95,12 +100,16 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
return processors.stream().map(x -> new WebDataTransferStreamProcessor(session, x)).collect(Collectors.toList());
}
@NotNull
@Override
public WebAsyncTaskInfo dataTransferExportDataFromContainer(
WebSQLProcessor sqlProcessor,
String containerNodePath,
WebDataTransferParameters parameters) throws DBWebException {
@NotNull WebSQLProcessor sqlProcessor,
@NotNull String containerNodePath,
@NotNull WebDataTransferParameters parameters
) throws DBWebException {
if (!validateExportPermission(sqlProcessor.getWebSession())) {
throw new DBWebException("Data export was disabled by administrator");
}
DBSDataContainer dataContainer;
try {
dataContainer = sqlProcessor.getDataContainerByNodePath(sqlProcessor.getWebSession().getProgressMonitor(), containerNodePath, DBSDataContainer.class);
@@ -111,11 +120,23 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
return asyncExportFromDataContainer(sqlProcessor, parameters, dataContainer, null);
}
private boolean validateExportPermission(@NotNull WebSession webSession) {
if (!WebAppUtils.getWebApplication().isCommunity()) {
// global permission is already checked
return true;
}
Map<String, Object> productSettings = WebAppUtils.getWebApplication().getServerConfiguration().getProductSettings();
// we need to check in product settings
// product settings parameter saves only disabled state that's why we invert result
return webSession.hasPermission(DBWConstants.PERMISSION_ADMIN) ||
!CommonUtils.getOption(productSettings, CBConstants.PREF_DATA_EDITOR_EXPORT_DISABLED_OLD, false);
}
@NotNull
private String makeUniqueFileName(
WebSQLProcessor sqlProcessor,
DataTransferProcessorDescriptor processor,
Map<String, Object> processorProperties
@NotNull WebSQLProcessor sqlProcessor,
@NotNull DataTransferProcessorDescriptor processor,
@Nullable Map<String, Object> processorProperties
) {
if (processorProperties != null && processorProperties.get(StreamConsumerSettings.PROP_FILE_EXTENSION) != null) {
return sqlProcessor.getWebSession().getSessionId() + "_" + UUID.randomUUID() +
@@ -124,25 +145,31 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
return sqlProcessor.getWebSession().getSessionId() + "_" + UUID.randomUUID() + "." + WebDataTransferUtils.getProcessorFileExtension(processor);
}
@NotNull
@Override
public WebAsyncTaskInfo dataTransferExportDataFromResults(
WebSQLContextInfo sqlContext,
String resultsId,
WebDataTransferParameters parameters) throws DBWebException {
@NotNull WebSQLContextInfo sqlContext,
@NotNull String resultsId,
@NotNull WebDataTransferParameters parameters
) throws DBWebException {
if (!validateExportPermission(sqlContext.getProcessor().getWebSession())) {
throw new DBWebException("Data export was disabled by administrator");
}
WebSQLResultsInfo results = sqlContext.getResults(resultsId);
return asyncExportFromDataContainer(sqlContext.getProcessor(), parameters, results.getDataContainer(), results);
}
@NotNull
@Override
public WebDataTransferDefaultExportSettings defaultExportSettings() {
return new WebDataTransferDefaultExportSettings();
}
@NotNull
@Override
@Deprecated
public Boolean dataTransferRemoveDataFile(WebSession webSession, String dataFileId) throws DBWebException {
public Boolean dataTransferRemoveDataFile(@NotNull WebSession webSession, @NotNull String dataFileId) throws DBWebException {
//deprecated
return true;
}
@@ -158,7 +185,9 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
DBSDataContainer dataContainer = taskConfig.getDataContainer();
WebSQLResultsInfo resultsInfo = taskConfig.getResultsInfo();
DataTransferProcessorDescriptor processor = DataTransferRegistry.getInstance().getProcessor(parameters.getProcessorId());
if (processor == null) {
throw new DBException("Wrong data processor '" + parameters.getProcessorId() + "'");
}
try {
exportData(monitor, processor, dataContainer, parameters, resultsInfo, outputStream);
} catch (Exception e) {
@@ -166,16 +195,20 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
}
}
@NotNull
private WebAsyncTaskInfo asyncExportFromDataContainer(
@NotNull WebSQLProcessor sqlProcessor,
@NotNull WebDataTransferParameters parameters,
@NotNull DBSDataContainer dataContainer,
@Nullable WebSQLResultsInfo resultsInfo
) {
) throws DBWebException {
sqlProcessor.getWebSession().addInfoMessage("Export data");
log.info(String.format("Data export started: [userId=%s]", sqlProcessor.getWebSession().getUserId()));
DataTransferProcessorDescriptor processor = DataTransferRegistry.getInstance().getProcessor(parameters.getProcessorId());
if (processor == null) {
throw new DBWebException("Wrong data processor '" + parameters.getProcessorId() + "'");
}
String uniqueFileName = makeUniqueFileName(sqlProcessor, processor, parameters.getProcessorProperties());
var outputSettings = parameters.getOutputSettings();
String fileNameKey = WebDataTransferUtils.normalizeFileName(uniqueFileName, outputSettings);
@@ -199,18 +232,24 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
);
}
public WebAsyncTaskInfo asyncImportDataContainer(@NotNull String processorId,
@NotNull Path path,
@NotNull WebSQLResultsInfo sqlContext,
@NotNull WebSession webSession) throws DBWebException {
@NotNull
public WebAsyncTaskInfo asyncImportDataContainer(
@NotNull String processorId,
@NotNull Path path,
@NotNull WebSQLResultsInfo sqlContext,
@NotNull WebSession webSession
) throws DBWebException {
webSession.addInfoMessage("Import data");
DataTransferProcessorDescriptor processor = DataTransferRegistry.getInstance().getProcessor(processorId);
if (processor == null) {
throw new DBWebException("Wrong data processor '" + processorId + "'");
}
log.info(String.format("Data import started: [userId=%s]", webSession.getUserId()));
DBSDataContainer dataContainer = sqlContext.getDataContainer();
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
@Override
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
public void run(@NotNull DBRProgressMonitor monitor) throws InvocationTargetException {
monitor.beginTask("Import data", 1);
try {
monitor.subTask("Import data using " + processor.getName());
@@ -238,13 +277,13 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
}
private void exportData(
DBRProgressMonitor monitor,
DataTransferProcessorDescriptor processor,
DBSDataContainer dataContainer,
WebDataTransferParameters parameters,
WebSQLResultsInfo resultsInfo,
OutputStream outputStream
) throws DBException, IOException {
@NotNull DBRProgressMonitor monitor,
@NotNull DataTransferProcessorDescriptor processor,
@NotNull DBSDataContainer dataContainer,
@NotNull WebDataTransferParameters parameters,
@NotNull WebSQLResultsInfo resultsInfo,
@NotNull OutputStream outputStream
) throws DBException {
IDataTransferProcessor processorInstance = processor.getInstance();
if (!(processorInstance instanceof IStreamDataExporter exporter)) {
throw new DBException("Invalid processor. " + IStreamDataExporter.class.getSimpleName() + " expected");
@@ -300,10 +339,11 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
}
private void importData(
DBRProgressMonitor monitor,
DataTransferProcessorDescriptor processor,
@NotNull DBSDataManipulator dataContainer,
Path path) throws DBException {
@NotNull DBRProgressMonitor monitor,
@NotNull DataTransferProcessorDescriptor processor,
@NotNull DBSDataManipulator dataContainer,
@NotNull Path path
) throws DBException {
IDataTransferProcessor processorInstance = processor.getInstance();
StreamTransferProducer producer;
@@ -323,10 +363,11 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer {
}
producerSettings.setProcessorProperties(properties);
producerSettings.updateProducerSettingsFromStream(
monitor,
producer,
processorInstance,
properties);
monitor,
producer,
processorInstance,
properties
);
DatabaseMappingContainer databaseMappingContainer =
new DatabaseMappingContainer(monitor, databaseConsumerSettings, producer.getDatabaseObject(), consumer.getTargetObject());
databaseMappingContainer.getAttributeMappings(monitor);
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - File System
Bundle-SymbolicName: io.cloudbeaver.service.fs;singleton:=true
Bundle-Version: 1.0.64.qualifier
Bundle-Release-Date: 20260601
Bundle-Version: 1.0.66.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.fs</artifactId>
<version>1.0.64-SNAPSHOT</version>
<version>1.0.66-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,8 +25,6 @@ import io.cloudbeaver.service.fs.model.FSFile;
import io.cloudbeaver.service.fs.model.FSFileSystem;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.navigator.DBNProject;
import org.jkiss.dbeaver.model.navigator.fs.DBNFileSystem;
import org.jkiss.dbeaver.model.navigator.fs.DBNFileSystems;
import org.jkiss.dbeaver.model.navigator.fs.DBNPathBase;
@@ -51,18 +49,12 @@ public class WebServiceFS implements DBWServiceFS {
@NotNull
@Override
public FSFileSystem[] getAvailableFileSystems(@NotNull WebSession webSession, @NotNull String projectId)
throws DBWebException {
public FSFileSystem[] getAvailableFileSystems(@NotNull WebSession webSession, @NotNull String projectId) throws DBWebException {
try {
DBPProject project = webSession.getProjectById(projectId);
if (project == null) {
throw new DBException(MessageFormat.format("Project ''{0}'' is not found in session", projectId));
DBNFileSystems dbnFileSystems = webSession.getNavigatorModelOrThrow().getRoot().getExtraNode(DBNFileSystems.class);
if (dbnFileSystems == null) {
throw new DBWebException("File systems not found in navigator");
}
DBNProject projectNode = webSession.getNavigatorModelOrThrow().getRoot().getProjectNode(project);
if (projectNode == null) {
throw new DBException(MessageFormat.format("Project ''{0}'' is not found in navigator model", projectId));
}
DBNFileSystems dbnFileSystems = projectNode.getExtraNode(DBNFileSystems.class);
var fsRegistry = FileSystemProviderRegistry.getInstance();
DBNFileSystem[] children = dbnFileSystems.getChildren(webSession.getProgressMonitor());
if (children == null) {
@@ -55,6 +55,6 @@ public class FSFile {
@Property
//TODO: node URI after finish migration to the new node path format
public String getNodePath() {
return node.getNodeItemPath();
return node.getNodeUri();
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
import org.eclipse.jetty.ee11.servlet.ServletContextRequest;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
@@ -41,6 +42,7 @@ import org.jkiss.utils.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.Map;
@@ -102,9 +104,10 @@ public class WebFSServlet extends WebServiceServletBase {
if (CommonUtils.isEmpty(fileName)) {
continue;
}
Path safeTarget = resolveSafeChild(path, fileName);
try (InputStream is = part.getInputStream()) {
Files.copy(is, path.resolve(fileName));
node.addChildResource(path.resolve(fileName));
Files.copy(is, safeTarget);
node.addChildResource(safeTarget);
}
}
} catch (Exception e) {
@@ -113,6 +116,26 @@ public class WebFSServlet extends WebServiceServletBase {
}
}
@NotNull
private Path resolveSafeChild(@NotNull Path parent, @NotNull String submittedFileName) throws DBException {
Path candidate;
try {
candidate = Path.of(submittedFileName);
} catch (InvalidPathException e) {
throw new DBException("Invalid file name");
}
Path baseName = candidate.getFileName();
if (baseName == null || baseName.toString().isBlank()) {
throw new DBException("Invalid file name");
}
Path normalizedParent = parent.normalize();
Path resolved = normalizedParent.resolve(baseName.toString()).normalize();
if (!resolved.startsWith(normalizedParent) || resolved.equals(normalizedParent)) {
throw new DBException("Invalid file name");
}
return resolved;
}
@Override
protected Map<String, Object> getVariables(HttpServletRequest request) {
Map<String, Object> variables = super.getVariables(request);
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Name: Cloudbeaver Web Service - LDAP
Bundle-Vendor: DBeaver Corp
Bundle-SymbolicName: io.cloudbeaver.service.ldap.auth;singleton:=true
Bundle-Version: 1.0.29.qualifier
Bundle-Release-Date: 20260601
Bundle-Version: 1.0.31.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.model;visibility:=reexport,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.ldap.auth</artifactId>
<version>1.0.29-SNAPSHOT</version>
<version>1.0.31-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Metadata
Bundle-SymbolicName: io.cloudbeaver.service.metadata;singleton:=true
Bundle-Version: 1.0.150.qualifier
Bundle-Release-Date: 20260601
Bundle-Version: 1.0.152.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.metadata</artifactId>
<version>1.0.150-SNAPSHOT</version>
<version>1.0.152-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Resource manager NIO implementation
Bundle-SymbolicName: io.cloudbeaver.service.rm.nio;singleton:=true
Bundle-Version: 1.0.64.qualifier
Bundle-Release-Date: 20260601
Bundle-Version: 1.0.66.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.model,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.rm.nio</artifactId>
<version>1.0.64-SNAPSHOT</version>
<version>1.0.66-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,8 @@ package io.cloudbeaver.service.rm.fs;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.fs.AbstractFileSystemProvider;
import org.jkiss.dbeaver.model.fs.DBFFileSystemContainer;
import org.jkiss.dbeaver.model.fs.DBFVirtualFileSystem;
import org.jkiss.dbeaver.model.rm.RMControllerProvider;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
@@ -27,15 +27,16 @@ import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
public class RMVirtualFileSystemProvider extends AbstractFileSystemProvider {
private static final Log log = Log.getLog(RMVirtualFileSystemProvider.class);
@NotNull
@Override
public DBFVirtualFileSystem[] getAvailableFileSystems(
@NotNull DBRProgressMonitor monitor,
@NotNull DBPProject project
@NotNull DBFFileSystemContainer fsContainer
) {
if (!(project instanceof RMControllerProvider)) {
if (!(fsContainer instanceof RMControllerProvider)) {
return new DBFVirtualFileSystem[0];
}
RMControllerProvider rmControllerProvider = (RMControllerProvider) project;
RMControllerProvider rmControllerProvider = (RMControllerProvider) fsContainer;
return new DBFVirtualFileSystem[]{new RMVirtualFileSystem(rmControllerProvider.getResourceController(),
rmControllerProvider.getRMProject())};
}

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