Compare commits

...
Author SHA1 Message Date
ElizabethandMashaKorax 5ac2d73935 dbeaver/pro#10453 Update Clickhouse driver (#4610)
Co-authored-by: MashaKorax <84867187+MashaKorax@users.noreply.github.com>
2026-09-02 11:31:19 +02:00
Ruslan Musaev dcbf3f4d6d dbeaver/pro#6861 add auth method for provisioning (#4603)
* dbeaver/pro#6861 add auth method for provisioning
2026-09-01 17:01:13 +02:00
861c2eeb3d dbeaver/pro#10012 redesign connection form (#4611)
* dbeaver/pro#10012 redesign connection form

* dbeaver/pro#10012 fixes build

* dbeaver/pro#10012 icons moved to packages respectively, moves readonly logic to helper, fixes broken layout with Groups component

---------

Co-authored-by: sergeyteleshev <sergei.teleshev@dbeaver.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-09-01 16:38:49 +02:00
sergeyteleshevandsergeyteleshev ad3b95f2a7 dbeaver/pro#10480 adds prepare to form flow + refactors format implementations (#4618)
* dbeaver/pro#10480 adds prepare to form flow + refactors format implementations

* dbeaver/pro#10480 fixes tests mocks to fix the EE build

* dbeaver/pro#10480 fixes tests to fix the EE build 2

* dbeaver/pro#10480 removes code duplications

---------

Co-authored-by: sergeyteleshev <sergei.teleshev@dbeaver.com>
2026-09-01 15:54:46 +02:00
sergeyteleshevandsergeyteleshev 82775f9dc2 dbeaver/pro#9917 adds slider dialog for creating of users and teams forms (#4616)
* dbeaver/pro#9917 adds slider dialog for creating of users and teams forms

* dbeaver/pro#9917 cleanup

* dbeaver/pro#9917 cleanup 2

---------

Co-authored-by: sergeyteleshev <sergei.teleshev@dbeaver.com>
2026-09-01 15:52:52 +02:00
mayer f95bb8c041 dbeaver/dbeaver-devops#2781 Add required build gate (#4619) 2026-09-01 14:02:05 +02:00
Serge RiderandAnton Radchenko 134a77e5bd dbeaver/pro#10477 Optimize bundle dependencies (#4613)
Co-authored-by: Anton Radchenko <34737043+RadAnton@users.noreply.github.com>
2026-08-31 19:27:37 +02:00
Daria Marutkina 2f04cc5bb0 dbeaver/pro#10399 Update README file (#4615)
* dbeaver/pro#10399 Update README file

* Update README with changes for version 26.1.0

This commit updates the README to reflect the latest changes, including new AI features, improvements in administration, authorization, data editor, data transfer, accessibility, general updates, database support, and security fixes.

* Update changelog for version 26.2.0
2026-08-31 12:40:34 +02:00
Serge Rider d1ab50783e dbeaver/pro#10402 Switch to NIO (#4599)
* dbeaver/pro#10402 Switch to NIO

* dbeaver/pro#10402 Switch to NIO
2026-08-27 20:53:33 +02:00
alex a12ab4f2a2 Revert "dbeaver/pro#10137 fixes max height for ariakit groups (#4545)" (#4607)
This reverts commit bc2c204a61.
2026-08-26 16:32:28 +02:00
Ainur b71010958c dbeaver/pro#10423 fix saving disabled features (#4606) 2026-08-26 14:57:36 +02:00
dbeaver-devops ac2cb461f3 dbeaver/pro#10399 26.2.1 version bump 2026-08-24 21:11:02 +02:00
alexandmr-anton-t 0a1616795d dbeaver/pro#10391 bump tar version (#4594)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-08-24 18:30:26 +02:00
571882eab0 dbeaver/pro#8886 fixes search panel chevron color and icon buttons background color (#4585)
* dbeaver/pro#8886 fixes search panel chevron color and icon buttons background color

* dbeaver/pro#8886 moves color to search panel

* dbeaver/pro#8886 moves color to search panel for button bg

* dbeaver/pro#8886 lint fix

* dbeaver/pro#8886 lint fix 2

* dbeaver/pro#8886 adjust hover style

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: naumov <iamemptyhuh@gmail.com>
2026-08-24 17:32:54 +02:00
Ainurandkseniaguzeeva 5b8c88c36a dbeaver/pro#8982 Skip ineffective connection notifications (#4581)
* dbeaver/pro#8982 Skip ineffective connection notifications

* dbeaver/pro#8982 Test navigator settings reset

* dbeaver/pro#8982 Fix test stub implementation

---------

Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-08-24 16:57:16 +02:00
Georgiiandmariiachugunova 1906f9d165 dbeaver/dbeaver#41638 arrow launch fix (#4583)
Signed-off-by: Georgii Gvinepadze <fireshowel@gmail.com>
Co-authored-by: mariiachugunova <mariia.chugunova@dbeaver.com>
2026-08-24 16:23:24 +02:00
alexandmr-anton-t 5e36ef4e27 dbeaver/pro#10373 fix connection defaults overwritten by initial state (#4592)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-08-24 12:03:39 +02:00
7cd9ae83bc dbeaver/pro#9260 adds authentication model label to auth selector (#4584)
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-08-24 11:55:37 +02:00
bcf4a2b581 dbeaver/pro#10010 fix returning node path for connections (#4576)
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-08-24 10:37:26 +02:00
Ruslan Musaev 50f63caa4f dbeaver/pro#10376 fix lost data in error (#4591) 2026-08-24 08:59:10 +02:00
sergeyteleshev a68a29aeb0 dbeaver/pro#10034 excludes network handler creds if profile is applied for this handler (#4582)
* dbeaver/pro#10034 excludes network handler creds if profile is applied for this handler

* dbeaver/pro#10034 introduces external network providers service
2026-08-23 19:09:03 +02:00
alex f5c31fbc07 dbeaver/pro#9236 show more readable notification (#4590) 2026-08-21 18:09:18 +02:00
aae05e4769 dbeaver/pro#10317 hide save checkbox (#4570)
* dbeaver/pro#10317 hide save checkbox

* dbeaver/pro#10317 hide empty handlers from auth dialog

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
2026-08-21 12:14:45 +02:00
39777dc5fa dbeaver/pro#10314 load AI models dynamically (#4577)
* dbeaver/pro#10314 load AI models dynamically

* dbeaver/pro#10314 simplify AI profile copying

* dbeaver/pro#10314 apply AI model defaults

* dbeaver/pro#10314 preserve AI model list order

* dbeaver/pro#10314 pr fixes

* dbeaver/pro#10314 cleanup

* dbeaver/pro#10314 ui fixes

* dbeaver/pro#10314 adds models part

* dbeaver/pro#10314 reverts models part + uses useExecutor for sync

* dbeaver/pro#10314 fixes width of combobox with model

* dbeaver/pro#10314 not allows to load models if switched engines

* dbeaver/pro#10314 fixes bugs with model

* dbeaver/pro#10314 removes useCallbacks redundant calls

* dbeaver/pro#10314 allows to refresh models if model is selected

* dbeaver/pro#10314 add alphabetical sort to models

* dbeaver/pro#10314 fixes sorting so it applies first model on refresh right

---------

Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-08-21 11:49:05 +02:00
4475513341 dbeaver/pro#10243 add reconcile project list on update (#4571)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
2026-08-21 11:39:35 +02:00
f4b55add99 dbeaver/pro#9515 do not duplicate driver props fix (#4560)
* dbeaver/pro#9515 do not duplicate driver props fix

* dbeaver/pro#9515 pass default values

* dbeaver/pro#9515 fix using mutable list

* dbeaver/pro#9515 add comment

---------

Co-authored-by: Ainur <ainur.iagudin@dbeaver.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
2026-08-20 17:29:58 +02:00
6d13275021 dbeaver/pro#10234 allow to grant/revoke yourself (admin) to the team (#4551)
* dbeaver/pro#10234 allow to grant/revoke yourself (admin) to the team

* dbeaver/pro#10234 allow admins edit their own teams

* dbeaver/pro#10234 adds team management logic for admin role

* dbeaver/pro#10234 cleanup

* dbeaver/pro#10234 uses EAdminPermission.admin

* dbeaver/pro#10234 fixes locales

* dbeaver/pro#10234 allow admins to manage their own permissions

---------

Co-authored-by: Ruslan <pupkinvasya7000@gmail.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-20 16:05:43 +02:00
2650b4dccd dbeaver/pro#6505 adds min value for object property info (#4565)
* dbeaver/pro#6505 adds min value for object property info

* dbeaver/pro#6605 implements constraints interface

* dbeaver/pro#6505 uses only min/max property constraints

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
2026-08-20 13:01:06 +02:00
AinurandDaria Marutkina 50f6835caf dbeaver/pro#10315 Persist deleted AI profiles (#4580)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-20 12:09:53 +02:00
3e9e71ca6d dbeaver/pro#10177 pass defaults to main properties (#4538)
* dbeaver/pro#10177 pass defaults to main properties

* dbeaver/pro#10177 postpone state init

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-08-20 12:08:16 +02:00
alexandDaria Marutkina d48cd7a0a7 dbeaver/pro#10209 add engine column width (#4579)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-20 10:42:55 +02:00
alexandEvgenia f37f173c49 dbeaver/pro#10257 do not set height if there is no value (#4575)
Let css min height handle it

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-19 16:23:00 +02:00
alexandAinur 63b25d7cb1 dbeaver/pro#4546 added agents.md (#4561)
* dbeaver/pro#4546 added agents.md

* dbeaver/pro#4546 backend instructions

---------

Co-authored-by: Ainur <ainur.iagudin@dbeaver.com>
2026-08-19 13:31:34 +02:00
Sychev AndreyandDaria Marutkina 67df14033c dbeaver/pro#9948 fix: show unsaved changes dialog on tab change (#4544)
* dbeaver/pro#9948 fix: show unsaved changes dialog on tab change

fixes bug with tabs fire executors twice

* dbeaver/pro#9948 refactor: move logic to tab service

* dbeaver/pro#9948 feat: add unsaved changes dialog for password change tab

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-19 12:29:30 +02:00
sergeyteleshevandDaria Marutkina 75a64c1c2d dbeaver/pro#6869 fixes shortcut for the sql editor show output (#4569)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-19 12:29:07 +02:00
sergeyteleshevandDaria Marutkina 28fb7f2264 dbeaver/pro#9785 fixes layout for driver selector (#4573)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-19 12:28:27 +02:00
alexandEvgenia 5176519fdd dbeaver/pro#8428 make error focusable (#4572)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-19 12:15:57 +02:00
alexandEvgenia 4d197c9548 dbeaver/pro#6028 truncate titles (#4574)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-19 11:59:38 +02:00
alexandEvgenia 56daa059a7 dbeaver/pro#4907 increase height (#4567)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-19 11:59:20 +02:00
3491fb42d7 dbeaver/pro#8422 feat: add keyboard navigation (#4534)
* dbeaver/pro#8422 feat: reexport useCompositeContext from ui-kit

* dbeaver/pro#8422 feat: extend Table interfaces to let ariakit control tabindex

* dbeaver/pro#8422 feat: add keyboard navigation in execution plan

* dbeaver/pro#8422 fix: split button visibility

* dbeaver/pro#8422 refactor: rename kbd navigation hook

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com>
2026-08-18 23:02:08 +02:00
Ainurandkseniaguzeeva bb9872dc2e dbeaver/pro#10055 events for ai toolboxes (#4552)
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-08-18 23:00:53 +02:00
sergeyteleshev 043d55c395 dbeaver/pro#10292 fixes context menu error (#4568) 2026-08-18 17:43:38 +02:00
b5c6cbfe82 dbeaver/pro#9888 fix sql filtering (#4555)
* dbeaver/pro#9888 fix sql filtering

* dbeaver/pro#9888 fix sql filtering

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
2026-08-18 16:23:54 +02:00
Ruslan MusaevandDaria Marutkina 152ab83006 dbeaver/pro#10157 fix readable error for cloudbeaver (#4548)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-18 16:09:05 +02:00
AinurandDaria Marutkina bea2451776 dbeaver/pro#10253 bump lz4 and httpcore5 for clickhouse (#4556)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-18 16:08:23 +02:00
2d0b7d428c dbeaver/pro#9994 fixes broken AI settings to fix the AI form flow (#4540)
* dbeaver/pro#9994 fixes broken AI settings to fix the AI form flow

* dbeaver/pro#9994 reverts ai settings changes

* dbeaver/pro#9994 fix returning empty configuration id

* dbeaver/pro#9994 adds validations and info messages to handle empty default configuration case

* dbeaver/pro#9994 syncs resources to get changes from backend ai settings

* dbeaver/pro#9994 cleanup

* dbeaver/pro#9994 allows to reset language

---------

Co-authored-by: Ainur <ainur.iagudin@dbeaver.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-18 13:22:41 +02:00
sergeyteleshevandEvgenia 9078a379b0 dbeaver/pro#8730 increases area of dropping during reordering for data editor (#4563)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-17 19:27:06 +02:00
sergeyteleshevandDaria Marutkina bc2c204a61 dbeaver/pro#10137 fixes max height for ariakit groups (#4545)
* dbeaver/pro#10137 fixes max height for ariakit groups

* dbeaver/pro#10137 uses max available height for menu popovers

* dbeaver/pro#10137 adds fallback for the menu max height

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-17 19:26:35 +02:00
sergeyteleshevandEvgenia 2ff806ed7c dbeaver/pro#8431 adds more accessibility to conversation caption for AI chat (#4559)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-17 19:25:58 +02:00
Daria Marutkina 5d2b246d61 dbeaver/pro#10231 Update README file (#4566) 2026-08-17 19:06:26 +02:00
Sychev AndreyandDaria Marutkina 212623c0a8 dbeaver/pro#10068 fix: ai setting layout (#4562)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-17 12:14:06 +02:00
sergeyteleshevandDaria Marutkina d08c191a08 dbeaver/pro#10254 allows to ger user info only for users in edit form mode (#4557)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-17 12:13:29 +02:00
sergeyteleshevandEvgenia 6b3e911534 dbeaver/pro#8658 opens links in new window and adds indicator for AI chat messages (#4558)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-17 12:13:11 +02:00
sergeyteleshev 620a5cb73b dbeaver/pro#10229 fixes references panel select popover position (#4549)
* dbeaver/pro#10229 fixes references panel select popover position

* dbeaver/pro#10229 fixes overflowPadding
2026-08-17 12:12:42 +02:00
DaniilandEvgenia 4cf41da82c dbeaver/pro#5650 databend driver is enable in cb-ce (#4553)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-14 17:53:52 +02:00
3844792b84 dbeaver/pro#9637 support restriction for connection (#4521)
* dbeaver/pro#9637 support restriction for connection

* dbeaver/pro#9637 support restriction for connection

* dbeaver/pro#9637 support edit and import restriction

* dbeaver/pro#9637 support restrictions

* dbeaver/pro#9637 review fix

* dbeaver/pro#9639 fix call

* dbeaver/pro#9637 remove duplicate

---------

Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-08-14 00:14:40 +02:00
Sychev Andrey e7f2b8e0a6 dbeaver/pro#6599 fix: first group width on AI profile setting (#4550) 2026-08-13 17:31:15 +02:00
serge-rider f2b30d0f1f dbeaver/pro#10155 Update gson and guava in products 2026-08-12 09:02:38 +02:00
dbeaver-devops 2781ec1eaa dbeaver/pro#10231 26.2.0 version bump 2026-08-11 21:11:20 +02:00
sergeyteleshevandmr-anton-t 707cb3ea41 dbeaver/pro#10205 removes nanoid CVE (#4543)
* dbeaver/pro#10205 removes nanoid CVE

* dbeaver/pro#10205 fixes image-size CVE with updating LESS

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-08-11 19:42:03 +02:00
5be49b8a7e dbeaver/pro#8821 feat: add import settings step (#4506)
* dbeaver/pro#8821 feat: add gql query for import

* dbeaver/cloudbeaver#8821 throw readable exception on import fail

* dbeaver/pro#8821 feat: add import settings step

* dbeaver/pro#8821 refactor: gql types for driver config

* dbeaver/pro#8821 refactor: use connectionKey

* dbeaver/pro#8821 add open New Connection

* dbeaver/pro#8821 feat: add openNewConnection checkbox and replace docs link

* dbeaver/pro#8821 feat: clear onDuplicateKey if useBulk is active

* dbeaver/pro#8821 feat: add titles

* dbeaver/pro#8821 refactor: remove extra checks (not a driver config, always true)

* dbeaver/pro#8821 remove defaultOpenNewConnection from driverConfiguration

* dbeaver/pro#8821 feat: let a user to clear the replace method

* dbeaver/pro#8821 refactor: update open new connection tooltip

* dbeaver/pro#8821 feat: remove checks for settings step

Always show settings steps with at least one option: use new connection

* dbeaver/pro#8821 refactor: simplify dialog's footer

---------

Co-authored-by: Ruslan <pupkinvasya7000@gmail.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
2026-08-11 18:45:36 +02:00
Viktor Kirst 317750ea7d dbeaver/pro#9733 Rename X- headers to CB- with backward compatibility (#4517) 2026-08-11 19:16:38 +03:00
alexandDaria Marutkina 5fb7234e24 dbeaver/pro#9901 reset password after submit (#4542)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-11 15:22:11 +02:00
7a93d56459 dbeaver/pro#6455 add cancel button (#4533)
* dbeaver/pro#6455 add cancel button

* dbeaver/pro#6455 add cancel button

* dbeaver/pro#6455 add cancel button

* dbeaver/pro#6455 fix cancel before job start

* dbeaver/pro#6455 chat message now in common model

* dbeaver/pro#6455 add synchronization for cancel

* dbeaver/pro#6455 add message cancel

---------

Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-11 15:17:44 +02:00
sergeyteleshevandEvgenia bd1e926929 dbeaver/pro#9981 adds export button for admin users page (#4531)
* dbeaver/pro#9981 adds export button for admin users page

* dbeaver/pro#9981 uses fetch to download csv instead of submitForm approach (backend issue)

* dbeaver/pro#9981 makes users export to be available only for PRO products

* dbeaver/pro#9981 reverts export logic

* dbeaver/pro#9981 add actionButtonsPlaceholder

* dbeaver/pro#9981 cleanup

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-11 12:29:14 +02:00
170650e6c2 dbeaver/pro#4711 support uploadable textarea (#4522)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-08-11 09:34:22 +02:00
sergeyteleshevandEvgenia ffc48e1979 dbeaver/pro#9494 removes circle after closing menu (#4541)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-10 19:44:01 +02:00
c4b0dd4650 dbeaver/pro#9999 api for ai engine icon (#4518)
* dbeaver/pro#9999 api for ai engine icon

* dbeaver/pro#9999 make list engines functions public

* dbeaver/pro#9999 add icons for engines and profiles

* dbeaver/pro#9999 keep icons + titles

---------

Co-authored-by: naumov <iamemptyhuh@gmail.com>
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-08-10 17:41:39 +02:00
Ainur 2d955087ea dbeaver/pro#10001 fix validation for custom path implementations (#4539) 2026-08-10 17:26:55 +02:00
sergeyteleshevandEvgenia b5289c7865 dbeaver/pro#9880 updates isChanged with isReadonly flag to fix the form flow (#4535)
* dbeaver/pro#9880 updates isChanged with isReadonly flag to fix the form flow

* Revert "dbeaver/pro#9880 updates isChanged with isReadonly flag to fix the form flow"

This reverts commit 69ab0b0cbd.

* dbeaver/pro#9880 update isChanged logic to handle readonly mode in ConnectionFormSSHPart

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-10 16:35:28 +02:00
sergeyteleshevandDaria Marutkina 740599f41d dbeaver/pro#9982 adds position prop to InputAutocompletionMenu for accurate placement (#4537)
* dbeaver/pro#9982 adds position prop to InputAutocompletionMenu for accurate placement

* dbeaver/pro#9982 recomputes proposals after it is hidden with esc to restore its position

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-10 16:02:44 +02:00
sergeyteleshevandDaria Marutkina 16915940ba 4551 generate full ddl for postgresql metaobjects (#4507)
* dbeaver/pro#4551 adds full DDL for GeneratedSql dialog

* dbeaver/pro#4551 introduces core-sql-generator plugin + adds "full DDL" action into data editor footer for DDL properties tab

* dbeaver/pro#4551 adds supportsFullDdl flag to hide full ddl feature if connection doesn't support it

* dbeaver/pro#4551 adds SqlEntityQueryResource to separate sqlGenerator logic

* dbeaver/pro#4551 refactor: remove core-sql-generator package and related files; migrate SQL generation logic to plugin-sql-generator

* dbeaver/pro#4551 implements DDL query state management and refactors DDL viewer context

* dbeaver/pro#4551 removes state & name in favor of onChange for GeneratedSqlDIalog

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-09 10:12:16 +02:00
sergeyteleshevandDaria Marutkina 1c789cf49b dbeaver/pro#9946 resets admin settings in leave layout scenario (#4528)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-07 15:47:35 +02:00
sergeyteleshevandDaria Marutkina 9474ec9718 dbeaver/pro#8405 adds accessibility for the filter button in the users page (#4527)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-07 14:25:30 +02:00
Ainurandkseniaguzeeva 46a07033ad dbeaver/pro#10001 validate resource folder fix for multinode apps (#4529)
* dbeaver/pro#10001 validate resource folder fix for multinode apps

* dbeaver/pro#10001 fix validation

* dbeaver/pro#10001 validate only dots fix

---------

Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-08-07 14:02:19 +02:00
eb907e95f4 dbeaver/pro#8926 feat: add js helpers for export as image (#4525)
* dbeaver/pro#8926 feat: add js helpers for export as image

* dbeaver/pro#8926 refactor: remove duplicated helpers from core-utils and used from js-helpers

* dbeaver/pro#8926 fix: resolveSize doesn't use partial sizes

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-08-07 13:25:07 +02:00
alexandmr-anton-t cb01f6a988 dbeaver/pro#9957 add labels to ai profile form (#4516)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-08-06 13:39:55 +02:00
sergeyteleshevandDaria Marutkina b2065ffe62 dbeaver/pro#10119 fixes fast-uri, undici, brace-expansion CVEs (#4524)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-06 12:30:47 +02:00
sergeyteleshevandDaria Marutkina 9e8f4b83a6 dbeaver/pro#4428 removes frontend reordering for the suggestions (#4511)
* dbeaver/pro#4428 removes frontend reordering for the suggestions

* dbeaver/pro#4428 refactor: improve fuzzy filtering and sorting of SQL autocompletion proposals

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-06 12:29:50 +02:00
alexandEvgenia 5170fa7dfc dbeaver/pro#10061 disable default profile deletion (#4526)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-06 10:59:16 +02:00
Ainur ad26e0e849 dbeaver/pro#10097 enable ai feature by default (#4530) 2026-08-06 10:30:44 +02:00
eed9cd979e dbeaver/cloudbeaver#4417 add redirect on the same tab (#4502)
* dbeaver/cloudbeaver#4417 add redirect on the same tab

* dbeaver/pro#4417 feat:support full-page redirect

* dbeaver/pro#4417 refactor: remove extra type

* dbeaver/pro#4417 feat: skip navigation when in-tab redirect

* Revert "dbeaver/pro#4417 feat: skip navigation when in-tab redirect"

This reverts commit 4244d41582.

---------

Co-authored-by: Sychev Andrey <44414066+SychevAndrey@users.noreply.github.com>
Co-authored-by: Sychev Andrey <nukemoore@gmail.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-05 16:46:01 +02:00
sergeyteleshevandEvgenia 1b8ea64cd0 dbeaver/pro#10062 moves focused cell after pin/unpin (#4513)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-04 17:34:39 +02:00
fb43b0f567 dbeaver/pro#9076 clear log for ws and long pooling (#4514)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-04 17:16:45 +02:00
317ba5486a dbeaver/pro#8482 fetch conditions for handlers (#4475)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
2026-08-04 17:14:12 +02:00
4d4049fcd1 dbeaver/pro#10054 fix returning default values for non-object properties (#4510)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-08-04 14:55:27 +02:00
alexandEvgenia 27cfdb405f dbeaver/pro#9256 treat boolean as editable on the data level (#4505)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-03 18:48:55 +02:00
alexandDaria Marutkina f1dc4e07bb dbeaver/pro#10056 adjust height in frame (#4509)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-08-03 18:48:20 +02:00
Ruslan MusaevandEvgenia 57cc6e4808 dbeaver/pro#10048 fix ldap groups matching (#4508)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-08-03 15:11:21 +02:00
Daria Marutkina 6bfe58506f dbeaver/pro#10040 Update README file (#4520)
Added changelog entries for version 26.1.4, including new AI features, connectivity improvements, accessibility updates, general enhancements, security fixes, and database driver updates.
2026-08-03 12:09:23 +02:00
Ruslan Musaev 1d6e5c6ce6 dbeaver/pro#9724 fix graphql schema for doc (#4519)
* dbeaver/pro#9724 fix graphql schema for doc

* dbeaver/pro#9724 fix graphql schema for doc
2026-07-31 13:58:54 +02:00
Serge Rider 3a60e04a84 dbeaver/pro#9893 Project navigator model is required 2026-07-30 20:12:48 +03:00
dbeaver-devops 8b33e3a08f dbeaver/pro#10040 26.1.5 version bump 2026-07-28 19:27:19 +02:00
b021ceec4b dbeaver/pro#9665 adds global network profiles (#4465)
* dbeaver/pro#9665 adds global network profiles

* dbeaver/pro#9665 marks global profiles in selector

* dbeaver/pro#9665 cleanup

* dbeaver/pro#9665 fixes document-global G color so it adapts to the theme

* dbeaver/pro#9665 splits logic to tab service & bootstrap

* dbeaver/pro#9665 hides connections tab if no sub-tabs was registered

* Merge branch 'devel' into 9665-cb-global-network-profiles-in-cloudbeaver

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-07-28 19:52:23 +03:00
Ruslan Musaevandsergeyteleshev d318579745 dbeaver/pro#9755 navigator settings sync (#4491)
* dbeaver/pro#9755 navigator settings sync

* dbeaver/pro#9755 navigator settings sync

* dbeaver/pro#9755 navigator settings sync

* dbeaver/pro#9755 adds synchronization for changing connection settings

---------

Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-07-28 19:25:16 +03:00
Ainur 8d17ed0ecd dbeaver/pro#9964 fix generating urls for saml with custom root uri (#4486) 2026-07-28 19:02:00 +03:00
DaniilandDaria Marutkina 3199a99b29 dbeaver/pro#9822 API for full DDL for PostgreSQL (#4477)
* dbeaver/pro#9822 API for full DDL for PostgreSQL

* dbeaver/pro#9822 linter fix

* dbeaver/pro#9822 linter fix

* dbeaver/pro#9822 add cancellable async tasks for DDL generation

* dbeaver/pro#9822 linter fix

* dbeaver/pro#9822 linter fix

* dbeaver/pro#9822 comment fix

* dbeaver/pro#9822 comment fix

* dbeaver/pro#9822 remove PostgreSQL-only restriction for full DDL feature

* dbeaver/pro#9822 review fix: add deprecated annotations to ghl schemas

* dbeaver/pro#9822 review fix: revert changes for metadataGetNodeDDL

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-07-28 16:42:41 +02:00
Sychev AndreyandDaria Marutkina 39008f7170 dbeaver/pro#9865 feat: add title prop to SelectField component (#4501)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-07-28 16:42:29 +02:00
8bf33d539f dbeaver/pro#9905 adds fallback to get connection from nodeId in case nodePath is not set (#4499)
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-07-28 16:37:56 +02:00
Elizabeth 42f6720acd dbeaver/pro#10033 Fix file connections creation (#4503) 2026-07-28 16:08:27 +02:00
sergeyteleshevandEvgenia 0a3cb12c0b dbeaver/pro#9798 adds sorting for columns in user tables (#4484)
* dbeaver/pro#9798 adds sorting for columns in user tables

* dbeaver/pro#9798 adds comparator helpers

* dbeaver/pro#9798 moves user helpers to compareUser file

* dbeaver/pro#9798 fixes naming and any

* dbeaver/pro# adds grantedUsersIdsMap to make less computations

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-07-28 13:22:43 +02:00
202823e731 dbeaver/pro#9719 move ai to community (#4471)
* dbeaver/pro#9719 move ai to community

* dbeaver/pro#9719 return prompt generator id input and fix system message type

* dbeaver/pro#9719 return prompt generator id for conversation

* dbeaver/pro#9719 remove rag

* dbeaver/pro#9719 move ai to ce

* dbeaver/pro#9719 fix description for ai chat

* dbeaver/pro#9719 clear feature

* dbeaver/pro#9719 fix conflicts

* dbeaver/pro#9719 remove unused import

---------

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>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-07-28 13:15:33 +02:00
Ruslan MusaevandAinur ed088fe923 dbeaver/pro#9934 api for user list downloaded (#4498)
Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
2026-07-28 12:01:49 +02:00
df21c3396a dbeaver/pro#9617 api for data-import settings (#4423)
* dbeaver/pro#9617 api for data import settings

* dbeaver/pro#9617 api for data import settings

* dbeaver/pro#9617 api for data import settings

* dbeaver/pro#9617 api for data import settings

* dbeaver/pro#9617 api for data import settings

* dbeaver/pro#9617 api for data import settings

* dbeaver/pro#9617 feat: support new import API on FE

* dbeaver/pro#9617 api for data import settings

---------

Co-authored-by: Sychev Andrey <nukemoore@gmail.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
2026-07-28 12:01:24 +02:00
90ccdc6432 dbeaver/pro#9621 feat: add state to diagram tab (#4495)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-07-28 11:39:37 +02:00
sergeyteleshevandmr-anton-t dba371a320 dbeaver/pro#10011 fixes js-yaml, brace-expansion, postcss CVEs (#4500)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-07-28 11:19:12 +02:00
d96cd8f0d2 dbeaver/pro#9912 add ability to save null for default auto commit mode (#4481)
* dbeaver/pro#9912 add ability to save null for default auto commit mode

* dbeaver/pro#9912 return null value by default

* dbeaver/pro#9912 return valid values

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-07-28 11:09:37 +02:00
ElizabethandDiana bd6e7d48f3 dbeaver/pro#9491 Handle exception from setUrl (#4479)
Co-authored-by: Diana <31996417+uslss@users.noreply.github.com>
2026-07-28 07:51:45 +02:00
8b41e4530f dbeaver/pro#4490 bump postgres version (#4493)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-07-27 18:51:50 +02:00
alexandDaria Marutkina a555f1e759 dbeaver/pro#4425 add controlled mode (#4468)
* dbeaver/pro#4425 add controlled mode

* dbeaver/pro#4425 update yarn lock

* dbeaver/pro#4425 use controlled state for users

* dbeaver/pro#4425 update copyright

* dbeaver/pro#4425 update notice

* dbeaver/pro#4425 update tokens

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-07-27 17:09:40 +02:00
alexandDaria Marutkina fdaa40e431 dbeaver/pro#8663 add auto resize textarea (#4497)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-07-27 17:02:18 +02:00
sergeyteleshevandDaria Marutkina 4c2f9d92e5 dbeaver/pro#9170 adds download button for the script (#4474)
* dbeaver/pro#9170 adds download button for the script

* dbeaver/pro#9170 gets sql filename from node or model

* dbeaver/pro#9170 adds action buttons

* dbeaver/pro#9170 adds borders to the buttons

* dbeaver/pro#9170 fixes build

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-07-27 14:35:17 +02:00
sergeyteleshevandmr-anton-t a92011eee6 dbeaver/pro#9967 fixes immutable & fast-uri CVEs (#4492)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-07-27 11:52:01 +02:00
ElizabethandDiana 61a7757a0e dbeaver/pro#9445 Update Clickhouse driver version (#4480)
Co-authored-by: Diana <31996417+uslss@users.noreply.github.com>
2026-07-24 13:11:02 +02:00
Nikita Akilov a53ca3592f dbeaver/dbeaver-devops#2751 stop including curl with cloud products (#4494) 2026-07-24 11:22:42 +02:00
Sychev AndreyandEvgenia 9e682617e6 dbeaver/pro#9638 [CB] refresh is needed to see replaced connectionfolder in another instance (#4464)
* dbeaver/pro#9638 fix: update project filtering logic to use project IDs for active projects

Changed all four spots from reference equality to id comparison, matching the rest of the codebase, see https://github.com/dbeaver/cloudbeaver/issues/4430

* dbeaver/pro#9638 refactor(connectionUpdateHandler): move node to a new parent on change

* dbeaver/pro#9638 revert commit eb2ba7871e

* dbeaver/pro#9638 fix: update node parents on move in other session

Server events don't carry the actual data, so besides outdating (which lets the handlers invalidate the previous state, e.g. the old location in the navigation tree) we explicitly load the connection to guarantee that the handlers receive the actual state

* dbeaver/pro#9638 fix: sync connection folder move from another session

* dbeaver/pro#9638 fix: revert back connectionUpdateHandler

* dbeaver/pro#9638 fix: remove try catch

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-07-23 17:33:26 +02:00
sergeyteleshevandDaria Marutkina 16dda810d7 dbeaver/pro#9171 adds ability to open sql editor with context from Generate SQL dialog (#4488)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-07-23 16:30:47 +02:00
Sychev AndreyandEvgenia 6042f16342 dbeaver/pro#9426 feat: add loading state to save button (#4487)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-07-23 16:30:31 +02:00
Viktor Kirst 0afdd06c80 dbeaver/pro#9768 Fix long-polling session cookie after session ID rotation (#4467) 2026-07-23 16:04:31 +03:00
Sychev Andreyandmr-anton-t 3158aebca4 dbeaver/pro#9877 UI fixes (#4473)
* dbeaver/pro#9877 fix: select render field popover

* dbeaver/pro#9877 fix: revert back ripple hover effect

* dbeaver/pro#9877 fix: make all outlines 1px by default

* dbeaver/pro#9877 fix: switch outline

* dbeaver/pro#9877 fix: topbar buttons outline

* dbeaver/pro#9877 fix: tablist styles

* dbeaver/pro#9877 fix: rule priorities

* dbeaver/pro#9877 fix: eslint

* dbeaver/pro#9877 fix: eslint

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-07-23 13:07:10 +02:00
mayerro 99866fdc12 Revert "dbeaver/dbeaver-devops#2751 remove curl from base java to fix cve (#4483)"
This reverts commit f54f377344.
2026-07-23 11:17:11 +02:00
sergeyteleshev a58eb59a32 dbeaver/pro#9938 fixes 8 CVEs (#4478) 2026-07-23 11:08:30 +02:00
DaniilandEvgenia eda1f8720f dbeaver/pro#9721 [CB] Add a flag into reverse proxy allowing new user… (#4460)
* dbeaver/pro#9721 [CB] Add a flag into reverse proxy allowing new users creation

* dbeaver/pro#9721 [CB] code style fix

* dbeaver/pro#9721 [CB] code style fix

* dbeaver/pro#9721 move the method isAutoUserProvisioningEnabled to SMAuthProviderExternal

* dbeaver/pro#9721 linter fix

* dbeaver/pro#9721 code review fix

* dbeaver/pro#9721 code review fix

* dbeaver/pro#9721 code review fix

* dbeaver/pro#9721 Fix login for existing users without reverse proxy credentials when auto user provisioning is disabled

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-07-23 10:09:30 +02:00
Nikolay f54f377344 dbeaver/dbeaver-devops#2751 remove curl from base java to fix cve (#4483)
* dbeaver/dbeaver-devops#2751 removed curl from base java to fix cve

* dbeaver/dbeaver-devops#2751 removed an extra comment
2026-07-22 16:31:49 +03:00
alex 81a9be17b8 dbeaver/pro#9659 add dnd scroll container (#4476) 2026-07-22 14:24:53 +02:00
alex af42d7f50a dbeaver/pro#9910 check key also (#4469) 2026-07-20 15:55:01 +02:00
Daria Marutkina d083ac1330 dbeaver/pro#9879 Update README file (#4472)
Added changelog for version 26.1.3, including updates on data editor, general improvements, data transfer enhancements, accessibility features, security fixes, and database driver updates.
2026-07-20 13:21:53 +02:00
Serge Rider cdb450dfde dbeaver/pro#7329 Navigator view actions model refactoring (get rid of UI).
Git service and navigator web service code style.
2026-07-15 12:01:30 +02:00
dbeaver-devops d0d9ee19ab dbeaver/pro#9879 26.1.4 version bump 2026-07-14 19:24:11 +02:00
9327ce0ac2 dbeaver/pro#9522 integrate profile selector to connection form (#4440)
* dbeaver/pro#9522 integrate profile selector to connection form

* dbeaver/pro#9522 handles edge cases

* dbeaver/pro#9522 uses rest instead of default getter

* dbeaver/pro#9522 adds preprocessing to handle null/undefined differences arrived from backend for both: connection form network handlers & profile form network handlers

* dbeaver/pro#9522 eslint fix

* dbeaver/pro#9522 refactors profiles

* dbeaver/pro#9522 removes preprocess

* dbeaver/pro#9522 adds header & footer items

* dbeaver/pro#9522 truncates items values

* dbeaver/pro#9522 fix retrieving creds from profiles

* dbeaver/pro#9522 fix test temporary connection

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Ainur <ainur.iagudin@dbeaver.com>
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-07-14 18:50:59 +02:00
1fb9dd7b5a dbeaver/pro#9723 Fix UI styles for improved accessibility and consistency (#4459)
* dbeaver/pro#9723 feat: add focus-visible styles for improved accessibility

* dbeaver/pro#9723 fix: adjust gutter size in ComboboxPopover for consistent spacing

* dbeaver/pro#9723 fix: update button outline offset for consistency

* dbeaver/pro#9723 fix: update hover opacity

---------

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-07-14 18:37:55 +02:00
alexandDaria Marutkina e82cac479b dbeaver/pro#9861 pass null as valid filter (#4461)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-07-14 17:44:07 +02:00
bf6afb0340 dbeaver/pro#9712 Databend driver updated to 0.4.8 (#4453)
Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-07-14 16:13:26 +02:00
4779c3b405 dbeaver/pro#9000 adds API token settings (#4455)
* dbeaver/pro#9000 adds API token settings

* dbeaver/pro#9000 adds save extend instead

* dbeaver/pro#9000 adds observable to the schema

* dbeaver/pro#9000 adds LooseObject in favor of server config part state extension

---------

Co-authored-by: Ainur <ainur.iagudin@dbeaver.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-07-14 12:15:43 +02:00
31ee01b6fa dbeaver/pro#9614 add predefined param for connection type (#4454)
* dbeaver/pro#9614 add predefined param for connection type

* dbeaver/pro#9614 add connection type selector

* dbeaver/pro#9614 make menu indicator more visible

* dbeaver/pro#9614 add extra colors

* dbeaver/pro#9614 outdate connections

* dbeaver/pro#9614 load types in bootstrap phrase

* dbeaver/pro#9614 always use useConnectionTypeColor

* dbeaver/pro#9614 remove unused import

* dbeaver/pro#9614 add useConnectionTypeLoader hook

* dbeaver/pro#9614 add default hint

* dbeaver/pro#9614 do not wrap arg:item by default

* dbeaver/pro#9614 use predefined unset for all types

* dbeaver/pro#9614 use common mapped unset color

---------

Co-authored-by: Ainur <ainur.iagudin@dbeaver.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-07-13 22:11:10 +02:00
025944a867 dbeaver/pro#8947 user provisioning return user ids (#4429)
Co-authored-by: Sychev Andrey <44414066+SychevAndrey@users.noreply.github.com>
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-07-13 16:00:32 +02:00
Serge Rider caee9914b8 dbeaver/pro#9851 Refactor data transfer producer. Smart mapping bug fix. (#4458) 2026-07-13 15:43:53 +02:00
sergeyteleshevandEvgenia 6e2b1b7a17 dbeaver/pro#8849 fixes spacing for bind parameters popup (#4456)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-07-09 19:22:41 +02:00
sergeyteleshevandDaria Marutkina a379c2d7ba dbeaver/pro#9755 adds frontend cleanup for js artifacts before each app build (#4450)
* dbeaver/pro#9755 adds frontend cleanup for js artifacts before each app build

* dbeaver/pro#9775 adds x to log (revert it)

* dbeaver/pro#9775 adds clear commands to build frontend script

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-07-08 17:44:33 +02:00
sergeyteleshevandDaria Marutkina 8b1054a148 dbeaver/pro#9775 adds unit test for getLocalizedDisplayName (#4451)
* dbeaver/pro#9775 adds unit test for getLocalizedDisplayName

* dbeaver/pro#9775 adds unit test for core utils

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-07-08 17:44:21 +02:00
Ruslan MusaevandEvgenia 7fdc8f7e61 dbeaver/pro#9714 fix ldap injection (#4441)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-07-08 10:21:15 +02:00
Sychev AndreyandEvgenia 32bebd4bad dbeaver/pro#9303 feat: update edge color variable in CSS for execution plan (#4387)
* dbeaver/pro#9303 feat: update edge color variable in CSS for execution plan

* dbeaver/pro#9303 feat: update node cost bar background and add theme-light styles for edges and captions

* dbeaver/pro#9303 feat: update edge color in execution plan CSS

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-07-08 09:47:46 +02:00
Serge RiderandAinur 4221ea7fe0 dbeaver/pro#8757 Test connection permissions required (#4434)
* dbeaver/pro#8757 Test connection permissions required (the same as createConnection)

* dbeaver/pro#8757 Anno added

* dbeaver/pro#8757 restrict private project use for gql

* dbeaver/pro#8757 change error message

---------

Co-authored-by: Ainur <ainur.iagudin@dbeaver.com>
2026-07-07 14:10:26 +02:00
sergeyteleshevandDaria Marutkina af11aaa8fe dbeaver/cloudbeaver#3815 upgrades lib to version where it works (#4165)
* dbeaver/cloudbeaver#3815 upgrades lib to version where it works

* dbeaver/pro#3815 updates react-hotkeys-hook lib

* dbeaver/pro#3815 fixes build

* dbeaver/pro#3815 fixes case dependant hotkeys activation

* dbeaver/pro#3815 allows textbox form tag to support sql editor

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-07-07 14:09:27 +02:00
5c58343a10 dbeaver/pro#8402 feat: enable checkbox selection via keyboard in Team… (#4445)
* dbeaver/pro#8402 feat: enable checkbox selection via keyboard in Teams and Users tables

* dbeaver/pro#8402 feat: enable checkbox selection via keyboard and improve accessibility in tables

fix checkbox and header cell keyboard events

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-07-07 10:59:16 +02:00
Serge Rider a565beb777 dbeaver/pro#9764 Fix launch parameters for Java 25 (#4448) 2026-07-06 16:59:22 +02:00
9df99fb62e dbeaver/pro#8427 feat: enable default fold keymap in CodeMirror (#4446)
* dbeaver/pro#8427 feat: enable default fold keymap in CodeMirror

* dbeaver/pro#8427 feat: add SQL editor shortcuts for folding and unfolding code blocks

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-07-06 14:31:26 +02:00
Serge RiderandMashaKorax b3127b9aa7 dbeaver/pro#9682 AI config profiles (#4437)
Co-authored-by: MashaKorax <84867187+MashaKorax@users.noreply.github.com>
2026-07-06 13:49:35 +02:00
Daria Marutkina f8677a0689 dbeaver/pro#9745 Update README file (#4447)
Updated changelog for version 26.1.2, detailing new features, bug fixes
2026-07-06 11:32:53 +02:00
Ruslan Musaev f22a936507 dbeaver/pro#9749 fix sso login npe (#4443)
* dbeaver/pro#9749 fix sso login npe

* dbeaver/pro#9749 fix sso login npe
2026-07-01 20:05:26 +02:00
Serge Rider b5858637d6 dbeaver/pro#9222 Annotations and permissions 2026-07-01 09:48:04 +02:00
serge-rider 475c8129fc dbeaver/pro#4320 Annotations 2026-06-30 21:55:37 +02:00
dbeaver-devops 50d846d087 dbeaver/pro#9745 26.1.3 version bump 2026-06-30 19:42:04 +02:00
Ainur 16bd3ca575 dbeaver/pro#9612 api for connection types (#4426)
* dbeaver/pro#9612 api for connection types

* dbeaver/pro#9612 add more fields for connection type info

* dbeaver/pro#9612 renaming and event handling

* dbeaver/pro#9612 fix doc and add all inputs
2026-06-30 19:15:46 +02:00
Alexander SkoblikovandMashaKorax 927184e009 dbeaver/pro#3833 fix bug with "null" origin header (#4395)
Co-authored-by: MashaKorax <84867187+MashaKorax@users.noreply.github.com>
2026-06-30 18:27:18 +02:00
d4d9081936 dbeaver/pro#9652 refactor ref api (#4412)
* dbeaver/pro#9652 refactor ref api

* dbeaver/pro#9652 refactor ref api

* dbeaver/pro#9652 refactor ref api

* dbeaver/pro#9652 refactor ref api

* dbeaver/pro#9652 remove references

* dbeaver/pro#9652 add anyConstraint

* dbeaver/pro#9652 add references panel

* dbeaver/pro#9652 remove logs

* dbeaver/pro#9652 fix attr position

* dbeaver/pro#9652 fix review

* dbeaver/pro#9652 change naming

* dbeaver/pro#9652 fallback to custom if composite and more than 1 row

* dbeaver/pro#9652 add indicator

* dbeaver/pro#9652 show proper error

* dbeaver/pro#9652 fix review, add targetColumnIndex

* dbeaver/pro#9652 fix targetColumnIndex

* dbeaver/pro#9652 pass target index

* dbeaver/pro#9652 add icons

* dbeaver/pro#9652 check prev constaints, decrease blicking

* dbeaver/pro#9652 fix constraint overwriting

* dbeaver/pro#9652 pass anyConstraint flag to export

* dbeaver/pro#9652 disable references for editors and viewers

* dbeaver/pro#9652 update icons

* dbeaver/pro#9652 show placeholder instead of loader

* dbeaver/pro#9652 show combobox

---------

Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com>
Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-06-30 17:57:40 +02:00
a0bcfba73d dbeaver/pro#9639 load driver properties fix (#4420)
* dbeaver/pro#9639 load driver properties fix

* dbeaver/pro#9639 pass connection config to get driver properties

* dbeaver/pro#9639 execute format action before loading properties

* dbeaver/pro#9639 do not reset properties if tab is selected

* dbeaver/pro#9639 fix import

---------

Co-authored-by: Ainur <ainur.iagudin@dbeaver.com>
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-30 17:38:36 +02:00
Sychev Andreyandmr-anton-t 09f4828006 dbeaver/pro#9704 [CB] Fixes outlines for several components (#4431)
* dbeaver/pro#9704 fix: adjust combobox and select control outline properties

* dbeaver/pro#9704 fix: add control outline color and width for checkbox and radio components

* dbeaver/pro#9704 fix: set outline color and width for disclosure component

* dbeaver/pro#9704 refactor: add text overflow handling for combobox item values

remove tw:truncate because it adds hidden text-sm font-size

* dbeaver/pro#9704 fix: update combobox popover and item classes

* dbeaver/pro#9704 fix: correct portal handling in SelectPopover component

* dbeaver/pro#9704 fix: ensure correct portal handling in ComboboxPopover component

* dbeaver/pro#9704 refactor: rename ComboboxType to IComboboxType for consistency

* dbeaver/pro#9704 fix: line-height for combobox items

* dbeaver/pro#9704 style: remove unnecessary letter-spacing from combobox item

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-30 17:17:54 +02:00
alexandDaria Marutkina cde64e255b dbeaver/pro#9586 adjust validation (#4427)
* dbeaver/pro#9586 let editing of loaded truncated values

* Revert "dbeaver/pro#9586 let editing of loaded truncated values"

This reverts commit 6b412ef57c.

* dbeaver/pro#9586 adjust quota validation check

* dbeaver/pro#9586 show limit amount

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-06-30 16:42:01 +02:00
7b134cef81 dbeaver/pro#9526 adds profile form (#4390)
* dbeaver/pro#9526 adds profile form base functionality

* dbeaver/pro#9526 adds profiles form

* dbeaver/pro#9526 moves SSHForm to connection-network-handlers to reuse it for admin settings in future without extra deps

* dbeaver/pro#9526 moves ssh files to connection-network-handlers-plugin

* pr fixes

* moves enable form switch inside SSHForm

* dbeaver/pro#9526 shares the rest logic for configuring the ssh with profiles form as well

* dbeaver/pro#9526 cleanup

* dbeaver/pro#9526 adds go back to profiles arrow action

* dbeaver/pro#9526 small ui adjustments

* dbeaver/pro#9618 pr fixes

* dbeaver/pro#9526 creates ProjectInfoOptionsPanelService to incapsulate opening logic with tabId

* dbeaver/pro#9526 adds core-network-handlers plugin

* dbeaver/pro#9526 moves ssh network handler layout to core-network-handlers conig + adds TODO to register it to connection form

* dbeaver/pro#9526 moves registration of connection SSH network handler to plugin-connection-network-handler (from plugin-connection)

* dbeaver/pro#9526 moves registration of connection SSL network handler to plugin-connection-network-handler (from plugin-connection)

* dbeaver/pro#9526 migrates localizations among the plugins + adds missing translations

* dbeaver/pro#9526 cleanup

* dbeaver/pro#9526 fixes build

* dbeaver/pro#9526 core-network-handlers -> plugin-network-handlers

* dbeaver/pro#9526 removes license from locale files

* dbeaver/pro#9526 CONNECTION_NETWORK_HANDLER_SCHEMA -> NETWORK_HANDLER_SCHEMA

* dbeaver/pro#9526 saveChanges interface update

* dbeaver/pro#9526 refactors core -> plugin tokens

* dbeaver/pro#9526 applies copilot suggestions

* dbeaver/pro#9526 pr fixes

* dbeaver/pro#9526 moves test tunnel functionality inside ssh form

* dbeaver/pro#9526 handle network profiles changes

* dbeaver/pro#9526 cleanup

* dbeaver/pro#9526 fixes after review

* dbeaver/pro#9526 fix sending event for profiles

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Ainur <ainur.iagudin@dbeaver.com>
2026-06-30 16:41:10 +02:00
Ruslan Musaev fde7502df8 dbeaver/pro#9713 fix event filter for rotating session (#4436) 2026-06-30 12:36:31 +02:00
Sychev Andrey 955b28c520 dbeaver/pro#9699 fix: reorder items in sql toolbar (#4433) 2026-06-30 12:29:58 +02:00
Ainur 5cfc37e26f dbeaver/pro#9481 save api token max duration fix (#4435) 2026-06-30 12:28:24 +02:00
Ruslan Musaevandsergeyteleshev 42cb6b3a9d dbeaver/pro#9022 fix error file saving (#4424)
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-06-26 12:16:44 +02:00
Serge Riderandkseniaguzeeva ad245e9f4c dbeaver/pro#9700 Save RM datasources correctly (#4425)
* dbeaver/pro#9700 Save RM datasources correctly

* dbeaver/pro#9700 Fix empty collection parameters pass over remote client

---------

Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-06-25 14:35:26 +02:00
08260e6a55 dbeaver/cloudbeaver#4249 added session rotation (#4274) (#4331)
* dbeaver/cloudbeaver#4249 added session rotation (#4274)

* dbeaver/cloudbeaver#4249 added session rotation

* dbeaver/pro#4249 update web session id

* dbeaver/pro#4249 remove log

* dbeaver/pro#4249 remove unused params

* dbeaver/pro#4249 revert non-related changes

* dbeaver/cloudbeaver#4249 rotate session before login

* dbeaver/cloudbeaver#4249 fix code style

* dbeaver/cloudbeaver#4249 rotate only for anon users

* dbeaver/cloudbeaver#4249 added notnulls

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>

* dbeaver/pro#4249 fix websocket for session rotation

---------

Co-authored-by: Dmitrii Barnukov <29237913+Nexus6v2@users.noreply.github.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Ruslan <pupkinvasya7000@gmail.com>
2026-06-25 07:06:38 +02:00
Serge Rider aea12e9fed dbeaver/pro#9700 Save RM datasources correctly 2026-06-24 17:48:29 +02:00
Ruslan MusaevandDaria Marutkina caef729a8d dbeaver/pro#9599 fix allows unauthorized server file stream (#4411)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-06-24 15:29:07 +02:00
12030822ea dbeaver/pro#9502 [CB] move chart settings export dialogs to the widget (#4399)
* dbeaver/pro#9502 feat: allow importing css files from core-blocks package

Plugins that build UI directly on ui-kit components need the CloudBeaver theme css for these components without importing the components themselves. Expose css files through package exports so a plugin can load them as a side effect.

* dbeaver/pro#9502 fix: update focus-visible styles for select component

* dbeaver/pro#9502 feat: add tokens for combobox items

* dbeaver/pro#9502 feat: load Select/Combobox base styles from core-blocks

ui-kit controls reuse these styles, but the CE wrapper components are lazy, so the CSS only shipped in the lazy chunk, we need those styles for widgets that use UI-kit controls

* dbeaver/pro#9502 feat: enhance Select component with value display styling

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-24 12:54:46 +02:00
Mikhailov GrigoriiandSerge Rider 97cf62d333 dbeaver/dbeaver-devops#2660 Added permissions read on contents pull-requests (#4422)
Co-authored-by: Serge Rider <serge@jkiss.org>
2026-06-24 05:01:41 +02:00
8240c92247 dbeaver/pro#8796 [CB] create a separate plugin for the execution plan (#4406)
* dbeaver/pro#8796 refactor: extract Execution Plan into separate plugin

Move all Execution Plan logic out of plugin-sql-editor into a new plugin-sql-editor-execution-plan. plugin-sql-editor no longer knows about execution plan and instead exposes generic extension points

Execution plan tab data now lives in-memory in SqlExecutionPlanService instead of the persisted editor state schema (these tabs are session-only).

* dbeaver/pro#8796 feat: add naming convention rule for exported interfaces

* dbeaver/pro#8796 fix: update js-helpers dependency version in yarn.lock

* dbeaver/pro#8796 refactor: reorder register method for better readability

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-23 21:51:29 +02:00
sergeyteleshevandEvgenia 62a194c1b0 dbeaver/pro#7510 adds last login info to users tables (#4421)
* dbeaver/pro#7510 adds last login info to users tables

* dbeaver/pro#7510 shows only date

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-06-23 21:46:51 +02:00
sergeyteleshevandEvgenia 36e954c440 dbeaver/pro#9664 removes undici CVE (#4415)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-06-23 13:05:38 +02:00
Sychev AndreyandEvgenia a674c183d5 dbeaver/pro#5015 [CB] Ability to use keyboard to enabledisable checkboxes in the properties tab (#4416)
* dbeaver/pro#5015 feat: enable keyboard navigation for context menu in ObjectMenuCell

* dbeaver/pro#5015 feat: enable keyboard activation for context menu in ObjectMenuCell

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-06-23 12:51:04 +02:00
Sychev Andrey 4bab1025d7 dbeaver/pro#9660 fix: selection for search panel inputs (#4418) 2026-06-23 12:49:27 +02:00
Serge Riderandmr-anton-t 7d21186158 dbeaver/pro#9266 Refactor workspace path init and change (#4409)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-22 16:54:26 +02:00
eb5f89dae3 dbeaver/pro#9644 Data filters thread safety (#4407)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: Diana <31996417+uslss@users.noreply.github.com>
2026-06-22 16:06:37 +02:00
70292cb9c0 dbeaver/pro#9588 remove top margin (#4405)
* dbeaver/pro#9588 remove top margin

* dbeaver/pro#9588 fix: move margin from indicator to checkbox itself

add multiline checkbox story for alignment with text

* Revert "dbeaver/pro#9588 remove top margin"

This reverts commit 0b25ed6f00.

---------

Co-authored-by: Sychev Andrey <nukemoore@gmail.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-06-22 14:38:20 +02:00
Evgenia 950f45833a dbeaver/pro#9630 Update README file CB 26.1.1 (#4414)
Added changelog for version 26.1.1
2026-06-22 12:39:55 +02:00
1008 changed files with 24515 additions and 3887 deletions
+25 -4
View File
@@ -30,16 +30,37 @@ jobs:
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
permissions:
contents: read
pull-requests: read
lint-frontend:
name: Frontend
uses: ./.github/workflows/frontend-lint.yml
secrets: inherit
required-builds:
name: Required Builds
if: ${{ always() && github.event.pull_request.draft != true }}
needs: [build-server, build-frontend]
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Verify required builds
env:
SERVER_RESULT: ${{ needs.build-server.result }}
FRONTEND_RESULT: ${{ needs.build-frontend.result }}
run: |
if [[ "$SERVER_RESULT" != "success" || "$FRONTEND_RESULT" != "success" ]]; then
echo "Server build: $SERVER_RESULT"
echo "Frontend build: $FRONTEND_RESULT"
exit 1
fi
# call-frontend-tests:
# name: Frontend Unit Tests
# needs: call-frontend-build
+1 -1
View File
@@ -48,6 +48,7 @@
"-Declipse.pde.launch=true",
"-XstartOnFirstThread",
"-Dfile.encoding=UTF-8",
"--enable-native-access=ALL-UNNAMED",
"--add-modules=ALL-DEFAULT",
"--add-opens=java.base/java.io=ALL-UNNAMED",
"--add-opens=java.base/java.lang=ALL-UNNAMED",
@@ -64,7 +65,6 @@
"--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED",
"--add-opens=java.base/sun.nio.ch=ALL-UNNAMED",
"--add-opens=java.base/sun.security.ssl=ALL-UNNAMED",
"--add-opens=java.base/sun.security.action=ALL-UNNAMED",
"--add-opens=java.base/sun.security.util=ALL-UNNAMED",
"--add-opens=java.security.jgss/sun.security.jgss=ALL-UNNAMED",
"--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED",
+96
View File
@@ -0,0 +1,96 @@
# CloudBeaver
## What is CloudBeaver?
CloudBeaver is an open-source, web-based database management application. Its modular frontend provides the database navigator, SQL editor, data editor, administration, and related features, and communicates with the CloudBeaver server through GraphQL.
## Frontend
### Scope
- These instructions apply to the frontend under `webapp/` in CloudBeaver Community Edition and to any other CloudBeaver edition or product that shares this frontend architecture.
- Do not modify `server/`, `deploy/`, or backend configuration unless the task explicitly requires it. Backend GraphQL schemas are an input to frontend code generation, not part of the default edit scope.
- Treat `package.json`, `.yarnrc.yml`, and the frontend CI workflows as the source of truth for tool and runtime versions; do not duplicate their version numbers in documentation.
### Project map
- `webapp/package.json`: Yarn Plug'n'Play workspace root and repository-wide frontend scripts.
- `webapp/packages/core-*`: shared application foundations, services, UI primitives, state, and infrastructure.
- `webapp/packages/plugin-*`: feature modules. A package normally registers services and bootstraps in `src/module.ts` and exposes its public API from `src/index.ts`.
- `webapp/packages/product-*`: CE product composition and Vite entry points. `product-default` is the runnable/bundled frontend; `product-default-impl` selects the CE plugin set.
- `webapp/packages/core-sdk`: GraphQL operations and generated client types.
- `webapp/common-typescript/@dbeaver/*` and `webapp/common-react/@dbeaver/*`: shared DBeaver utilities and React components used by the workspace.
- Tests are colocated with source as `*.test.ts` or `*.test.tsx`; styles are usually colocated CSS modules.
### Stack
TypeScript ES modules, React, MobX, the project DI/module registry, GraphQL Code Generator, Vite, Yarn workspaces with PnP, Vitest and Testing Library, ESLint, Prettier, CSS Modules, and Tailwind-based theming.
### Setup and common commands
Use the Node version configured by frontend CI and the Yarn version pinned in `webapp/package.json`. Run frontend commands from `webapp/` unless noted.
```sh
corepack enable
cd webapp
yarn install --immutable
```
```sh
# Start the frontend dev server; a compatible CloudBeaver API must run separately.
(cd packages/product-default && yarn dev)
# From webapp/: repository-wide checks.
yarn lint
yarn test
yarn validate-dependencies
# Production frontend build.
(cd packages/product-default && yarn bundle)
```
For a focused check, use a package's existing script, for example `yarn workspace @cloudbeaver/core-utils test` or `yarn workspace @cloudbeaver/plugin-sql-editor lint`. Do not invent a package script that is absent from its `package.json`.
### Working conventions
- Put changes in the smallest package that owns the behavior. Prefer existing `core-*` abstractions and UI primitives before adding cross-package helpers.
- Keep dependencies directed from plugins to core: `core-*` packages must never import or depend on `plugin-*` packages. Plugins may use public APIs from core packages and from other plugins, subject to the administration boundary and the no-cycles rule below.
- Put administration-only behavior in a separate package whose name ends with `-administration`. Public/non-administration plugins must not import or depend on administration packages.
- Keep the workspace package dependency graph acyclic; do not introduce direct or indirect circular dependencies.
- Import workspace packages through their public `@cloudbeaver/*` or `@dbeaver/*` exports; never reach into another package's `src/` directory.
- When adding a workspace dependency, update the package's `package.json` through Yarn. The configured `ts-project-linker` synchronizes `tsconfig.json` project references during a non-immutable install; edit references manually only if automatic linking cannot complete. Run `yarn validate-dependencies` afterward.
- Register services and bootstraps through the package's `src/module.ts`. Change product composition only when enabling or disabling a package for the CE product.
- Put user-visible text in the package localization files and follow the neighboring `LocaleService` pattern; do not hardcode UI copy in components.
- Add or change GraphQL operations as `.gql` files in `core-sdk`. Regenerate through `yarn workspace @cloudbeaver/core-sdk gql:gen`; never edit the ignored generated `src/sdk.ts` manually.
- Preserve valid license headers on existing source files. When modifying an existing source file, update the ending copyright year to the current four-digit calendar year while keeping its original starting year. For new source files, use the template below and replace `<CURRENT_YEAR>` with the current year; never leave the placeholder in committed code. Use the repository ESLint and Prettier configuration rather than manual formatting rules.
- Do not edit or commit generated/install artifacts such as `node_modules/`, `.pnp.*`, `lib/`, `dist/`, `coverage/`, or `allure-results/`.
- Add focused tests for behavior changes. Before handoff, run lint and tests for affected packages; run the repository-wide checks and product bundle for cross-cutting or product-composition changes.
### License header for new source files
```text
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-<CURRENT_YEAR> DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
```
## Backend
- Before backend work, read and follow `../dbeaver/AGENTS.md` for inherited Java and Tycho conventions.
- Cloudbeaver does not use SWT or Eclipse RCP, and its backend is headless.
- Configuration file is generated using `apps/config-generator`. For making changes there, update `config/template/cloudbeaver-base.conf` or use patches (for specific product parameters).
- The backend must support at least the following databases: PostgreSQL, MySQL, Oracle, and SQL Server. Support for additional databases is optional.
### GraphQL API
- Use consistent names. IDs must use type `ID`; ID arguments and inputs must end with `Id`, for example `projectId`.
- Top-level methods follow `{pluginId}{methodName}`, for example `authLogin`, `rmListProjects`, and `navNodeChildren`. Mark new public API with `@since`.
- Preserve released APIs: deprecate instead of renaming or removing, include the deprecation version, and remove only after more than one year. EA-only APIs may change before public release.
- New arguments and input fields added to a released API must be optional.
### License header
- Please use header from ../dbeaver/docs/license_header.txt for backend source files.
+68 -73
View File
@@ -28,84 +28,79 @@ You can see a live demo of CloudBeaver server here: https://demo.cloudbeaver.io
## Changelog
### 26.1.0 2026-06-01
### 26.2.0 2026-08-31
### Changes since 26.0.0:
### Changes since 26.1.0
- AI Assistant:
- Added AI Chat to help users generate and fix queries and explore data more easily. It’s integrated with the SQL Editor to run generated queries instantly. All AI features can be turned off in the Server configuration.
- Added the ability to use multiple AI configurations for working with the AI chat. Administrators can define tokens, engines and models for OpenAI and Copilot in the Administration panel for each AI profile separately. After that, users can switch between these profiles while using AI features.
- Added the ability to cancel a response in the AI Chat to stop response generation when needed.
- The "Endpoint" setting was renamed to "API Base URL" for the OpenAI provider.
- Added AI engine icons to AI configuration profiles, making it easier to identify AI providers in the Administration panel and AI Chat configuration.
- 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)
- Added the database version and the driver version to diagnostic logs.
- Fixed the issue of fetching groups during LDAP authorization. CloudBeaver now provides all matched groups regardless of fetching errors.
- Added the Last Login time to the user form and the user table in the administration part.
- Authorization:
- Added HTTP header authentication support for GraphQL queries. Users can provide API tokens via Authorization or X-Api-Key headers, allowing external data tools to authenticate without modifying request bodies.
- Fixed a bug in the authentication form: the existing password is now required for password changes.
- SQL Editor:
- Updated the SQL Editor to use real tab characters for indentation. The application displayed tabs as four-space gaps with a distinct visual marker.
- Added an ability to view and edit scripts without connecting to the database.
- Added the ability to open generated queries right from the Generate SQL window.
- Added fuzzy search to SQL Editor auto-completion for object names. The feature allows users to find tables and containers even with typos or incorrect letter ordering.
- Added the tooltip about quoting rules for the Bind variables parameters dialog.
- Fixed keyword autocompletion for partially typed keywords to avoid redundant letters.
- Added the option to upload files for certificates and keys in the SSL authorization configuration.
- Fixed an issue causing the application to lose connection to the database after extended uptime. Access is kept automatically during long-running sessions.
- Data Editor:
- Added the ability to copy-paste multiple cells at once. Pasted values will be distributed across selected cells.
- Added an ability to add, duplicate, delete, or set values to NULL in multiple highlighted rows via the Data Editor context menu.
- Added the Find and Replace functionality for the Data Editor with the ability to find data by matching case, whole word, or using regular expressions.
- Data Editor started to keep the state of column configurations, such as filters, sorting, and ordering, after the reconnect, page refresh, and re-login.
- Added key column mark for tables with unique keys in the data grid.
- Added more settings to the Generated SQL window, including the ability to select fully qualified names for query or use compact SQL formatting.
- Added a DDL generation option to the Data Editor and Result Set context menu.
- Fixed copy and paste behavior for multiline and special characters in the Data Editor. Added visual glyphs to indicate line breaks and tabs in the data grid.
- Fixed the ability to copy Boolean values in the clipboard in the Data Editor.
- Fixed the ability to calculate row count for non-standard SELECT statements. (thanks to @fdcastel)
- Navigator tree:
- Reorganized the context menu on the connection level to make it more compact.
- Added support for special symbols (pipe, comma, and asterisk) for the search field.
- Removed the "Folders" setting from the settings list for the Navigator Tree.
- Added the References panel to the Data Editor. The panel displays related records from connected tables. Users can explore table relationships directly.
- Added the ability to export generated SQL to a .sql file from the Generate SQL dialog.
- Fixed copying of large JSON values in the Data Editor - now the full value can be copied.
- Resolved an issue where the Data Editor ignored the text preview's maximum length quota. Increasing this quota now enables the ability to edit large values.
- Data Transfer:
- Added advanced data import settings, including database-specific replace methods, transaction support, bulk loading, and the option to use a separate connection for data import.
- Improved memory usage when importing large CSV files to improve the application performance.
- 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.
- Improved accessibility for the search on the Users section in the Administration panel. The application allows focusing on the search field with the Tab key.
- Added a loader to the Save button in the Administration panel to improve indication of the long-performing operations.
- Improved drag-and-drop in the Navigator Tree for objects by enlarging the auto-scroll trigger areas.
- Added the ability to expand and collapse code block elements, including JSON, using the keyboard in the Data Editor.
- Fixed shortcuts behavior for AZERTY keyboards. Now shortcuts like Ctrl/Cmd + Z work correctly.
- Added the ability to use the Enter or space keys to turn checkboxes on/off in the connection properties tab.
- General:
- Marked icons for non-saved (temporary) scripts with the hourglass symbol to add visual distinction from the saved ones.
- Added the ability to set to NULL any value in the Driver Properties.
- Fixed custom driver properties display for the connection page.
- Fixed data export in SQL format for values containing single quotes.
- Databases and drivers:
- ClickHouse:
- Fixed the display of Array(JSON) types in the data grid.
- Updated driver version to 0.9.7.
- Fixed IP address display in the Data Editor by removing the leading slash.
- Added support for the "prompt=false" connection property for DB2 for IBM i to turn off interactive prompts and prevent related errors.
- DuckDB:
- Fixed map rendering for GEOMETRY types with CRS parameters.
- Fixed geometry type display for database versions > 1.5.
- H2 database:
- Added new embedded driver version 2.4.2.
- Updated server driver to version 2.4.2.
- Firebird: Expanded database-specific SQL dialect coverage for the SQL Editor. Added more keywords, built-in functions, and SQL generators.
- MySQL: Fixed query boundaries recognition in the SQL Editor for scripts with the DELIMITER keyword.
- PostgreSQL:
- Updated driver to version 42.7.11
- Fixed missing INOUT parameters in CHECK generation for procedures and functions.
- Fixed handling of 24:xx values in time columns.
- Fixed an error when connecting via URL if the database name contains a hyphen.
- SQL Server:
- Fixed DDL generation for external tables.
- Fixed the database connectivity issue after the Java update.
- Added connection types to visually distinguish database connections using background colors across the Database Navigator and application UI.
- Improved SQL autocomplete suggestion ordering for object names. Now suggestions are sorted alphabetically.
- Added a Project Info tab containing the project description to the project context menu.
- Fixed synchronization between devices. Users see updated connections and folders in other devices without manual refreshes.
- Databases:
- ClickHouse
- The driver was updated to version 0.10.0
- Improved JSON display in the Value panel
- Added support for the Map data type visualization for the Data Editor
- The Databend driver was updated to version 0.4.8.
- LibSQL: Added support for database views.
- MySQL: Fixed support for comments starting with '#' symbol in the SQL Editor.
- PostgreSQL
- Updated the driver to version 42.7.13.
- Added a Show full DDL option for schemas and tables to generate complete DDL, including objects within a schema and object comments and privileges.
- Fixed an error that occurred when applying filters to database nodes
- Fixed generated calls for procedures with OUT parameters
- Security:
- Added an administrative setting to restrict SSH tunneling capabilities. Administrators can now limit tunnel configuration to authorized users, reducing the risk of unauthorized network access.
- Fixed the critical vulnerability (CVE-2025-62718) in the axios library. The library was updated to version 1.15.0.
- Fixed the high vulnerability (CVE-2026-4800) in the lodash library. The library was updated to version 4.18.0.
- Fixed the high vulnerability (CVE-2026-39363) in the vite library. The library was updated to version 7.3.2.
- Fixed the high vulnerability (CVE-2026-42035) in the axios library. The library was updated to version 1.15.1.
- Fixed the high vulnerability (CVE-2026-3505) in the bcpg-jdk18on library. The library was updated to version 1.84.0.
- Fixed the high vulnerability (CVE-2026-33671) in the picomatch library. The library was updated to version 2.3.2.
- Fixed the high vulnerability (CVE-2026-33943) in the happy-dom library. The library was updated to version 20.8.8.
- Fixed the high vulnerability (CVE-2025-45141) in the tar library. The library was updated to version 7.5.11.
- Fixed the high vulnerability (CVE-2026-27148) in the storybook library. The library was updated to version 10.2.10.
- Fixed the high vulnerability (CVE-2026-27606) in the rollup library. The library was updated to version 4.59.0.
- Fixed the high vulnerability (CVE-2026-27904) in the minimatch library. The library was updated to version 3.1.3.
- Fixed the high vulnerability (CVE-2026-27959) in the koa library. The library was updated to version 2.16.4.
- Fixed the high vulnerability (CVE-2026-32141) in the flatted library. The library was updated to version 4.4.0.
- Fixed the medium vulnerability (CVE-2026-41305) in the postcss library. The library was updated to version 8.5.14.
- Removed the ability to rename SQL scripts that start with dots to prevent them from being moved outside the Scripts directory into the root Tree.
- Added a new "Auto-create users" setting for the reverse proxy provider, allowing new user creation on login. It is enabled by default.
- Fixed a security flaw allowing unauthorized users to test database connections. The application now requires proper permissions for this action, preventing malicious requests and protecting server files.
- Fixed a security flaw in the LDAP authentication process. The application validates the username input to prevent unauthorized access and protect directory data.
- Fixed a cross-site scripting vulnerability caused by unsanitized user input in the web interface.
- Resolved a security flaw related to user-controlled file paths by adding strict validation to prevent unauthorized resource access.
- Updated session handling to prevent session fixation vulnerabilities. The application generates new session identifiers upon login.
- Updated the PostgreSQL JDBC driver to version 42.7.13, including a fix for a high-severity security vulnerability (CVE-2026-54291).
- Fixed the critical vulnerability (CVE-2026-59873) in the tar library. The library was updated to version 7.5.19.
- Fixed the critical vulnerability (CVE-2026-9277) in the shell-quote library (updated to version 1.8.4)
- Fixed the high vulnerability (CVE-2026-10050) in the jetty-security library. The library was updated to version 12.1.11.
- Fixed the high vulnerability (CVE-2026-14257) in the brace-expansion library. The library was updated to version 5.0.8.
- Fixed the high vulnerability (GHSA-r28c-9q8g-f849) in the postcss library. The library was updated to version 8.5.18.
- Fixed the high vulnerability (GHSA-pm4m-ph32-ghv5) in the js-yaml library. The library was updated to version 5.2.2.
- Fixed the high vulnerability (CVE-2026-59880) in the immutable library. The library was updated to version 5.1.8.
- Fixed the high vulnerability (CVE-2026-59879) in the fast-uri library. The library was updated to version 3.1.3.
- Fixed the high vulnerability (CVE-2026-13311) in the shell-quote library. The library was updated to version 1.9.0.
- Fixed the high vulnerability (GHSA-gcfj-64vw-6mp9) in the axios library. The library was updated to version 1.18.0.
- Fixed the high vulnerability (CVE-2026-67213) in the nanoid library. The library was updated to version 3.3.17.
- Fixed the high vulnerability (CVE-2025-71329) in the less library. The library was updated to version 4.7.0.
- Fixed the high vulnerability (CVE-2026-13697) in the undici library. The library was updated to version 8.9.0.
- Fixed the high vulnerability (CVE-2026-18446) in the fast-uri library. The library was updated to version 4.1.2.
- Fixed the high vulnerability (CVE-2026-69152) in the brace-expansion library. The library was updated to version 5.0.9.
- Fixed the high vulnerability (GHSA-gv7w-rqvm-qjhr) in the esbuild library. The library was updated to version 0.28.1.
- Fixed the high vulnerability (CVE-2026-12143) in the form-data library. The library was updated to version 4.0.6.
+1 -1
View File
@@ -18,7 +18,7 @@
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.13.2</version>
<version>2.14.0</version>
</dependency>
</dependencies>
+9 -1
View File
@@ -8,7 +8,15 @@ mkdir -p ./cloudbeaver/web
cd ../../cloudbeaver/webapp
yarn install --immutable
cd ./packages/product-default
yarn clear
cd common-react
yarn clear
cd ../common-typescript
yarn clear
cd ../packages/product-default
yarn run bundle
if [[ "$?" -ne 0 ]] ; then
-2
View File
@@ -9,8 +9,6 @@ RUN set -eux; \
apt-get update && \
apt-get dist-upgrade -y --no-install-recommends && \
apt-get install -y --no-install-recommends \
# curl required for historical reasons, see https://github.com/adoptium/containers/issues/255
curl \
# java.lang.UnsatisfiedLinkError: libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory
# java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11FontManager
# https://github.com/docker-library/openjdk/pull/235#issuecomment-424466077
+1 -1
View File
@@ -17,6 +17,7 @@ If Not Defined JAVA_OPTS (
java %JAVA_OPTS% ^
-Dfile.encoding=UTF-8 ^
--enable-native-access=ALL-UNNAMED ^
--add-modules=ALL-DEFAULT ^
--add-opens=java.base/java.io=ALL-UNNAMED ^
--add-opens=java.base/java.lang=ALL-UNNAMED ^
@@ -33,7 +34,6 @@ java %JAVA_OPTS% ^
--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED ^
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED ^
--add-opens=java.base/sun.security.ssl=ALL-UNNAMED ^
--add-opens=java.base/sun.security.action=ALL-UNNAMED ^
--add-opens=java.base/sun.security.util=ALL-UNNAMED ^
--add-opens=java.security.jgss/sun.security.jgss=ALL-UNNAMED ^
--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED ^
+1 -1
View File
@@ -11,6 +11,7 @@ echo "Starting Cloudbeaver Server"
exec java ${JAVA_OPTS} \
-Dfile.encoding=UTF-8 \
--enable-native-access=ALL-UNNAMED \
--add-modules=ALL-DEFAULT \
--add-opens=java.base/java.io=ALL-UNNAMED \
--add-opens=java.base/java.lang=ALL-UNNAMED \
@@ -27,7 +28,6 @@ exec java ${JAVA_OPTS} \
--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED \
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED \
--add-opens=java.base/sun.security.ssl=ALL-UNNAMED \
--add-opens=java.base/sun.security.action=ALL-UNNAMED \
--add-opens=java.base/sun.security.util=ALL-UNNAMED \
--add-opens=java.security.jgss/sun.security.jgss=ALL-UNNAMED \
--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED \
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Model
Bundle-SymbolicName: io.cloudbeaver.model.cli;singleton:=true
Bundle-Version: 1.0.13.qualifier
Bundle-Release-Date: 20260706
Bundle-Version: 1.0.18.qualifier
Bundle-Release-Date: 20260921
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.model.cli;visibility:=reexport
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.model.cli</artifactId>
<version>1.0.13-SNAPSHOT</version>
<version>1.0.18-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,21 +3,20 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Model
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
Bundle-Version: 1.0.104.qualifier
Bundle-Release-Date: 20260706
Bundle-Version: 1.0.109.qualifier
Bundle-Release-Date: 20260921
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
org.jkiss.dbeaver.model;visibility:=reexport,
org.jkiss.dbeaver.model.sm;visibility:=reexport,
org.jkiss.dbeaver.model.event;visibility:=reexport,
org.jkiss.dbeaver.model.nio;visibility:=reexport,
org.jkiss.dbeaver.registry;visibility:=reexport,
org.jkiss.dbeaver.net.ssh,
org.jkiss.dbeaver.net.ssh;visibility:=reexport,
org.jkiss.bundle.logback;visibility:=reexport,
org.jkiss.bundle.graphql;visibility:=reexport,
org.jkiss.bundle.apache.dbcp,
jakarta.servlet-api;bundle-version:="6.0.0";visibility:=reexport
jakarta.servlet-api;visibility:=reexport
Export-Package: io.cloudbeaver,
io.cloudbeaver.auth,
io.cloudbeaver.auth.provider,
@@ -13,7 +13,7 @@ meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.smDbProductName.
meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.smDbProductVersion.name=Security manager database product version
meta.io.cloudbeaver.model.WebExpertSettingsProperties.readOnly.name = Read-only connection
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommit.name = Auto-commit
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommitMode.name = Auto-commit mode
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.name = Keep-alive interval (seconds)
meta.io.cloudbeaver.model.WebExpertSettingsProperties.defaultSchema.name = Default schema
meta.io.cloudbeaver.model.WebExpertSettingsProperties.defaultCatalog.name = Default catalog
@@ -1,5 +1,5 @@
meta.io.cloudbeaver.model.WebExpertSettingsProperties.readOnly.name = Только для чтения
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommit.name = Авто-коммит
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommitMode.name = Авто-коммит
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.name = Интервал Keep-alive (в секундах)
meta.io.cloudbeaver.model.WebExpertSettingsProperties.defaultSchema.name = Схема по умолчанию
meta.io.cloudbeaver.model.WebExpertSettingsProperties.defaultCatalog.name = Каталог по умолчанию
@@ -1,4 +1,4 @@
meta.io.cloudbeaver.model.WebExpertSettingsProperties.readOnly.name = Kết nối chỉ đọc
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommit.name = Tự động commit
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommitMode.name = Tự động commit
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.name = Giữ kết nối (tính bằng giây)
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.description = Không tự động ngắt kết nối
@@ -1,3 +1,3 @@
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommit.name = 自动提交
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommitMode.name = 自动提交
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.name = 保持连接 (秒)
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.description = 无自动断开连接
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.model</artifactId>
<version>1.0.104-SNAPSHOT</version>
<version>1.0.109-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.
@@ -20,6 +20,7 @@ import graphql.ErrorClassification;
import graphql.ErrorType;
import graphql.GraphQLError;
import graphql.language.SourceLocation;
import org.jkiss.dbeaver.DBDatabaseException;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.sql.SQLState;
import org.jkiss.utils.CommonUtils;
@@ -146,19 +147,22 @@ public class DBWebException extends DBException implements GraphQLError {
} else {
return message;
}
} else if (cause.getMessage() == null && cause.getCause() != null) {
return message + ":\n" + cause.getCause().getClass().getSimpleName();
}
// Preserve enriched web/database messages, but unwrap generic exceptions to their most specific cause.
Throwable messageCause = cause instanceof DBWebException || cause instanceof DBDatabaseException
? cause
: CommonUtils.getRootCause(cause);
String causeMessage = messageCause.getMessage();
if (CommonUtils.isEmpty(causeMessage)) {
causeMessage = messageCause.getClass().getSimpleName();
}
if (CommonUtils.isEmpty(message)) {
if (cause.getMessage() != null) {
return cause.getMessage();
}
return cause.getClass().getName();
return causeMessage;
}
if (CommonUtils.equalObjects(message, cause.getMessage())) {
if (CommonUtils.equalObjects(message, causeMessage)) {
return message;
}
return message + ":\n" + cause.getMessage();
return message + ":\n" + causeMessage;
}
}
@@ -77,9 +77,7 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
}
dataSource.setFolder(input.getFolder() != null ? registry.getFolder(input.getFolder()) : null);
if (input.isDefaultAutoCommit() != null) {
dataSource.getConnectionConfiguration().getBootstrap().setDefaultAutoCommit(input.isDefaultAutoCommit());
}
dataSource.getConnectionConfiguration().getBootstrap().setDefaultAutoCommit(input.isDefaultAutoCommit());
WebDataSourceUtils.setConnectionConfiguration(
dataSource.getDriver(),
dataSource.getConnectionConfiguration(),
@@ -367,6 +367,7 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
dataSourceRegistry.initializeDataSources();
}
@Nullable
@Override
public DataSourceParseResults loadDataSources(
@NotNull List<DBPDataSourceConfigurationStorage> storages,
@@ -382,6 +383,7 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
dataSourceRegistry.saveDataSources();
}
@NotNull
@Override
public DataSourceConfigurationManager getConfigurationManager() {
return dataSourceRegistry.getConfigurationManager();
@@ -103,7 +103,7 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
return projectSettings;
}
@Nullable
@NotNull
@Override
public DBNModel getNavigatorModel() {
return webSession.getNavigatorModel();
@@ -135,8 +135,11 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
if (registryIsLoaded) {
return;
}
getDataSourceRegistry().getDataSources().forEach(this::addConnection);
Throwable lastError = getDataSourceRegistry().getLastError();
DBPDataSourceRegistry dataSourceRegistry = getDataSourceRegistry();
// Connection node paths are resolved through the navigator model, so materialize its lazy database branch first.
webSession.initializeProjectNavigator(this);
dataSourceRegistry.getDataSources().forEach(this::addConnection);
Throwable lastError = dataSourceRegistry.getLastError();
if (lastError != null) {
webSession.addSessionError(lastError);
log.error("Error refreshing connections from project '" + getId() + "'", lastError);
@@ -237,6 +240,18 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
public synchronized boolean updateProjectDataSources(@NotNull WSDataSourceEvent event) {
var sendDataSourceUpdatedEvent = false;
DBPDataSourceRegistry registry = getDataSourceRegistry();
Map<String, DataSourceDescriptor> dataSourceSnapshots = new HashMap<>();
if (WSDataSourceEvent.UPDATED.equals(event.getId())) {
for (String dsId : event.getDataSourceIds()) {
DBPDataSourceContainer dataSource = registry.getDataSource(dsId);
if (dataSource instanceof DataSourceDescriptor descriptor) {
DBPDataSourceContainer snapshot = registry.createDataSource(descriptor);
if (snapshot instanceof DataSourceDescriptor snapshotDescriptor) {
dataSourceSnapshots.put(dsId, snapshotDescriptor);
}
}
}
}
if (WSDataSourceEvent.CREATED.equals(event.getId()) || WSDataSourceEvent.UPDATED.equals(event.getId())) {
registry.refreshConfig(event.getDataSourceIds());
}
@@ -251,6 +266,10 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
sendDataSourceUpdatedEvent = true;
}
case WSDataSourceEvent.UPDATED -> {
DataSourceDescriptor snapshot = dataSourceSnapshots.get(dsId);
if (snapshot != null && !event.getProperty().hasEffectiveChanges(snapshot, ds)) {
continue;
}
if (event.getProperty() == WSDataSourceProperty.CONFIGURATION) {
WebDataSourceUtils.disconnectDataSource(webSession, ds, true);
}
@@ -357,6 +376,13 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
return getInputConfigHandler(configInput).createDataSourceContainer();
}
public void updateDataSourceContainerFromInput(
@NotNull WebConnectionConfig configInput,
@NotNull DataSourceDescriptor dataSource
) throws DBWebException {
getInputConfigHandler(configInput).updateDataSource(dataSource);
}
@NotNull
public WebConnectionConfig getConnectionConfigInput(@Nullable Map<String, Object> configMap) {
return new WebConnectionConfig(configMap == null ? Map.of() : configMap);
@@ -69,6 +69,13 @@ public interface SMAuthProviderExternal<AUTH_SESSION extends SMSession> extends
@NotNull WebUser user,
boolean selfIdentity) throws DBException;
/**
* Whether a user that is not found should be provisioned (auto-created) on external login.
*/
default boolean isAutoUserProvisioningEnabled(@Nullable SMAuthProviderCustomConfiguration providerConfig) {
return true;
}
/**
* Make some post authentication actions
*/
@@ -0,0 +1,62 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.utils.CommonUtils;
public enum AutoCommitMode {
DEFAULT("Default", null),
AUTO_COMMIT("Auto commit", true),
MANUAL_COMMIT("Manual commit", false);
private final String title;
private final Boolean value;
AutoCommitMode(@NotNull String title, @Nullable Boolean value) {
this.title = title;
this.value = value;
}
@NotNull
@Override
public String toString() {
return title;
}
@Nullable
public Boolean getValue() {
return value;
}
@NotNull
public static AutoCommitMode fromValue(@Nullable Boolean value) {
if (value == null) {
return DEFAULT;
}
return value ? AUTO_COMMIT : MANUAL_COMMIT;
}
@Nullable
public static Boolean from(@Nullable String mode) {
if (CommonUtils.isEmpty(mode)) {
return null;
}
return valueOf(mode).getValue();
}
}
@@ -65,6 +65,7 @@ public class WebConnectionConfig {
private Boolean defaultAutoCommit;
private String defaultCatalogName;
private String defaultSchemaName;
private String connectionType;
@NotNull
private Map<String, String> defaultUserPreferences = new LinkedHashMap<>();
@@ -102,8 +103,14 @@ public class WebConnectionConfig {
expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_KEEP_ALIVE_INTERVAL, -1);
readOnly = JSONUtils.getBoolean(
expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_READ_ONLY);
defaultAutoCommit = JSONUtils.getBoolean(
expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_AUTO_COMMIT, true);
if (expertSettingsValues != null) {
defaultAutoCommit = AutoCommitMode.from(
JSONUtils.getString(expertSettingsValues, WebExpertSettingsProperties.PROP_AUTO_COMMIT_MODE));
} else {
defaultAutoCommit = JSONUtils.getBoolean(params, WebExpertSettingsProperties.PROP_AUTO_COMMIT, true);
}
defaultCatalogName = JSONUtils.getString(
expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_DEFAULT_CATALOG);
defaultSchemaName = JSONUtils.getString(
@@ -120,6 +127,7 @@ public class WebConnectionConfig {
for (Map<String, Object> nhc : JSONUtils.getObjectList(params, "networkHandlersConfig")) {
networkHandlersConfig.add(new WebNetworkHandlerConfigInput(nhc));
}
connectionType = JSONUtils.getString(params, "connectionType");
}
@Property
@@ -381,4 +389,13 @@ public class WebConnectionConfig {
public void setDefaultUserPreferences(@NotNull Map<String, String> defaultUserPreferences) {
this.defaultUserPreferences = defaultUserPreferences;
}
@Nullable
public String getConnectionType() {
return connectionType;
}
public void setConnectionType(@Nullable String connectionType) {
this.connectionType = connectionType;
}
}
@@ -72,6 +72,11 @@ public class WebConnectionInfo {
private static final String FEATURE_PROVIDED = "provided";
private static final String FEATURE_MANAGEABLE = "manageable";
private static final String FEATURE_RESTRICT_DATA_EDIT = "restrictDataEdit";
private static final String FEATURE_RESTRICT_SCRIPT_EXECUTE = "restrictScriptExecute";
private static final String FEATURE_RESTRICT_DATA_IMPORT = "restrictDataImport";
private static final String FEATURE_RESTRICT_METADATA_EDIT = "restrictMetadataEdit";
private static final String TOOL_SESSION_MANAGER = "sessionManager";
private final WebSession session;
@@ -275,6 +280,18 @@ public class WebConnectionInfo {
if (dataSourceContainer.isConnectionReadOnly()) {
features.add(FEATURE_READ_ONLY);
}
if (!dataSourceContainer.hasModifyPermission(DBPDataSourcePermission.PERMISSION_EDIT_DATA)) {
features.add(FEATURE_RESTRICT_DATA_EDIT);
}
if (!dataSourceContainer.hasModifyPermission(DBPDataSourcePermission.PERMISSION_EXECUTE_SCRIPTS)) {
features.add(FEATURE_RESTRICT_SCRIPT_EXECUTE);
}
if (!dataSourceContainer.hasModifyPermission(DBPDataSourcePermission.PERMISSION_IMPORT_DATA)) {
features.add(FEATURE_RESTRICT_DATA_IMPORT);
}
if (!dataSourceContainer.hasModifyPermission(DBPDataSourcePermission.PERMISSION_EDIT_METADATA)) {
features.add(FEATURE_RESTRICT_METADATA_EDIT);
}
if (dataSourceContainer.isProvided()) {
features.add(FEATURE_PROVIDED);
}
@@ -467,7 +484,7 @@ public class WebConnectionInfo {
@Property
public Map<String, Object> getExpertSettingsValues() {
Map<String, Object> expertSettings = new LinkedHashMap<>();
expertSettings.put(WebExpertSettingsProperties.PROP_AUTO_COMMIT, isAutocommit());
expertSettings.put(WebExpertSettingsProperties.PROP_AUTO_COMMIT_MODE, AutoCommitMode.fromValue(isAutocommit()));
expertSettings.put(WebExpertSettingsProperties.PROP_KEEP_ALIVE_INTERVAL, getKeepAliveInterval());
expertSettings.put(WebExpertSettingsProperties.PROP_READ_ONLY, isReadOnly());
expertSettings.put(WebExpertSettingsProperties.PROP_DEFAULT_CATALOG, getDefaultCatalogName());
@@ -541,12 +558,9 @@ public class WebConnectionInfo {
}
@Property
public boolean isAutocommit() {
Boolean isAutoCommit = dataSourceContainer.getConnectionConfiguration().getBootstrap().getDefaultAutoCommit();
if (isAutoCommit == null) {
return true;
}
return isAutoCommit;
@Nullable
public Boolean isAutocommit() {
return dataSourceContainer.getConnectionConfiguration().getBootstrap().getDefaultAutoCommit();
}
@Property
@@ -586,6 +600,11 @@ public class WebConnectionInfo {
return tools;
}
@NotNull
public String getConnectionType() {
return dataSourceContainer.getConnectionConfiguration().getConnectionType().getId();
}
/**
* Updates param that checks whether credentials were saved only in session.
*/
@@ -593,4 +612,13 @@ public class WebConnectionInfo {
this.credentialsSavedInSession = credentialsSavedInSession;
}
/**
* Database-dependent driver configuration used to drive data import
*/
@Property
@NotNull
public WebDriverConfiguration getDriverConfiguration() {
return new WebDriverConfiguration(dataSourceContainer);
}
}
@@ -0,0 +1,58 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.meta.Property;
/**
* Generic id/name/description dictionary value.
*/
public class WebDictionaryValueInfo {
@NotNull
private final String id;
@NotNull
private final String name;
@Nullable
private final String description;
public WebDictionaryValueInfo(@NotNull String id, @NotNull String name, @Nullable String description) {
this.id = id;
this.name = name;
this.description = description;
}
@Property
@NotNull
public String getId() {
return id;
}
@Property
@NotNull
public String getName() {
return name;
}
@Property
@Nullable
public String getDescription() {
return description;
}
}
@@ -0,0 +1,75 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.DBPDataSource;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBUtils;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.sql.SQLDialectInsertReplaceMethod;
import org.jkiss.dbeaver.model.sql.SQLDialectMetadata;
import org.jkiss.dbeaver.model.struct.DBSDataBulkLoader;
import org.jkiss.utils.CommonUtils;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class WebDriverConfiguration {
@NotNull
private final DBPDataSourceContainer dataSourceContainer;
public WebDriverConfiguration(@NotNull DBPDataSourceContainer dataSourceContainer) {
this.dataSourceContainer = dataSourceContainer;
}
/**
* 'On duplicate key' replace methods supported by this connection's SQL dialect
*/
@Property
@NotNull
public List<WebDictionaryValueInfo> getSupportedInsertReplaceMethods() {
SQLDialectMetadata dialect = dataSourceContainer.getDriver().getScriptDialect();
List<SQLDialectInsertReplaceMethod> replaceMethods = dialect.getSupportedInsertReplaceMethodsDescriptors();
if (CommonUtils.isEmpty(replaceMethods)) {
return Collections.emptyList();
}
return replaceMethods.stream()
.map(method -> new WebDictionaryValueInfo(method.getId(), method.getLabel(), method.getDescription()))
.collect(Collectors.toList());
}
/**
* Whether the connection's database supports bulk load during data import
*/
@Property
public boolean isSupportsBulkLoad() {
DBPDataSource dataSource = dataSourceContainer.getDataSource();
return dataSource != null && DBUtils.getAdapter(DBSDataBulkLoader.class, dataSource) != null;
}
/**
* Whether the connection's database supports transaction
*/
@Property
public boolean isSupportsTransactions() {
DBPDataSource dataSource = dataSourceContainer.getDataSource();
return dataSource != null && dataSource.getInfo().supportsTransactions();
}
}
@@ -1,3 +1,19 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model;
import org.jkiss.code.NotNull;
@@ -5,6 +21,7 @@ import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.DBPObject;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.connection.DBPDriverConstants;
import org.jkiss.dbeaver.model.meta.IPropertyValueListProvider;
import org.jkiss.dbeaver.model.meta.IPropertyValueValidator;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.utils.CommonUtils;
@@ -15,6 +32,7 @@ import org.jkiss.utils.CommonUtils;
public class WebExpertSettingsProperties implements DBPObject {
public static final String PROP_READ_ONLY = "readOnly";
public static final String PROP_AUTO_COMMIT = "autocommit";
public static final String PROP_AUTO_COMMIT_MODE = "autoCommitMode";
public static final String PROP_KEEP_ALIVE_INTERVAL = "keepAliveInterval";
public static final String PROP_DEFAULT_CATALOG = "defaultCatalogName";
public static final String PROP_DEFAULT_SCHEMA = "defaultSchemaName";
@@ -30,9 +48,15 @@ public class WebExpertSettingsProperties implements DBPObject {
return 0;
}
@Property(order = 2, id = PROP_AUTO_COMMIT, visibleIf = AutoCommitFieldValidator.class)
public boolean isAutoCommit() {
return true;
@NotNull
@Property(
order = 2,
id = PROP_AUTO_COMMIT_MODE,
visibleIf = AutoCommitFieldValidator.class,
listProvider = AutoCommitListProvider.class
)
public AutoCommitMode isAutoCommitMode() {
return AutoCommitMode.DEFAULT;
}
@Property(order = 3, id = PROP_READ_ONLY, visibleIf = ReadOnlyFieldValidator.class)
@@ -40,11 +64,13 @@ public class WebExpertSettingsProperties implements DBPObject {
return false;
}
@Nullable
@Property(order = 4, id = PROP_DEFAULT_CATALOG, visibleIf = DefaultCatalogFieldVisibleValidator.class)
public String getDefaultCatalog() {
return null;
}
@Nullable
@Property(order = 5, id = PROP_DEFAULT_SCHEMA, visibleIf = DefaultSchemaFieldVisibleValidator.class)
public String getDefaultSchema() {
return null;
@@ -85,4 +111,18 @@ public class WebExpertSettingsProperties implements DBPObject {
return CommonUtils.toBoolean(object.driver.getDriverParameter(DBPDriverConstants.PARAM_SUPPORTS_SCHEMA_SELECTION), true);
}
}
public static class AutoCommitListProvider implements IPropertyValueListProvider<WebExpertSettingsProperties> {
@Override
public boolean allowCustomValue() {
return false;
}
@Nullable
@Override
public Object[] getPossibleValues(@Nullable WebExpertSettingsProperties object) {
return AutoCommitMode.values();
}
}
}
@@ -27,6 +27,7 @@ import org.jkiss.dbeaver.model.impl.PropertyDescriptor;
import org.jkiss.dbeaver.model.impl.ProviderPropertyDescriptor;
import org.jkiss.dbeaver.model.meta.IPropertyValueListProvider;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.meta.PropertyConstraints;
import org.jkiss.dbeaver.model.meta.PropertyLength;
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
import org.jkiss.dbeaver.model.preferences.DBPPropertySource;
@@ -42,63 +43,58 @@ import java.util.*;
* Web connection info
*/
public class WebPropertyInfo {
private WebSession session;
private DBPPropertyDescriptor property;
private DBPPropertySource propertySource;
private boolean showProtected;
@NotNull
private final WebSession session;
@NotNull
private final DBPPropertyDescriptor property;
@Nullable
private final DBPPropertySource propertySource;
private Object[] validValues;
private String[] supportedConfigurationTypes = new String[0];
private Object defaultValue;
public WebPropertyInfo(WebSession session, DBPPropertyDescriptor property, DBPPropertySource propertySource) {
public WebPropertyInfo(
@NotNull WebSession session,
@NotNull DBPPropertyDescriptor property,
@Nullable DBPPropertySource propertySource
) {
this.session = session;
this.property = property;
this.propertySource = propertySource;
}
public WebPropertyInfo(WebSession session, DBPPropertyDescriptor property) {
this.session = session;
this.property = property;
}
public boolean isShowProtected() {
return showProtected;
}
public void setShowProtected(boolean showProtected) {
this.showProtected = showProtected;
public WebPropertyInfo(@NotNull WebSession session, @NotNull DBPPropertyDescriptor property) {
this(session, property, null);
}
///////////////////////////////////
// General properties
///////////////////////////////////
@NotNull
@Property
public String getId() {
return CommonUtils.toString(property.getId());
}
@NotNull
@Property
public String getDisplayName() {
if (property instanceof DBPNamedObjectLocalized) {
return ((DBPNamedObjectLocalized) property).getLocalizedName(session.getLocale());
if (property instanceof DBPNamedObjectLocalized localized) {
return localized.getLocalizedName(session.getLocale());
} else {
return property.getDisplayName();
}
}
@Nullable
@Property
public String getDescription() {
if (property instanceof DBPObjectWithDescriptionLocalized) {
return ((DBPObjectWithDescriptionLocalized) property).getLocalizedDescription(session.getLocale());
if (property instanceof DBPObjectWithDescriptionLocalized localized) {
return localized.getLocalizedDescription(session.getLocale());
} else {
return property.getDescription();
}
}
@Nullable
@Property
public String getHint() {
return property.getHint();
@@ -109,38 +105,53 @@ public class WebPropertyInfo {
return property instanceof ObjectPropertyDescriptor ? ((ObjectPropertyDescriptor) property).getOrderNumber() : -1;
}
@Nullable
@Property
public String getCategory() {
return property.getCategory();
}
@Nullable
@Property
public String getType() {
return getDataType();
}
@Nullable
@Property
public String getDataType() {
Class<?> dataType = property.getDataType();
return dataType == null ? null : dataType.getSimpleName();
}
@NotNull
@Property
public PropertyLength getLength() {
return property.getLength();
}
@Nullable
@Property
public Object getDefaultValue() throws DBException {
var defaultValue = property.getDefaultValue() == null ? this.defaultValue : property.getDefaultValue();
return defaultValue == null ? getValue() : defaultValue;
Object defaultValue = property.getDefaultValue();
if (property instanceof ObjectPropertyDescriptor && defaultValue == null) {
return getValue();
}
return defaultValue;
}
@Nullable
@Property
public PropertyConstraints getConstraints() {
return property instanceof ObjectPropertyDescriptor descriptor ? descriptor.getConstraints() : null;
}
@Nullable
@Property
public Object getValue() throws DBException {
Object value = propertySource == null ? null : propertySource.getPropertyValue(session.getProgressMonitor(), property.getId());
if (property instanceof ObjectPropertyDescriptor opd &&
(!showProtected && opd.isPassword() || opd.isHidden())
(opd.isPassword() || opd.isHidden())
) {
return maskValue(value);
} else if (property instanceof PropertyDescriptor pd) {
@@ -152,6 +163,7 @@ public class WebPropertyInfo {
return value == null ? null : makePropertyValue(value);
}
@Nullable
@Property
public Object[] getValidValues() {
if (property instanceof IPropertyValueListProvider) {
@@ -164,17 +176,18 @@ public class WebPropertyInfo {
}
return validValues;
}
return validValues;
}
return validValues;
return null;
}
@NotNull
@Property
public String[] getFeatures() {
String[] features = property.getFeatures();
return features == null ? new String[0] : features;
}
@NotNull
@Property
public String[] getSupportedConfigurationTypes() {
if (property instanceof ProviderPropertyDescriptor) {
@@ -182,7 +195,7 @@ public class WebPropertyInfo {
.map(DBPDriverConfigurationType::toString)
.toArray(String[]::new);
}
return supportedConfigurationTypes;
return new String[0];
}
@Property
@@ -203,12 +216,14 @@ public class WebPropertyInfo {
return false;
}
@NotNull
@Override
public String toString() {
return CommonUtils.toString(property.getId());
}
private Object makePropertyValue(Object value) {
@Nullable
private Object makePropertyValue(@Nullable Object value) {
if (value == null) {
return null;
// } else if (value instanceof DBSObject) {
@@ -290,21 +305,6 @@ public class WebPropertyInfo {
return conditions;
}
//TODO: delete after refactoring on front-end
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
//TODO: delete after refactoring on front-end
public void setValidValues(Object[] validValues) {
this.validValues = validValues;
}
//TODO: delete after refactoring on front-end
public void setSupportedConfigurationTypes(String[] supportedConfigurationTypes) {
this.supportedConfigurationTypes = supportedConfigurationTypes;
}
public record Condition(@NotNull String expression, @NotNull Type conditionType) {
public enum Type {
HIDE,
@@ -37,7 +37,6 @@ import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
import org.jkiss.dbeaver.model.rm.RMController;
import org.jkiss.dbeaver.model.secret.DBSSecretController;
import org.jkiss.dbeaver.model.websocket.event.WSEventController;
import org.jkiss.dbeaver.runtime.IVariableResolver;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.dbeaver.utils.RuntimeUtils;
@@ -89,12 +88,6 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
return new SharedFileLockManager(getApplicationInstanceId());
}
@Nullable
@Override
public Path getDefaultWorkingFolder() {
return getServerConfigurationController().getWorkspacePath();
}
@Override
public boolean isHeadlessMode() {
return true;
@@ -192,20 +185,20 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
@SuppressWarnings("unchecked")
public static void patchConfigurationWithProperties(
Map<String, Object> configProps, IVariableResolver varResolver
@NotNull Map<String, Object> configProps,
@NotNull IVariableResolver varResolver
) {
for (Map.Entry<String, Object> entry : configProps.entrySet()) {
Object propValue = entry.getValue();
if (propValue instanceof String) {
entry.setValue(GeneralUtils.replaceVariables((String) propValue, varResolver));
if (propValue instanceof String strValue) {
entry.setValue(GeneralUtils.replaceVariables(strValue, varResolver));
} else if (propValue instanceof Map) {
patchConfigurationWithProperties((Map<String, Object>) propValue, varResolver);
} else if (propValue instanceof List) {
List value = (List) propValue;
} else if (propValue instanceof List value) {
for (int i = 0; i < value.size(); i++) {
Object colItem = value.get(i);
if (colItem instanceof String) {
value.set(i, GeneralUtils.replaceVariables((String) colItem, varResolver));
if (colItem instanceof String strItem) {
value.set(i, GeneralUtils.replaceVariables(strItem, varResolver));
} else if (colItem instanceof Map) {
patchConfigurationWithProperties((Map<String, Object>) colItem, varResolver);
}
@@ -218,6 +211,8 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
@Override
public Object start(IApplicationContext context) {
initializeApplicationServices();
setWorkspacePath(getServerConfigurationController().getWorkspacePath());
try {
startServer();
} catch (Exception e) {
@@ -255,27 +250,17 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
}
@NotNull
@Override
public Path getWorkspaceDirectory() {
return getServerConfigurationController().getWorkspacePath();
}
public String getApplicationId() {
try {
return getApplicationInstanceId();
} catch (DBException e) {
return null;
// Fallback to hash code
return getClass().getSimpleName() + "#" + hashCode();
}
}
@NotNull
@Override
public WSEventController getEventController() {
return null;
}
public abstract ServletServerConfigurationController getServerConfigurationController();
public abstract ServletServerConfigurationController<?> getServerConfigurationController();
@Override
public boolean isEnvironmentVariablesAccessible() {
@@ -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.
@@ -53,9 +53,6 @@ public interface ServletApplication extends DBPApplication {
@NotNull
Path getDataDirectory(boolean create);
@NotNull
Path getWorkspaceDirectory();
@NotNull
Path getHomeDirectory();
@@ -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.
@@ -23,6 +23,7 @@ import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
public interface ServletAuthApplication extends ServletApplication {
@NotNull
ServletAuthConfiguration getAuthConfiguration();
@Nullable
@@ -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,8 +67,7 @@ public abstract class BaseLocalResourceController implements RMController {
@Nullable
@Override
public RMProject getProject(@NotNull String projectId, boolean readResources, boolean readProperties)
throws DBException {
public RMProject getProject(@NotNull String projectId, boolean readResources, boolean readProperties) throws DBException {
RMProject project = makeProjectFromId(projectId, true);
if (project == null) {
return null;
@@ -110,8 +109,7 @@ public abstract class BaseLocalResourceController implements RMController {
}
@Override
public String getProjectsDataSources(@NotNull String projectId, @Nullable String[] dataSourceIds)
throws DBException {
public String getProjectsDataSources(@NotNull String projectId, @Nullable String[] dataSourceIds) throws DBException {
DBPProject projectMetadata = getWebProject(projectId, false);
return doFileReadOperation(
projectId,
@@ -40,6 +40,7 @@ import org.jkiss.dbeaver.model.fs.lock.LockTarget;
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
import org.jkiss.dbeaver.model.navigator.DBNLocalFolder;
import org.jkiss.dbeaver.model.net.DBWNetworkProfile;
import org.jkiss.dbeaver.model.rm.*;
import org.jkiss.dbeaver.model.security.SMAdminController;
import org.jkiss.dbeaver.model.sql.DBQuotaException;
@@ -48,6 +49,7 @@ import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
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.datasource.WSNetworkProfileEvent;
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
import org.jkiss.dbeaver.registry.DataSourceParseResults;
import org.jkiss.dbeaver.registry.ResourceTypeDescriptor;
@@ -407,8 +409,13 @@ public class LocalResourceController extends BaseLocalResourceController {
registry::createDataSource
)
);
Map<String, DBWNetworkProfile> oldNetworkProfiles = registry.getNetworkProfiles().getProfiles().stream()
.collect(Collectors.toMap(
DBWNetworkProfile::getProfileId,
p -> p
));
DataSourceParseResults parseResults = super.updateProjectDataSourcesConfig(projectId, configuration, dataSourceIds);
sendDataSourcesConfigUpdatedEvent(registry, oldDataSources, parseResults);
sendConfigUpdatedEvent(registry, oldDataSources, oldNetworkProfiles, parseResults);
return parseResults != null;
}
@@ -501,14 +508,48 @@ public class LocalResourceController extends BaseLocalResourceController {
return DBNLocalFolder.makeLocalFolderItemPath(projectId, folderPath);
}
private void sendDataSourcesConfigUpdatedEvent(
private void sendConfigUpdatedEvent(
@NotNull DBPDataSourceRegistry registry,
@NotNull Map<String, DataSourceDescriptor> oldDataSources,
@NotNull Map<String, DBWNetworkProfile> oldNetworkProfiles,
@Nullable DataSourceParseResults parseResults
) {
if (parseResults == null || credentialsProvider.getActiveUserCredentials() == null || oldDataSources.isEmpty()) {
if (parseResults == null || credentialsProvider.getActiveUserCredentials() == null) {
return;
}
boolean profilesChanged = !parseResults.removedProfiles.isEmpty();
// We don't need to check for updated profiles if there are already added or removed profiles
if (!profilesChanged) {
for (DBWNetworkProfile updatedProfile : parseResults.updatedProfiles) {
if (oldNetworkProfiles.containsKey(updatedProfile.getProfileId())) {
DBWNetworkProfile oldProfile = oldNetworkProfiles.get(updatedProfile.getProfileId());
if (!oldProfile.equalConfigurations(updatedProfile)) {
profilesChanged = true;
break;
}
} else {
// profile was added
profilesChanged = true;
break;
}
}
}
if (profilesChanged) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSNetworkProfileEvent.update(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
registry.getProject().getId()
)
);
}
if (oldDataSources.isEmpty()) {
return;
}
List<String> updatedConfigurationDataSourceIds = new ArrayList<>();
List<String> updatedNameDataSourceIds = new ArrayList<>();
List<String> updatedInternalConfigurationDataSourceIds = new ArrayList<>();
@@ -612,7 +653,6 @@ public class LocalResourceController extends BaseLocalResourceController {
)
);
}
}
@NotNull
@@ -988,8 +1028,24 @@ public class LocalResourceController extends BaseLocalResourceController {
try {
while (resourcePath.startsWith("/")) resourcePath = resourcePath.substring(1);
Path targetPath = projectPath.resolve(resourcePath).normalize();
if (!targetPath.startsWith(projectPath)) {
throw new DBException("Invalid resource path");
// hack for custom path implementations that returns / as root path
Iterator<Path> projectParts = projectPath.iterator();
Iterator<Path> targetParts = targetPath.iterator();
Iterator<Path> unnormalizedTargetParts = projectPath.resolve(resourcePath).iterator();
while (projectParts.hasNext()) {
if (!targetParts.hasNext()) {
throw new DBException("Invalid resource path");
}
Path projectPart = projectParts.next();
Path targetPart = targetParts.next();
unnormalizedTargetParts.next();
if (!projectPart.equals(targetPart)) {
throw new DBException("Invalid resource path");
}
}
// validating unnormalized target parts to avoid invalid characters in resource names
while (unnormalizedTargetParts.hasNext()) {
GeneralUtils.validateResourceNameUnconditionally(unnormalizedTargetParts.next().toString());
}
return targetPath;
} catch (InvalidPathException e) {
@@ -123,6 +123,16 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
}
}
public void migrateEventHandlersTo(@NotNull BaseWebSession target) {
synchronized (sessionEventHandlers) {
for (CBWebSessionEventHandler handler : sessionEventHandlers) {
handler.migrateToSession(target);
target.addEventHandler(handler);
}
sessionEventHandlers.clear();
}
}
public boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
return userContext.refresh(smAuthInfo);
}
@@ -145,6 +155,17 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
}
}
/**
* Refreshes user permissions, teams and accessible projects.
* <p>
* Unlike {@link #refreshUserData()} this method must not re-create heavyweight session state
* (navigator model, session projects, connection caches), so it is safe to call
* for foreign sessions on server-wide events.
*/
public void refreshUserPermissions() {
refreshUserData();
}
@NotNull
@Override
public SMAuthSpace getSessionSpace() {
@@ -262,12 +262,54 @@ public class WebSession extends BaseWebSession
@Override
public void refreshUserData() {
super.refreshUserData();
refreshSessionAuth();
refreshUserPermissions();
initNavigatorModel();
}
@Override
public void refreshUserPermissions() {
super.refreshUserData();
refreshSessionAuth();
if (getUserId() == null && globalProject != null) {
// refreshSessionAuth() updates accessible connections for named users only,
// for anonymous sessions this was done by initNavigatorModel()
globalProject.refreshAccessibleConnectionIds();
}
syncSessionProjects();
}
/**
* Adds/removes only those session projects whose accessibility actually changed.
* <p>
* Unlike {@link #initNavigatorModel()} this keeps existing projects, and therefore
* their live connections and SQL contexts, intact.
*/
private void syncSessionProjects() {
if (getNavigatorModel() == null) {
// model was never initialized for this session - nothing to sync
return;
}
try {
Map<String, RMProject> actualProjects = new LinkedHashMap<>();
for (RMProject rmProject : getRmController().listAccessibleProjects()) {
actualProjects.put(rmProject.getId(), rmProject);
}
for (WebSessionProjectImpl project : new ArrayList<>(getWorkspace().getProjects())) {
if (actualProjects.remove(project.getId()) == null && !project.isInMemory()) {
// in-memory (anonymous) projects are never listed by RM
deleteSessionProject(project);
}
}
for (RMProject rmProject : actualProjects.values()) {
createWebProject(rmProject);
}
} catch (DBException e) {
addSessionError(e);
log.error("Error synchronizing accessible projects", e);
}
}
// Note: for admin use only
public void resetUserState(boolean needResetUserCache) throws DBException {
clearAuthTokens();
@@ -460,6 +502,25 @@ public class WebSession extends BaseWebSession
navigatorModelLock.readLock().unlock();
}
}
/**
* Initializes the project's lazy database branch while preventing the navigator model from being replaced.
*/
public void initializeProjectNavigator(@NotNull WebSessionProjectImpl project) {
navigatorModelLock.readLock().lock();
try {
if (navigatorModel == null) {
return;
}
var projectNode = navigatorModel.getRoot().getProjectNode(project);
if (projectNode != null) {
projectNode.getDatabases();
}
} finally {
navigatorModelLock.readLock().unlock();
}
}
/**
* Returns and clears progress messages
*/
@@ -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.
@@ -68,6 +68,13 @@ public class WebSessionEventsFilter {
}
}
public void migrateTo(@NotNull WebSessionEventsFilter target) {
synchronized (target.subscribedProjectIds) {
target.subscribedEventTopics.addAll(this.subscribedEventTopics);
target.subscribedProjectIds.addAll(this.subscribedProjectIds);
}
}
public boolean isEventAllowed(WSEvent event) {
if (event.isForceProcessed()) {
return true;
@@ -59,6 +59,7 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
private final boolean isPrivate;
private final boolean isAuthHidden;
private final boolean isCaseInsensitive;
private final boolean sameTabRedirectOnLogout;
private final boolean serviceProvider;
private final String[] requiredFeatures;
private final boolean isRequired;
@@ -75,6 +76,7 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
this.isRequired = CommonUtils.toBoolean(cfg.getAttribute(WebRegistryConstant.ATTR_REQUIRED));
this.isAuthHidden = CommonUtils.toBoolean(cfg.getAttribute(WebRegistryConstant.ATTR_AUTH_HIDDEN));
this.isCaseInsensitive = CommonUtils.toBoolean(cfg.getAttribute(WebRegistryConstant.ATTR_CASE_INSENSITIVE));
this.sameTabRedirectOnLogout = CommonUtils.toBoolean(cfg.getAttribute(WebRegistryConstant.ATTR_SAME_TAB_LOGOUT_REDIRECT));
this.serviceProvider = CommonUtils.toBoolean(cfg.getAttribute(WebRegistryConstant.ATTR_SERVICE_PROVIDER));
for (IConfigurationElement cfgElement : cfg.getChildren(WebRegistryConstant.TAG_CONFIGURATION)) {
@@ -129,6 +131,10 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
return trusted;
}
public boolean isSameTabRedirectOnLogout() {
return sameTabRedirectOnLogout;
}
public boolean isPrivate() {
return isPrivate;
}
@@ -29,6 +29,7 @@ public class WebRegistryConstant {
public static final String ATTR_REQUIRED = "required";
public static final String ATTR_AUTH_HIDDEN = "authHidden";
public static final String ATTR_CASE_INSENSITIVE = "caseInsensitive";
public static final String ATTR_SAME_TAB_LOGOUT_REDIRECT = "sameTabRedirectOnLogout";
public static final String ATTR_REQUIRED_FEATURES = "requiredFeatures";
public static final String ATTR_CATEGORIES = "categories";
public static final String ATTR_SERVICE_PROVIDER = "serviceProvider";
@@ -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.
@@ -76,7 +76,7 @@ public abstract class BaseServletPlatform extends BasePlatformImpl {
// Remove temp folder
if (tempFolder != null) {
if (!ContentUtils.deleteFileRecursive(tempFolder.toFile())) {
if (!ContentUtils.deleteFileRecursive(tempFolder)) {
log.warn("Can't delete temp folder '" + tempFolder.toAbsolutePath() + "'");
}
tempFolder = null;
@@ -54,7 +54,7 @@ public class ServerGlobalWorkspace extends BaseWorkspaceImpl {
@NotNull DBPPlatform platform,
@NotNull ServletApplication application
) {
super(platform, application.getWorkspaceDirectory());
super(platform, application.getWorkspacePath());
this.application = application;
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ public interface DBWServiceBindingServlet<APPLICATION extends ServletApplication
return true;
}
void addServlets(APPLICATION application, DBWServletContext servletContext) throws DBException;
void addServlets(@NotNull APPLICATION application, @NotNull DBWServletContext servletContext) throws DBException;
@NotNull
default List<String> getExcludedServletPaths(@NotNull APPLICATION application) {
@@ -192,13 +192,16 @@ public class ServletAppUtils {
@NotNull String serviceId,
@NotNull String origin
) throws DBException {
String finalOrigin = webAuthApplication.modifyOrigin(origin);
String finalOrigin = ServletAppUtils.substringBeforeRootURI(webAuthApplication.modifyOrigin(origin));
String serviceUriSegment = removeSideSlashes(webAuthApplication.getAuthServiceUriSegment());
String rootUri = removeSideSlashes(getServletApplication().getRootURI());
if (finalOrigin.endsWith("/" + rootUri) && serviceUriSegment.startsWith(rootUri + "/")) {
finalOrigin = finalOrigin.substring(0, finalOrigin.length() - rootUri.length());
}
StringBuilder authUriBuilder = new StringBuilder(removeSideSlashes(finalOrigin));
if (CommonUtils.isNotEmpty(rootUri)) {
authUriBuilder.append("/").append(rootUri);
}
if (CommonUtils.isNotEmpty(serviceUriSegment)) {
authUriBuilder.append("/").append(serviceUriSegment);
}
@@ -232,6 +235,14 @@ public class ServletAppUtils {
response.addCookie(sessionCookie);
}
public static String getHeaderOrLegacy(HttpServletRequest request, String headerName, String legacyHeaderName) {
String value = request.getHeader(headerName);
if (CommonUtils.isEmpty(value)) {
value = request.getHeader(legacyHeaderName);
}
return value;
}
public static String getRequestCookie(HttpServletRequest request, String cookieName) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
@@ -297,12 +308,21 @@ public class ServletAppUtils {
}
}
@NotNull
public static String getHostOriginFromRequest(@NotNull HttpServletRequest request) {
return substringBeforeRootURI(getOriginFromRequest(request));
}
@NotNull
public static String getOriginFromRequest(@NotNull HttpServletRequest request) {
String origin = request.getHeader(HEADER_ORIGIN);
if (log.isTraceEnabled()) {
log.trace("Origin header: " + origin);
}
if ("null".equals(origin)) {
//strange identity provider bug
origin = null;
}
if (CommonUtils.isEmpty(origin)) {
origin = request.getHeader(HEADER_X_ORIGIN);
if (log.isTraceEnabled()) {
@@ -33,6 +33,7 @@ import org.jkiss.dbeaver.model.access.DBAAuthCredentials;
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.DBPConnectionType;
import org.jkiss.dbeaver.model.connection.DBPDataSourceProviderDescriptor;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNativeCredentials;
@@ -40,6 +41,7 @@ import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration;
import org.jkiss.dbeaver.model.net.ssh.SSHConstants;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceDisconnectEvent;
import org.jkiss.dbeaver.registry.DataSourceProviderRegistry;
import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor;
import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
@@ -55,9 +57,12 @@ public class WebDataSourceUtils {
private WebDataSourceUtils() {
}
public static void saveCredentialsInDataSource(WebConnectionInfo webConnectionInfo, DBPDataSourceContainer dataSourceContainer, DBPConnectionConfiguration configuration) {
public static void saveCredentialsInDataSource(
@NotNull WebConnectionInfo webConnectionInfo,
@NotNull DBPDataSourceContainer dataSourceContainer,
@NotNull DBPConnectionConfiguration configuration
) {
// Properties passed from web
// webConnectionInfo may be null in some cases (e.g. connection test when no actual connection exist yet)
Map<String, Object> authProperties = webConnectionInfo.getSavedAuthProperties();
if (authProperties != null) {
authProperties.forEach((s, o) -> configuration.setAuthProperty(s, CommonUtils.toString(o)));
@@ -305,9 +310,7 @@ public class WebDataSourceUtils {
if (config.getKeepAliveInterval() >= 0) {
dsConfig.setKeepAliveInterval(config.getKeepAliveInterval());
}
if (config.isDefaultAutoCommit() != null) {
dsConfig.getBootstrap().setDefaultAutoCommit(config.isDefaultAutoCommit());
}
dsConfig.getBootstrap().setDefaultAutoCommit(config.isDefaultAutoCommit());
dsConfig.getBootstrap().setDefaultCatalogName(config.getDefaultCatalogName());
dsConfig.getBootstrap().setDefaultSchemaName(config.getDefaultSchemaName());
// Save provider props
@@ -320,8 +323,19 @@ public class WebDataSourceUtils {
if (config.getConfigurationType() != null) {
dsConfig.setConfigurationType(config.getConfigurationType());
}
if (config.getConnectionType() != null) {
DBPConnectionType connectionType = DataSourceProviderRegistry.getInstance().getConnectionType(config.getConnectionType(), null);
if (connectionType != null) {
dsConfig.setConnectionType(connectionType);
}
}
if (CommonUtils.isEmpty(config.getUrl())) {
dsConfig.setUrl(driver.getConnectionURL(dsConfig));
try {
dsConfig.setUrl(driver.getConnectionURL(dsConfig));
} catch (DBException e) {
log.error("Error preparing connection URL", e);
}
}
// Save network handlers
if (config.getNetworkHandlersConfig() != null) {
@@ -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,8 @@
*/
package io.cloudbeaver.websocket;
import io.cloudbeaver.model.session.BaseWebSession;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
@@ -24,4 +26,6 @@ public interface CBWebSessionEventHandler {
void close();
void migrateToSession(@NotNull BaseWebSession newSession);
}
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Community Product
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
Bundle-Version: 26.1.2.qualifier
Bundle-Release-Date: 20260706
Bundle-Version: 26.2.1.qualifier
Bundle-Release-Date: 20260921
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce
@@ -16,6 +16,10 @@
</product>
</extension>
<extension point="io.cloudbeaver.feature">
<feature id="ai" label="AI Integration" description="Enables AI Chat in the application." enabledByDefault="true"/>
</extension>
<extension point="org.jkiss.dbeaver.application">
<application
id="cloudbeaver-ce"
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.product.ce</artifactId>
<version>26.1.2-SNAPSHOT</version>
<version>26.2.1-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Cloudbeaver Base JDBC drivers
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
Bundle-Version: 1.0.149.qualifier
Bundle-Release-Date: 20260706
Bundle-Version: 1.0.154.qualifier
Bundle-Release-Date: 20260921
Bundle-Vendor: DBeaver Corp
Bundle-ActivationPolicy: lazy
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
@@ -66,7 +66,7 @@
<driver id="generic:trino_jdbc"/>
<driver id="duckdb:duckdb_jdbc"/>
<driver id="generic:kyuubi_hive"/>
<driver id="databend:databend"/>
<driver id="databend:databend_jdbc"/>
</extension>
@@ -9,6 +9,6 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
<version>1.0.149-SNAPSHOT</version>
<version>1.0.154-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.2.qualifier
Bundle-Release-Date: 20260706
Bundle-Version: 26.2.1.qualifier
Bundle-Release-Date: 20260921
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.server.ce</artifactId>
<version>26.1.2-SNAPSHOT</version>
<version>26.2.1-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -1,3 +1,7 @@
####################################################
# Core API
####################################################
extend type ServerConfig {
serverURL: String! @deprecated
rootURI: String!
@@ -14,11 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.service.admin;
package io.cloudbeaver.model.config;
import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBConstants;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
@@ -57,7 +57,7 @@ public class AdminServerConfig {
@Nullable
private String bindSessionToIp;
public AdminServerConfig(Map<String, Object> params) {
public AdminServerConfig(@NotNull Map<String, Object> params) {
this.serverName = JSONUtils.getString(params, "serverName");
this.adminName = JSONUtils.getString(params, "adminName");
this.adminPassword = JSONUtils.getString(params, "adminPassword");
@@ -181,6 +181,7 @@ public abstract class CBApplication<T extends CBServerConfig>
return getServerConfigurationController().getServerConfiguration();
}
@NotNull
@Override
public ServletAuthConfiguration getAuthConfiguration() {
return getAppConfiguration();
@@ -228,11 +229,11 @@ public abstract class CBApplication<T extends CBServerConfig>
Location instanceLoc = Platform.getInstanceLocation();
try {
if (!instanceLoc.isSet()) { // always false?
URL wsLocationURL = getWorkspaceDirectory().toUri().toURL();
URL wsLocationURL = getWorkspacePath().toUri().toURL();
instanceLoc.set(wsLocationURL, true);
}
} catch (Exception e) {
log.error("Error setting workspace location to " + getWorkspaceDirectory().toAbsolutePath(), e);
log.error("Error setting workspace location to " + getWorkspacePath().toAbsolutePath(), e);
return;
}
this.systemInformationCollector = createSystemInformationCollector();
@@ -443,7 +444,7 @@ public abstract class CBApplication<T extends CBServerConfig>
@NotNull
public Path getDataDirectory(boolean create) {
Path dataDir = getWorkspaceDirectory().resolve(CBConstants.RUNTIME_DATA_DIR_NAME);
Path dataDir = getWorkspacePath().resolve(CBConstants.RUNTIME_DATA_DIR_NAME);
if (create && !Files.exists(dataDir)) {
try {
Files.createDirectories(dataDir);
@@ -751,6 +752,7 @@ public abstract class CBApplication<T extends CBServerConfig>
eventController.addEvent(new WSServerConfigurationChangedEvent(sessionId, null));
}
@NotNull
@Override
public abstract CBServerConfigurationController<T> getServerConfigurationController();
@@ -93,6 +93,7 @@ public class CBApplicationCE extends CBApplication<CBServerConfig> {
return new LocalFileController(DBWorkbench.getPlatform().getWorkspace().getAbsolutePath().resolve(DBFileController.DATA_FOLDER));
}
@NotNull
@Override
public CBServerConfigurationControllerEmbedded<CBServerConfig> getServerConfigurationController() {
return serverConfigController;
@@ -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.
@@ -20,10 +20,7 @@ import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import io.cloudbeaver.model.app.BaseServerConfigurationController;
import io.cloudbeaver.model.app.BaseServletApplication;
import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.model.config.PasswordPolicyConfiguration;
import io.cloudbeaver.model.config.SMControllerConfiguration;
import io.cloudbeaver.model.config.*;
import io.cloudbeaver.utils.ServletAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
@@ -374,6 +371,7 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
try (Writer out = new OutputStreamWriter(Files.newOutputStream(runtimeConfigPath), StandardCharsets.UTF_8)) {
Gson gson = new GsonBuilder()
.setStrictness(Strictness.LENIENT)
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.setPrettyPrinting()
.create();
gson.toJson(configurationProperties, out);
@@ -489,6 +487,7 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
}
}
appConfigProperties.put("enabledFeatures", Arrays.asList(appConfig.getEnabledFeatures()));
appConfigProperties.put("disabledFeatures", Arrays.asList(appConfig.getDisabledFeatures()));
if (appConfig.getEnabledAuthProviders() != null) {
appConfigProperties.put("enabledAuthProviders", Arrays.asList(appConfig.getEnabledAuthProviders()));
}
@@ -508,7 +507,7 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
@NotNull
protected Map<String, Object> collectServerConfigProperties(
@NotNull CBServerConfig serverConfig,
Map<String, Object> originServerConfig
@NotNull Map<String, Object> originServerConfig
) {
var serverConfigProperties = new LinkedHashMap<String, Object>();
if (!CommonUtils.isEmpty(serverConfig.getServerName())) {
@@ -633,6 +632,9 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
return serverConfiguration;
}
@NotNull
public abstract CBServerConfigurationMapper<T, ? extends AdminServerConfig> createServerConfigurationInputMapper();
public CBAppConfig getAppConfiguration() {
return appConfiguration;
}
@@ -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,6 +19,7 @@ package io.cloudbeaver.server;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import io.cloudbeaver.model.config.AdminServerConfig;
import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.model.config.WebDatabaseConfig;
import org.jkiss.code.NotNull;
@@ -41,6 +42,11 @@ public class CBServerConfigurationControllerEmbedded<T extends CBServerConfig> e
super(serverConfig, homeDirectory);
}
@NotNull
public CBServerConfigurationMapper<T, ? extends AdminServerConfig> createServerConfigurationInputMapper() {
return new CBServerConfigurationMapper<>();
}
@NotNull
@Override
protected Map<String, Object> collectServerConfigProperties(
@@ -0,0 +1,145 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.server;
import io.cloudbeaver.DBWFeatureSet;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.config.AdminServerConfig;
import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.utils.ServletAppUtils;
import io.cloudbeaver.utils.WebDataSourceUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.utils.CommonUtils;
import java.util.*;
public class CBServerConfigurationMapper<C extends CBServerConfig, I extends AdminServerConfig> {
private static final Log log = Log.getLog(CBServerConfigurationMapper.class);
@NotNull
public CBServerConfigurations loadServerConfigurationsFromParams(@NotNull Map<String, Object> params) {
CBAppConfig appConfig = new CBAppConfig(CBApplication.getInstance().getAppConfiguration());
C serverConfig = createServerConfiguration();
serverConfig.setServerName(CBApplication.getInstance().getServerName());
serverConfig.setMaxSessionIdleTime(CBApplication.getInstance().getMaxSessionIdleTime());
String adminName = null;
String adminPassword = null;
if (!params.isEmpty()) { // FE can send an empty configuration
I config = createInputConfiguration(params);
adminName = config.getAdminName();
adminPassword = config.getAdminPassword();
populateConfigurations(config, serverConfig, appConfig);
}
return new CBServerConfigurations(
serverConfig,
appConfig,
adminName,
adminPassword
);
}
protected void populateConfigurations(@NotNull I input, @NotNull C serverConfig, @NotNull CBAppConfig appConfig) {
appConfig.setAnonymousAccessEnabled(input.isAnonymousAccessEnabled());
appConfig.setSupportsCustomConnections(input.isCustomConnectionsEnabled());
appConfig.setPublicCredentialsSaveEnabled(input.isPublicCredentialsSaveEnabled());
appConfig.setAdminCredentialsSaveEnabled(input.isAdminCredentialsSaveEnabled());
updateDisabledFeaturesConfig(appConfig, input.getEnabledFeatures());
// custom logic for enabling embedded drivers
updateDisabledDriversConfig(appConfig, input.getDisabledDrivers());
appConfig.setResourceManagerEnabled(input.isResourceManagerEnabled());
appConfig.setSecretManagerEnabled(input.isSecretManagerEnabled());
if (CommonUtils.isEmpty(input.getEnabledAuthProviders())) {
// All of them
appConfig.setEnabledAuthProviders(new String[0]);
} else {
appConfig.setEnabledAuthProviders(input.getEnabledAuthProviders().toArray(new String[0]));
}
appConfig.setDefaultNavigatorSettings(
CBApplication.getInstance().getAppConfiguration().getDefaultNavigatorSettings());
serverConfig.setServerName(input.getServerName());
serverConfig.setMaxSessionIdleTime(input.getSessionExpireTime());
if (input.getForceHttps() != null) {
serverConfig.setForceHttps(input.getForceHttps());
}
if (input.getSupportedHosts() != null) {
serverConfig.setSupportedHosts(input.getSupportedHosts());
}
if (input.getBindSessionToIp() != null) {
serverConfig.setBindSessionToIp(input.getBindSessionToIp());
}
}
private void updateDisabledFeaturesConfig(@NotNull CBAppConfig appConfig, @NotNull List<String> enabledFeatures) {
Set<String> enabledIds = new LinkedHashSet<>(enabledFeatures);
appConfig.setEnabledFeatures(enabledFeatures.toArray(new String[0]));
String[] disabledFeatures = ServletAppUtils.getServletApplication().getFeatureRegistry().getWebFeatures().stream()
.map(DBWFeatureSet::getId)
.filter(id -> !enabledIds.contains(id))
.toArray(String[]::new);
appConfig.setDisabledFeatures(disabledFeatures);
}
// we disable embedded drivers by default and enable it in enabled drivers list
// that's why we need so complicated logic for disabling drivers
private void updateDisabledDriversConfig(@NotNull CBAppConfig appConfig, @NotNull String[] disabledDriversConfig) {
Set<String> disabledIds = new LinkedHashSet<>(Arrays.asList(disabledDriversConfig));
Set<String> enabledIds = new LinkedHashSet<>(Arrays.asList(appConfig.getEnabledDrivers()));
// remove all disabled embedded drivers from enabled drivers list
enabledIds.removeAll(disabledIds);
// enable embedded driver if it is not in disabled drivers list
for (String driverId : appConfig.getDisabledDrivers()) {
if (disabledIds.contains(driverId)) {
// driver is also disabled
continue;
}
// driver is removed from disabled list
// we need to enable if it is embedded
try {
DBPDriver driver = WebDataSourceUtils.getDriverById(driverId);
if (driver.isEmbedded()) {
enabledIds.add(driverId);
}
} catch (DBWebException e) {
log.error("Failed to find driver by id", e);
}
}
appConfig.setDisabledDrivers(disabledDriversConfig);
appConfig.setEnabledDrivers(enabledIds.toArray(String[]::new));
}
@NotNull
protected C createServerConfiguration() {
return (C) new CBServerConfig();
}
@NotNull
protected I createInputConfiguration(@NotNull Map<String, Object> params) {
return (I) new AdminServerConfig(params);
}
}
@@ -0,0 +1,30 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.server;
import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.model.config.CBServerConfig;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
public record CBServerConfigurations(
@NotNull CBServerConfig serverConfig,
@NotNull CBAppConfig appConfig,
@Nullable String adminName,
@Nullable String adminPassword
) {
}
@@ -202,6 +202,43 @@ public class CBSessionManager implements WebAppSessionManager {
return httpSession.getId();
}
/**
* Invalidates the current HTTP session, creates a new one, and binds a new {@link WebSession} to it.
*/
@NotNull
public WebSession rotateSession(
@NotNull HttpServletRequest request,
@NotNull WebSession oldWebSession
) throws DBWebException {
HttpSession oldHttpSession = request.getSession(false);
if (oldHttpSession != null) {
oldHttpSession.invalidate();
}
String newSessionId = request.getSession(true).getId();
String locale = oldWebSession.getLocale();
String remoteAddr = oldWebSession.getLastRemoteAddr();
String remoteUserAgent = oldWebSession.getLastRemoteUserAgent();
var requestInfo = new WebHttpRequestInfo(newSessionId, locale, remoteAddr, remoteUserAgent);
WebSession newWebSession;
try {
newWebSession = createWebSessionImpl(requestInfo);
} catch (DBException e) {
throw new DBWebException(e);
}
oldWebSession.migrateEventHandlersTo(newWebSession);
oldWebSession.getEventsFilter().migrateTo(newWebSession.getEventsFilter());
String oldSessionId = oldWebSession.getSessionId();
synchronized (sessionMap) {
sessionMap.remove(oldSessionId);
sessionMap.put(newSessionId, newWebSession);
}
oldWebSession.close(false, false);
log.debug("Session rotated '" + oldSessionId + "' -> '" + newSessionId + "'");
return newWebSession;
}
/**
* Returns not expired session from cache, or restore it.
*
@@ -288,13 +325,12 @@ public class CBSessionManager implements WebAppSessionManager {
@Nullable
public WebSession findWebSession(@NotNull HttpServletRequest request) {
String sessionId = getSessionId(request);
WebSession webSession;
synchronized (sessionMap) {
var session = sessionMap.get(sessionId);
if (session instanceof WebSession) {
return (WebSession) session;
}
return null;
webSession = (session instanceof WebSession) ? (WebSession) session : null;
}
return webSession;
}
@Nullable
@@ -3,16 +3,11 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Server
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
Bundle-Version: 26.1.2.qualifier
Bundle-Release-Date: 20260706
Bundle-Version: 26.2.1.qualifier
Bundle-Release-Date: 20260921
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.model.sql;visibility:=reexport,
org.jkiss.dbeaver.data.gis,
org.jkiss.bundle.jetty.server;visibility:=reexport,
org.jkiss.bundle.graphql;visibility:=reexport,
org.jkiss.bundle.apache.dbcp,
org.jkiss.dbeaver.net.ssh,
Require-Bundle: org.jkiss.bundle.jetty.server;visibility:=reexport,
io.cloudbeaver.model;visibility:=reexport,
io.cloudbeaver.model.cli,
io.cloudbeaver.service.security;visibility:=reexport
@@ -45,6 +45,9 @@
<eventHandler class="io.cloudbeaver.server.events.WSDataSourceUpdatedEventHandlerImpl">
<topic id="cb_datasource"/>
</eventHandler>
<eventHandler class="io.cloudbeaver.server.events.WSNetworkProfileEventHandler">
<topic id="cb_profiles"/>
</eventHandler>
<eventHandler class="io.cloudbeaver.server.events.WSFolderUpdatedEventHandlerImpl">
<topic id="cb_datasource_folder"/>
</eventHandler>
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.server</artifactId>
<version>26.1.2-SNAPSHOT</version>
<version>26.2.1-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -1,6 +1,10 @@
# Any object (JSON)
####################################################
# Base schema
####################################################
"Any object (JSON)"
scalar Object
# Date/Time
"Date/Time"
scalar DateTime
scalar Date
@@ -25,6 +25,8 @@ type ObjectPropertyInfo {
validValues: [ Object ]
"Default property value"
defaultValue: Object
"Constraints for the property value"
constraints: PropertyConstraints @since(version: "26.2.0")
"Property value length"
length: ObjectPropertyLength!
"List of supported features (e.g., system, hidden, inherited, foreign, expensive)"
@@ -41,6 +43,13 @@ type ObjectPropertyInfo {
conditions: [Condition!] @since(version: "25.0.1")
}
type PropertyConstraints @since(version: "26.2.0") {
"Minimum allowed value for numeric properties"
min: Float
"Maximum allowed value for numeric properties"
max: Float
}
enum ObjectPropertyLength {
"1 character"
TINY,
@@ -556,7 +565,7 @@ type ConnectionInfo {
supportedDataFormats: [ ResultDataFormat! ]!
configurationType: DriverConfigurationType
#Access properties
"Access properties"
canViewSettings: Boolean!
canEdit: Boolean!
canDelete: Boolean!
@@ -570,6 +579,31 @@ type ConnectionInfo {
"List of tools that can be used with this connection. Returns empty list if no tools are available"
tools: [String!]! @since(version: "24.1.3")
connectionType: ID! @since(version: "26.1.2")
"Driver configuration"
driverConfiguration: DriverConfiguration! @since(version: "26.1.2")
}
"Database-dependent metadata describing features supported by the driver"
type DriverConfiguration {
"Supported 'on duplicate key' replace methods for this connection's SQL dialect"
supportedInsertReplaceMethods: [DictionaryValueInfo!]!
"Whether the connection's database supports bulk load during data import"
supportsBulkLoad: Boolean!
"Whether the connection's database supports transactions"
supportsTransactions: Boolean!
}
"Generic dictionary value: an identifier with a human readable name and an optional description"
type DictionaryValueInfo {
"Value ID"
id: ID!
"Human readable value name"
name: String!
"Value description"
description: String
}
type ConnectionFolderInfo {
@@ -633,6 +667,39 @@ type LogEntry {
stackTrace: String
}
type ConnectionType @since(version: "26.1.2") {
"Unique identifier of the connection type"
id: ID!
"Human-readable name of the connection type"
name: String!
"Description of the connection type"
description: String
"Color of the connection type (for light theme)"
colorLight: String!
"Alternative color of the connection type (for dark theme)"
colorDark: String
"Is autocommit enabled for this connection type"
autocommit: Boolean!
"Confirm execution of queries for this connection type"
confirmExecute: Boolean!
"Confirm data change operations (INSERT, UPDATE, DELETE) for this connection type"
confirmDataChange: Boolean!
"Smart commit mode is enabled for this connection type"
smartCommit: Boolean!
"Recover smart commit mode is enabled for this connection type"
smartCommitRecover: Boolean!
"Auto-close transactions is enabled for this connection type"
autoCloseTransactions: Boolean!
"Auto-close idle transactions period in seconds for this connection type"
closeIdleTransactionPeriod: Int!
"Auto-close connections is enabled for this connection type"
autoCloseConnections: Boolean!
"Auto-close idle connections period in seconds for this connection type"
closeIdleConnectionPeriod: Int!
"Specifies whether this connection type is system-defined or user-defined. System-defined connection types cannot be deleted."
predefined: Boolean!
}
####################################################
# Input types
####################################################
@@ -679,7 +746,7 @@ input ConnectionConfig {
"ID of database driver"
driverId: ID
# Custom connection parameters (all optional)
"Custom connection parameters (all optional)"
host: String
port: String
serverName: String
@@ -745,6 +812,8 @@ input ConnectionConfig {
defaultSchemaName: String @since(version: "25.0.5") @deprecated(reason: "25.2.1 use expertPropertyValues instead")
"Default user settings for connection"
defaultUserPreferences: Object @since(version: "26.0.2")
connectionType: ID @since(version: "26.1.2")
}
####################################################
@@ -790,6 +859,8 @@ extend type Query {
"Returns connection driver properties based on configuration"
listConnectionDriverProperties( projectId: ID!, config: ConnectionConfig! ): [ObjectPropertyInfo!]!
listConnectionTypes: [ConnectionType!]! @since(version: "26.1.2")
}
extend type Mutation {
@@ -862,4 +933,3 @@ extend type Mutation {
"Get async task info by ID"
asyncTaskInfo(id: String!, removeOnFinish: Boolean!): AsyncTaskInfo!
}
@@ -1,4 +1,7 @@
####################################################
# Datasource events
####################################################
extend enum CBServerEventId {
"Datasource created"
@@ -31,6 +31,10 @@ enum CBServerEventId {
"Open URL request in client"
cb_open_url @since(version: "25.3.1")
"User-specific object settings updated"
cb_object_settings_updated @since(version: "26.1.4")
"User-specific object settings deleted"
cb_object_settings_deleted @since(version: "26.1.4")
}
"Events sent by client"
@@ -57,6 +61,7 @@ enum CBEventTopic {
cb_workspace_configuration @since(version: "25.1.1")
cb_notification @since(version: "25.2.1")
cb_session_action @since(version: "25.3.1")
cb_object_settings @since(version: "26.1.4")
}
"Base server event interface"
@@ -178,6 +183,26 @@ type WSOpenUrlEvent implements CBServerEvent @since(version: "25.3.1") {
actionId: String!
}
"Type of the object that owns user-specific settings"
enum WSObjectSettingsType @since(version: "26.1.4") {
datasource
project
}
"User-specific object settings change event (e.g. navigator view)"
type WSObjectSettingsEvent implements CBServerEvent @since(version: "26.1.4") {
id: CBServerEventId!
topicId: CBEventTopic!
"Type of the object the settings belong to"
smObjectType: WSObjectSettingsType!
"Project of the object"
projectId: String
"Id of the object"
objectId: String!
"Ids of the settings that were changed"
settingIds: [String!]!
}
extend type Query {
emptyEvent: Boolean
}
@@ -1,3 +1,7 @@
####################################################
# SQL events
####################################################
extend enum CBServerEventId {
"Database output log updated (e.g., DBMS in Oracle)"
cb_database_output_log_updated,
@@ -1,4 +1,6 @@
####################################################
# Nodes, objects and properties
####################################################
type ObjectDescriptor {
id: Int
@@ -143,7 +145,7 @@ extend type Query {
"Refreshes node based on the node path"
navRefreshNode( nodePath: ID! ): Boolean @deprecated # Use navReloadNode method from Mutation (24.2.4)
# contextId currently not using
"contextId currently not using"
navGetStructContainers( projectId: ID, connectionId: ID!, contextId: ID, catalog: ID ): DatabaseStructContainers!
}
@@ -38,7 +38,7 @@ type SQLCompletionProposal {
# Data read / SQL execution
####################################################
# SQL context must be created for each SQL editor
"SQL context must be created for each SQL editor"
type SQLContextInfo {
id: ID!
@@ -68,19 +68,39 @@ input SQLDataFilter {
limit: Int
constraints: [ SQLDataFilterConstraint ]
"When true constraints are combined with OR instead of AND"
anyConstraint: Boolean
where: String
orderBy: String
}
type SQLResultReference @since(version: "26.1.1") {
"Pair (source-row column index, target-entity column name) used to build a navigation filter"
type SQLReferenceColumnMapping @since(version: "26.1.2") {
"Position in the result-set bindings"
sourceColumnIndex: Int!
"Column name in the source result set"
sourceColumnName: String!
"Ordinal position of the column in the target entity definition"
targetColumnIndex: Int!
"Column name in the target entity"
targetColumnName: String!
}
type SQLResultAssociation @since(version: "26.1.2") {
"True for reverse references (incoming FKs)"
reference: Boolean!
"Name of the association (foreign key)"
associationName: String!
"Fully qualified name of the entity"
"Name of the catalog the target entity belongs to"
targetCatalogName: String
"Name of the schema the target entity belongs to"
targetSchemaName: String
"Plain name of the target 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!]!
targetNodePath: String
"List of column mappings between the source result set and the target entity"
columnMapping: [SQLReferenceColumnMapping!]!
}
type SQLResultColumn {
@@ -133,15 +153,12 @@ 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
"""
True means that resultset was generated by single entity query
New rows can be added, old rows can be deleted
"""
singleEntity: Boolean!
# server always returns hasMoreData = false
"server always returns hasMoreData = false"
hasMoreData: Boolean!
"Identifies if result set has row identifier. If true then it is possible update data or load LOB files"
hasRowIdentifier: Boolean! @deprecated (reason: "use rowIdentifierState (25.3.4)")
@@ -198,7 +215,7 @@ type SQLQueryResults {
updateRowCount: Float
sourceQuery: String
# Actual data format of this result
"Actual data format of this result"
dataFormat: ResultDataFormat
resultSet: SQLResultSet
@@ -206,17 +223,17 @@ type SQLQueryResults {
type SQLExecuteInfo {
# Status message
"Status message"
statusMessage: String
# Execute time (ms)
"Execute time (ms)"
duration: Int!
# Actual conditions applied to query
"Actual conditions applied to query"
filterText: String
# Full query that was executed, contains all used filters
"Full query that was executed, contains all used filters"
fullQuery: String
# Original query that was executed, without modifications
"Original query that was executed, without modifications"
originalQuery: String
# Results
"Results"
results: [ SQLQueryResults! ]!
}
@@ -281,6 +298,8 @@ type SQLQueryGenerator {
input SQLQueryGeneratorOptions {
useFullyQualifiedNames: Boolean!
compactSql: Boolean!
"Include nested objects, comments and permissions in generated DDL (since 26.0.4)"
showFullDdl: Boolean
}
####################################################
@@ -328,6 +347,15 @@ type TransactionLogInfos {
extend type Query {
"Foreign-key navigation metadata for the given result set. Set isReference to true for reverse references, false for forward associations, or omit it to get both"
sqlResultAssociations(
projectId: ID,
connectionId: ID!,
contextId: ID!,
resultsId: ID!,
isReference: Boolean
): [SQLResultAssociation!]! @since(version: "26.1.2")
"Returns SQL dialect info for the specified connection"
sqlDialectInfo( projectId: ID, connectionId: ID! ): SQLDialectInfo
@@ -373,7 +401,7 @@ extend type Query {
options: Object @deprecated(reason: "use generatorOptions (26.0.3)")
nodePathList: [String!]!,
generatorOptions: SQLQueryGeneratorOptions
): String!
): String! @deprecated(reason: "use asyncSqlGenerateEntityQuery")
"""
Returns an auto-generated SQL query based on provided rows data.
@@ -467,19 +495,6 @@ 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!,
@@ -582,6 +597,13 @@ extend type Mutation {
"Returns row count for async SQL row data count task"
asyncSqlRowDataCountResult(taskId: ID!): Int!
"Creates async task to generate entity SQL. Generated SQL is returned in the taskResult field"
asyncSqlGenerateEntityQuery(
generatorId: String!,
nodePathList: [String!]!,
generatorOptions: SQLQueryGeneratorOptions
): AsyncTaskInfo!
"Reads dynamic trace from provided database results"
sqlGetDynamicTrace(
projectId: ID,
@@ -50,7 +50,6 @@ 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;
@@ -209,9 +208,16 @@ public class WebServiceUtils extends WebCommonUtils {
dataSourceContainer,
cfg
);
Map<String, Object> connectionProperties = driver.getConnectionProperties();
List<DBPPropertyDescriptor> propertyList = new ArrayList<>(Arrays.asList(properties));
Set<String> propertyNames = propertyList.stream().map(DBPPropertyDescriptor::getId).collect(Collectors.toSet());
Map<String, Object> connectionProperties = new LinkedHashMap<>(driver.getConnectionProperties());
// In case of collision, value from connectionProperties will be used for a driver property.
// Default value of property will be from driver.
for (Map.Entry<String, Object> connProp : connectionProperties.entrySet()) {
String propName = connProp.getKey();
if (propertyNames.contains(propName)) {
continue;
}
Object propValue = connProp.getValue();
DBPPropertyDescriptor dbpPropertyDescriptor = new PropertyDescriptor(
null,
@@ -223,19 +229,20 @@ public class WebServiceUtils extends WebCommonUtils {
propValue,
null
);
properties = ArrayUtils.add(DBPPropertyDescriptor.class, properties, dbpPropertyDescriptor);
propertyList.add(dbpPropertyDescriptor);
cfg.setProperty(propName, (String) propValue);
}
if (properties == null) {
if (propertyList.isEmpty()) {
return new WebPropertyInfo[0];
}
connectionProperties.putAll(cfg.getProperties());
PropertySourceCustom propertySource = new PropertySourceCustom(
properties,
cfg.getProperties()
propertyList,
connectionProperties
);
return Arrays.stream(properties)
return propertyList.stream()
.map(p -> new WebPropertyInfo(webSession, p, propertySource)).toArray(WebPropertyInfo[]::new);
} catch (DBException e) {
log.error("Error reading driver properties:\n" + e.getMessage());
@@ -21,8 +21,10 @@ import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBConstants;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBConstants;
import org.jkiss.dbeaver.model.DatabaseURL;
import org.jkiss.dbeaver.model.connection.*;
import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNative;
import org.jkiss.dbeaver.model.meta.Property;
@@ -180,13 +182,26 @@ public class WebDatabaseDriverInfo {
@NotNull
@Property
public WebPropertyInfo[] getDriverProperties() throws DBWebException {
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);
try {
DBPConnectionConfiguration cfg = new DBPConnectionConfiguration();
cfg.setUrl(CommonUtils.notEmpty(driver.getSampleURL()));
DatabaseURL.Pattern dbUrlPattern = DatabaseURL.getUrlPattern(CommonUtils.notEmpty(driver.getSampleURL()));
for (String propName : dbUrlPattern.getMandatoryPropertyNames()) {
switch (propName) {
case DBConstants.PROP_HOST -> cfg.setHostName(CBConstants.HOST_LOCALHOST);
case DBConstants.PROP_PORT -> cfg.setHostPort(CommonUtils.notNull(driver.getDefaultPort(), "0"));
case DBConstants.PROP_SERVER -> cfg.setServerName(CommonUtils.notNull(driver.getDefaultServer(), propName));
case DBConstants.PROP_USER -> cfg.setUserName(CommonUtils.notNull(driver.getDefaultUser(), propName));
case DBConstants.PROP_DATABASE, DBConstants.PROP_FOLDER, DBConstants.PROP_FILE ->
cfg.setDatabaseName(CommonUtils.notNull(driver.getDefaultDatabase(), propName));
default -> log.debug("Unexpected mandatory URL property " + propName);
}
}
cfg.setUrl(driver.getConnectionURL(cfg));
return WebServiceUtils.getDriverProperties(webSession, driver, null, cfg);
} catch (DBException e) {
throw new DBWebException("Error obtaining driver properties", e);
}
}
@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.
@@ -19,6 +19,7 @@ package io.cloudbeaver.server.events;
import io.cloudbeaver.server.BaseWebPlatform;
import io.cloudbeaver.server.WebAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
import org.jkiss.dbeaver.model.websocket.WSEventHandler;
@@ -33,7 +34,10 @@ public class WSDeleteTempFileHandler implements WSEventHandler<WSEventDeleteTemp
private static final Log log = Log.getLog(WSDeleteTempFileHandler.class);
public void resetTempFolder(String sessionId) {
public void resetTempFolder(@Nullable String sessionId) {
if (sessionId == null) {
return;
}
Path path = WebAppUtils.getWebPlatform()
.getTempFolder(new VoidProgressMonitor(), BaseWebPlatform.TEMP_FILE_FOLDER)
.resolve(sessionId);
@@ -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.
@@ -20,7 +20,6 @@ import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.WorkspaceConfigEventManager;
import org.jkiss.dbeaver.model.websocket.event.WSWorkspaceConfigurationChangedEvent;
import org.jkiss.dbeaver.registry.driver.DriverDescriptorSerializerLegacy;
public class WSEventHandlerWorkspaceConfigUpdate extends WSDefaultEventHandler<WSWorkspaceConfigurationChangedEvent> {
private static final Log log = Log.getLog(WSEventHandlerWorkspaceConfigUpdate.class);
@@ -30,8 +29,6 @@ public class WSEventHandlerWorkspaceConfigUpdate extends WSDefaultEventHandler<W
String configFileName = event.getConfigFilePath();
log.info("Config file changed: " + configFileName);
WorkspaceConfigEventManager.fireConfigChangedEvent(configFileName);
if (DriverDescriptorSerializerLegacy.DRIVERS_FILE_NAME.equals(event.getConfigFilePath())) {
super.handleEvent(event);
}
super.handleEvent(event);
}
}
@@ -0,0 +1,56 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.server.events;
import io.cloudbeaver.WebSessionProjectImpl;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSNetworkProfileEvent;
import org.jkiss.dbeaver.runtime.DBWorkbench;
public class WSNetworkProfileEventHandler extends WSDefaultEventHandler<WSNetworkProfileEvent> {
private static final Log log = Log.getLog(WSNetworkProfileEventHandler.class);
@Override
protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull WSNetworkProfileEvent event) {
if (event.getProjectId() != null) {
if (activeUserSession instanceof WebSession webSession) {
WebSessionProjectImpl project = webSession.getProjectById(event.getProjectId());
if (project == null) {
log.debug("Project " + event.getProjectId() + " is not found in session " + webSession.getSessionId());
return;
}
project.getDataSourceRegistry().getNetworkProfiles().reloadProfiles();
} else {
super.updateSessionData(activeUserSession, event);
}
} else {
DBWorkbench.getPlatform().getNetworkProfiles().reloadProfiles();
super.updateSessionData(activeUserSession, event);
}
}
@Override
protected boolean isAcceptableInSession(@NotNull BaseWebSession activeUserSession, @NotNull WSNetworkProfileEvent event) {
if (event.getProjectId() != null && !activeUserSession.isProjectAccessible(event.getProjectId())) {
return false;
}
return super.isAcceptableInSession(activeUserSession, event);
}
}
@@ -24,7 +24,7 @@ public class WSServerConfigurationChangedEventHandler extends WSDefaultEventHand
@Override
protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull WSServerConfigurationChangedEvent event) {
activeUserSession.refreshUserData();
activeUserSession.refreshUserPermissions();
super.updateSessionData(activeUserSession, event);
}
}
@@ -33,12 +33,16 @@ public class CBClientEventProcessor {
private static final Log log = Log.getLog(CBClientEventProcessor.class);
final BaseWebSession webSession;
volatile BaseWebSession webSession;
public CBClientEventProcessor(@NotNull BaseWebSession webSession) {
this.webSession = webSession;
}
void setWebSession(@NotNull BaseWebSession webSession) {
this.webSession = webSession;
}
public void process(@Nullable String message) {
if (CommonUtils.isEmpty(message)) {
return;
@@ -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.
@@ -40,16 +40,21 @@ public class CBEventsLongPolling implements CBWebSessionEventHandler {
private static final int QUEUE_CAPACITY = 1000;
private final BaseWebSession webSession;
private volatile BaseWebSession webSession;
private final BlockingQueue<WSEvent> queue = new LinkedBlockingQueue<>(QUEUE_CAPACITY);
private final CBClientEventProcessor processor;
private volatile long lastPoll;
private final CBEventsLongPollingServlet servlet;
public CBEventsLongPolling(@NotNull BaseWebSession webSession) {
public CBEventsLongPolling(
@NotNull BaseWebSession webSession,
@NotNull CBEventsLongPollingServlet owner
) {
this.webSession = webSession;
this.lastPoll = System.currentTimeMillis();
this.webSession.addEventHandler(this);
this.processor = new CBClientEventProcessor(this.webSession);
this.servlet = owner;
}
@NotNull
@@ -88,6 +93,14 @@ public class CBEventsLongPolling implements CBWebSessionEventHandler {
return result;
}
@Override
public void migrateToSession(@NotNull BaseWebSession newSession) {
String oldSid = this.webSession.getSessionId();
this.webSession = newSession;
this.processor.setWebSession(newSession);
servlet.rekeySession(oldSid, newSession.getSessionId());
}
@Override
public void handleWebSessionEvent(@NotNull WSEvent event) {
if (!queue.offer(event)) {
@@ -106,6 +119,7 @@ public class CBEventsLongPolling implements CBWebSessionEventHandler {
queue.clear();
}
@NotNull
@Override
public String toString() {
return "CBEventsLongPolling{" +
@@ -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.
@@ -83,7 +83,7 @@ public class CBEventsLongPollingServlet extends HttpServlet {
return;
}
CBEventsLongPolling ps = getOrCreatePollSession(ws);
CBEventsLongPolling ps = getOrCreatePollSession(ws, resp);
ps.onPoll();
List<WSEvent> events = ps.pollEvents(POLL_TIMEOUT_SEC);
@@ -111,7 +111,7 @@ public class CBEventsLongPollingServlet extends HttpServlet {
return;
}
CBEventsLongPolling ps = getOrCreatePollSession(ws);
CBEventsLongPolling ps = getOrCreatePollSession(ws, resp);
ps.onUserActivity();
String json = new String(req.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
@@ -163,23 +163,12 @@ public class CBEventsLongPollingServlet extends HttpServlet {
) {
try {
BaseWebSession ws = resolveSession(req);
resp.setHeader(WSConstants.WS_SESSION_HEADER, ws.getSessionId());
ServletAppUtils.addResponseCookie(
req,
resp,
CBConstants.CB_SESSION_COOKIE_NAME,
ws.getSessionId(),
-1
);
return ws;
return resolveSession(req);
} catch (SMException e) {
log.debug("LP request rejected: access token expired");
sendError(resp, HttpConstants.CODE_TOKEN_EXPIRED, e.getMessage());
} catch (SecurityException e) {
log.warn("LP session resolve failed", e);
log.debug("LP request rejected: " + e.getMessage());
sendError(resp, HttpConstants.CODE_UNAUTHORIZED, e.getMessage());
}
return null;
@@ -214,7 +203,7 @@ public class CBEventsLongPollingServlet extends HttpServlet {
}
@NotNull
private CBEventsLongPolling getOrCreatePollSession(@NotNull BaseWebSession ws) {
private CBEventsLongPolling getOrCreatePollSession(@NotNull BaseWebSession ws, @NotNull HttpServletResponse resp) {
final String sid = ws.getSessionId();
return sessions.compute(sid, (key, existing) -> {
@@ -222,9 +211,11 @@ public class CBEventsLongPollingServlet extends HttpServlet {
return existing;
}
CBEventsLongPolling ps = new CBEventsLongPolling(ws);
CBEventsLongPolling ps = new CBEventsLongPolling(ws, this);
ps.handleWebSessionEvent(new WSSocketConnectedEvent(ws.getApplication().getApplicationRunId()));
resp.setHeader(WSConstants.WS_SESSION_HEADER, sid);
resp.setHeader(WSConstants.WS_SESSION_HEADER_LEGACY, sid);
log.debug("HTTP Long-Poll channel opened for session " + sid);
return ps;
@@ -253,7 +244,8 @@ public class CBEventsLongPollingServlet extends HttpServlet {
@NotNull
protected static WebHttpRequestInfo createRequestInfo(
@Nullable String sessionId, @NotNull HttpServletRequest req
@Nullable String sessionId,
@NotNull HttpServletRequest req
) {
return new WebHttpRequestInfo(
sessionId,
@@ -263,6 +255,13 @@ public class CBEventsLongPollingServlet extends HttpServlet {
);
}
void rekeySession(@NotNull String oldSid, @NotNull String newSid) {
CBEventsLongPolling moved = sessions.get(oldSid);
if (moved != null) {
sessions.put(newSid, moved);
}
}
/**
* Resolves a {@link BaseWebSession} from an incoming HTTP request.
* <p>
@@ -306,7 +305,7 @@ public class CBEventsLongPollingServlet extends HttpServlet {
@Override
public BaseWebSession resolve(@NotNull HttpServletRequest req) throws SMException {
String sid = req.getHeader(WSConstants.WS_SESSION_HEADER);
String sid = ServletAppUtils.getHeaderOrLegacy(req, WSConstants.WS_SESSION_HEADER, WSConstants.WS_SESSION_HEADER_LEGACY);
String token = req.getHeader(WSConstants.WS_AUTH_HEADER);
if (CommonUtils.isEmpty(sid) && CommonUtils.isEmpty(token)) {
@@ -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.
@@ -32,7 +32,11 @@ public class CBEventsWebSocket extends CBAbstractWebSocket implements CBWebSessi
private static final Log log = Log.getLog(CBEventsWebSocket.class);
@Nullable
private BaseWebSession webSession;
private volatile BaseWebSession webSession;
@Nullable
private FromUserEventHandler eventProcessor;
@Nullable
private WebSocketPingPongCallback pingPongCallback;
@Override
public void onOpen(Session session, EndpointConfig config) {
@@ -43,18 +47,41 @@ public class CBEventsWebSocket extends CBAbstractWebSocket implements CBWebSessi
} else {
this.webSession = (BaseWebSession) session.getUserProperties()
.get(CBWebSocketServerConfigurator.PROP_WEB_SESSION);
if (this.webSession == null) {
log.debug("No web session for websocket connection, closing");
close();
return;
}
this.webSession.addEventHandler(this);
handleEvent(new WSSocketConnectedEvent(webSession.getApplication().getApplicationRunId()));
log.debug("EventWebSocket connected to the " + webSession.getSessionId() + " session");
session.setMaxIdleTimeout(Duration.ofMinutes(5).toMillis());
session.addMessageHandler(String.class, new FromUserEventHandler(webSession));
session.addMessageHandler(PongMessage.class, new WebSocketPingPongCallback(webSession));
this.eventProcessor = new FromUserEventHandler(webSession);
session.addMessageHandler(String.class, eventProcessor);
this.pingPongCallback = new WebSocketPingPongCallback(webSession);
session.addMessageHandler(PongMessage.class, pingPongCallback);
CBJettyWebSocketManager.registerWebSocket(webSession.getSessionId(), this);
}
}
@Override
public void migrateToSession(@NotNull BaseWebSession newSession) {
BaseWebSession oldSession = this.webSession;
this.webSession = newSession;
if (eventProcessor != null) {
eventProcessor.setWebSession(newSession);
}
if (pingPongCallback != null) {
pingPongCallback.setWebSession(newSession);
}
if (oldSession != null) {
CBJettyWebSocketManager.migrateWebSocket(oldSession.getSessionId(), newSession.getSessionId(), this);
}
log.debug("EventWebSocket migrated to the " + newSession.getSessionId() + " session");
}
private static class FromUserEventHandler extends CBClientEventProcessor implements MessageHandler.Whole<String> {
private FromUserEventHandler(@NotNull BaseWebSession webSession) {
@@ -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.
@@ -39,6 +39,25 @@ public class CBJettyWebSocketManager {
socketBySessionId.computeIfAbsent(webSessionId, key -> new CopyOnWriteArrayList<>()).add(webSocket);
}
/**
* Re-keys a live web socket from the old session id to the new one after a session rotation,
* so that keep-alive pings keep being delivered to it.
*/
public static void migrateWebSocket(
@NotNull String oldSessionId,
@NotNull String newSessionId,
@NotNull CBEventsWebSocket webSocket
) {
List<CBEventsWebSocket> oldSockets = socketBySessionId.get(oldSessionId);
if (oldSockets != null) {
oldSockets.remove(webSocket);
if (oldSockets.isEmpty()) {
socketBySessionId.remove(oldSessionId);
}
}
registerWebSocket(newSessionId, webSocket);
}
public static void sendPing() {
//remove expired sessions
WebAppSessionManager webSessionManager = WebAppUtils.getWebApplication().getSessionManager();
@@ -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.
@@ -88,7 +88,8 @@ public class CBWebSocketServerConfigurator extends ServerEndpointConfig.Configur
throw new RuntimeException(e.getMessage(), e);
}
if (sec.getUserProperties().get(PROP_WEB_SESSION) == null) {
throw new RuntimeException("No web session found for websocket request");
// no throwing exception here. will be handled in onOpen
log.debug("No web session found for websocket request, connection will be closed on open");
}
}
@@ -24,12 +24,16 @@ import org.jkiss.code.NotNull;
public class WebSocketPingPongCallback implements MessageHandler.Whole<PongMessage> {
@NotNull
private final BaseWebSession webSession;
private volatile BaseWebSession webSession;
public WebSocketPingPongCallback(@NotNull BaseWebSession webSession) {
this.webSession = webSession;
}
void setWebSession(@NotNull BaseWebSession webSession) {
this.webSession = webSession;
}
@Override
public void onMessage(PongMessage message) {
if (webSession instanceof WebHeadlessSession) {
@@ -275,6 +275,12 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
if (project == null) {
throw new DBException("Project not found:" + projectId);
}
var customConnectionsEnabled =
ServletAppUtils.getServletApplication().getAppConfiguration().isSupportsCustomConnections();
if (!customConnectionsEnabled && project.isPrivateProject()) {
throw new DBWebExceptionAccessDenied("Access to private project is denied");
}
RMProject rmProject = project.getRMProject();
for (String reqProjectPermission : requireProjectPermissions) {
@@ -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.
@@ -71,7 +71,7 @@ public abstract class WebServiceServletBase extends HttpServlet {
} catch (Exception e) {
log.error(e);
errorMessage = e.getMessage();
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Error processing request: " + errorMessage);
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Error processing request: " + flattenForResponse(errorMessage));
} finally {
if (WebAppUtils.getWebApplication() instanceof ApiCallInterceptor apiCallInterceptor) {
apiCallInterceptor.onApiCallEvent(
@@ -83,6 +83,17 @@ public abstract class WebServiceServletBase extends HttpServlet {
protected abstract void processServiceRequest(WebSession session, HttpServletRequest request, HttpServletResponse response) throws DBException, IOException;
/**
* Replaces control characters (line breaks, tabs, etc.) with spaces so the message can be safely
* embedded into the JSON error body produced by the servlet container's error handler.
*/
private String flattenForResponse(String message) {
if (message == null) {
return null;
}
return message.replaceAll("\\p{Cntrl}+", " ").trim();
}
protected Map<String, Object> getVariables(HttpServletRequest request) {
return gson.fromJson(request.getParameter(REQUEST_PARAM_VARIABLES), MAP_STRING_OBJECT_TYPE);
}
@@ -25,6 +25,7 @@ import jakarta.servlet.http.HttpServletResponse;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.connection.DBPConnectionType;
import org.jkiss.dbeaver.model.rm.RMConstants;
import org.jkiss.dbeaver.registry.DataSourceNavigatorSettings;
import org.jkiss.dbeaver.registry.settings.ProductSettingDescriptor;
@@ -115,6 +116,9 @@ public interface DBWServiceCore extends DBWService {
@NotNull Map<String, Object> connectionConfig
) throws DBWebException;
@NotNull
List<DBPConnectionType> getConnectionTypes(@NotNull WebSession webSession, @Nullable String id);
@WebAction
WebConnectionInfo getConnectionState(WebSession webSession, @Nullable String projectId, String connectionId) throws DBWebException;
@@ -159,17 +163,17 @@ public interface DBWServiceCore extends DBWService {
@NotNull Map<String, Object> connectionConfig
) throws DBWebException;
@WebAction
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATASOURCES_EDIT})
WebConnectionInfo testConnection(
@NotNull WebSession webSession,
@Nullable String projectId,
@Nullable @WebObjectId String projectId,
@NotNull Map<String, Object> connectionConfig
) throws DBWebException;
@WebAction
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATASOURCES_EDIT})
WebNetworkEndpointInfo testNetworkHandler(
@NotNull WebSession webSession,
@Nullable String projectId,
@Nullable @WebObjectId String projectId,
@Nullable String connectionId,
@NotNull WebNetworkHandlerConfigInput nhConfig
) throws DBWebException;
@@ -87,6 +87,11 @@ public class WebServiceBindingCore extends WebServiceBindingBase<DBWServiceCore>
getArgumentVal(env, "projectId"),
getArgumentVal(env, "config")
)
).dataFetcher(
"listConnectionTypes", env -> getService(env).getConnectionTypes(
getWebSession(env),
getArgument(env, "id")
)
)
;
@@ -45,6 +45,7 @@ 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.DBPConnectionType;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.exec.DBCConnectException;
import org.jkiss.dbeaver.model.navigator.DBNDataSource;
@@ -310,17 +311,32 @@ public class WebServiceCore implements DBWServiceCore {
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
);
try {
DataSourceDescriptor testDataSource = getDataSourceDescriptor(webSession, dataSource, configInput, project);
DBPConnectionConfiguration connectionConfiguration
= new DBPConnectionConfiguration(testDataSource.getConnectionConfiguration());
return WebServiceUtils.getDriverProperties(
webSession,
testDataSource.getDriver(),
testDataSource,
connectionConfiguration
);
} catch (DBWebException e) {
log.error("Error getting driver properties", e);
return new WebPropertyInfo[0];
}
}
@NotNull
@Override
public List<DBPConnectionType> getConnectionTypes(@NotNull WebSession webSession, @Nullable String id) {
return DataSourceProviderRegistry.getInstance().getConnectionTypes().stream()
.map(ct -> id == null || id.equals(ct.getId()) ? ct : null)
.filter(Objects::nonNull)
.toList();
}
@Override
public WebConnectionInfo initConnection(
@@ -541,6 +557,9 @@ public class WebServiceCore implements DBWServiceCore {
DataSourceDescriptor dataSource = (DataSourceDescriptor) WebDataSourceUtils.getLocalOrGlobalDataSource(
webSession, projectId, configInput.getConnectionId());
DataSourceDescriptor testDataSource = getDataSourceDescriptor(webSession, dataSource, configInput, project);
testDataSource.setTemporary(true);
WebConnectionInfo connectionInfo = project.addConnection(testDataSource);
connectionInfo.setSavedCredentials(configInput.getCredentials(), configInput.getNetworkHandlersConfig());
try {
ConnectionTestJob ct = new ConnectionTestJob(
testDataSource, param -> {
@@ -557,7 +576,6 @@ public class WebServiceCore implements DBWServiceCore {
}
throw new DBWebException("Connection failed", ct.getConnectError());
}
WebConnectionInfo connectionInfo = project.createConnectionInfo(testDataSource);
connectionInfo.setConnectError(ct.getConnectError());
connectionInfo.setServerVersion(ct.getServerVersion());
connectionInfo.setClientVersion(ct.getClientVersion());
@@ -565,6 +583,8 @@ public class WebServiceCore implements DBWServiceCore {
return connectionInfo;
} catch (DBException e) {
throw new DBWebException("Error connecting to database", e);
} finally {
project.removeConnection(testDataSource);
}
}
@@ -585,11 +605,7 @@ public class WebServiceCore implements DBWServiceCore {
}
testDataSource = (DataSourceDescriptor) dataSource.createCopy(dataSource.getRegistry());
WebDataSourceUtils.setConnectionConfiguration(
testDataSource.getDriver(),
testDataSource.getConnectionConfiguration(),
configInput
);
project.updateDataSourceContainerFromInput(configInput, testDataSource);
if (configInput.getSelectedSecretId() != null) {
try {
dataSource.listSharedCredentials()
@@ -33,7 +33,6 @@ import org.jkiss.dbeaver.model.struct.rdb.DBSSchema;
import org.jkiss.dbeaver.model.struct.rdb.DBSTable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -58,6 +57,7 @@ public class WebDatabaseObjectInfo {
public static final String OBJECT_FEATURE_SCHEMA = "schema";
public static final String OBJECT_FEATURE_SCRIPT = "script";
public static final String OBJECT_FEATURE_SCRIPT_EXTENDED = "scriptExtended";
public static final String OBJECT_FEATURE_SUPPORTS_FULL_DDL = "supportsFullDdl";
private final WebSession session;
private final DBSObject object;
@@ -168,10 +168,21 @@ public class WebDatabaseObjectInfo {
return features.toArray(new String[0]);
}
private void getObjectFeatures(DBSObject object, List<String> features) {
private void getObjectFeatures(@NotNull DBSObject object, @NotNull List<String> features) {
if (object instanceof DBPScriptObject) {
features.add(OBJECT_FEATURE_SCRIPT);
}
if (object instanceof DBPScriptObjectExt) {
features.add(OBJECT_FEATURE_SCRIPT_EXTENDED);
}
if (object instanceof DBPScriptObjectExt2 scriptObjectExt2 && (
scriptObjectExt2.supportsObjectDefinitionOption(DBPScriptObject.OPTION_INCLUDE_NESTED_OBJECTS)
|| scriptObjectExt2.supportsObjectDefinitionOption(DBPScriptObject.OPTION_INCLUDE_COMMENTS)
|| scriptObjectExt2.supportsObjectDefinitionOption(DBPScriptObject.OPTION_INCLUDE_PERMISSIONS))
) {
features.add(OBJECT_FEATURE_SUPPORTS_FULL_DDL);
}
boolean isDiagramSupported = true;
if (object instanceof DBPScriptObject) features.add(OBJECT_FEATURE_SCRIPT);
if (object instanceof DBPScriptObjectExt) features.add(OBJECT_FEATURE_SCRIPT_EXTENDED);
if (object instanceof DBSDataContainer) {
features.add(OBJECT_FEATURE_DATA_CONTAINER);
if (((DBSDataContainer) object).isFeatureSupported(DBSDataContainer.FEATURE_DATA_FILTER)) {
@@ -181,7 +192,9 @@ public class WebDatabaseObjectInfo {
isDiagramSupported = false;
}
}
if (object instanceof DBSDataManipulator) features.add(OBJECT_FEATURE_DATA_MANIPULATOR);
if (object instanceof DBSDataManipulator) {
features.add(OBJECT_FEATURE_DATA_MANIPULATOR);
}
if (object instanceof DBSEntity) {
features.add(OBJECT_FEATURE_ENTITY);
if (object instanceof DBSDataType
@@ -192,8 +205,12 @@ public class WebDatabaseObjectInfo {
features.add(OBJECT_FEATURE_RELATIONAL_ENTITY);
}
}
if (object instanceof DBSSchema) features.add(OBJECT_FEATURE_SCHEMA);
if (object instanceof DBSCatalog) features.add(OBJECT_FEATURE_CATALOG);
if (object instanceof DBSSchema) {
features.add(OBJECT_FEATURE_SCHEMA);
}
if (object instanceof DBSCatalog) {
features.add(OBJECT_FEATURE_CATALOG);
}
if (object instanceof DBSObjectContainer objectContainer) {
features.add(OBJECT_FEATURE_OBJECT_CONTAINER);
try {
@@ -279,7 +279,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
// in that way all related scripts will use the same context and overwrite the user's define context
// So why we need restore the default execution context after refreshing datasource
DBCExecutionContext defaultContext = DBUtils.getDefaultContext(refreshableObject, false);
DBCExecutionContextDefaults contextDefaults = defaultContext.getContextDefaults();
DBCExecutionContextDefaults<?, ?> contextDefaults = defaultContext.getContextDefaults();
refreshableObject.refreshObject(monitor);
if (contextDefaults != null && contextDefaults.getDefaultSchema() != null
&& contextDefaults.getDefaultCatalog() != null) {
@@ -461,6 +461,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
return node.getNodeUri();
}
if (node instanceof DBNDatabaseNode dbNode) {
checkMetadataEditPermission(dbNode);
return renameDatabaseObject(
session,
dbNode,
@@ -545,8 +546,9 @@ public class WebServiceNavigator implements DBWServiceNavigator {
throw new DBWebException("Navigator node '" + path + "' not found");
}
checkProjectEditAccess(node, session);
if (node instanceof DBNDatabaseNode) {
DBSObject object = ((DBNDatabaseNode) node).getObject();
if (node instanceof DBNDatabaseNode dbnDatabaseNode) {
checkMetadataEditPermission(dbnDatabaseNode);
DBSObject object = dbnDatabaseNode.getObject();
DBEObjectMaker objectDeleter = DBWorkbench.getPlatform().getEditorsRegistry().getObjectManager(
object.getClass(), DBEObjectMaker.class);
if (objectDeleter == null || !objectDeleter.canDeleteObject(object)) {
@@ -602,9 +604,15 @@ public class WebServiceNavigator implements DBWServiceNavigator {
}
}
private void checkMetadataEditPermission(@NotNull DBNDatabaseNode node) throws DBException {
if (!node.getDataSourceContainer().hasModifyPermission(DBPDataSourcePermission.PERMISSION_EDIT_METADATA)) {
throw new DBWebException("Structure edit is restricted for this connection");
}
}
private void checkProjectEditAccess(@NotNull DBNNode node, @NotNull WebSession session) throws DBException {
BaseWebProjectImpl project = (BaseWebProjectImpl) node.getOwnerProject();
if (project == null || !hasNodeEditPermission(session, node, project.getRMProject())) {
var project = node.getOwnerProject();
if (!(project instanceof BaseWebProjectImpl bwp) || !hasNodeEditPermission(session, node, bwp.getRMProject())) {
throw new DBException("Access denied");
}
}
@@ -81,6 +81,15 @@ public interface DBWServiceSQL extends DBWService {
@NotNull WebSQLGeneratorOptions options
) throws DBWebException;
@NotNull
@WebAction
WebAsyncTaskInfo asyncGenerateEntityQuery(
@NotNull WebSession session,
@NotNull String generatorId,
@NotNull List<String> nodePathList,
@NotNull WebSQLGeneratorOptions options
) throws DBWebException;
@NotNull
@WebAction
String sqlGenerateResultSetQuery(
@@ -127,15 +136,12 @@ public interface DBWServiceSQL extends DBWService {
@Nullable WebDataFormat dataFormat) throws DBWebException;
@WebAction
WebAsyncTaskInfo asyncNavigateForeignKey(
List<WebSQLQueryResultAssociation> getSqlResultAssociations(
@NotNull WebSession webSession,
@NotNull WebSQLContextInfo contextInfo,
@NotNull String resultsId,
@NotNull WebSQLResultsRow row,
int columnIndex,
@NotNull String associationName,
boolean isReference,
@Nullable WebDataFormat dataFormat) throws DBException;
@Nullable Boolean isReference
) throws DBException;
/**
* Reads dynamic trace from provided database results.
@@ -22,6 +22,7 @@ import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.data.DBDAttributeConstraint;
import org.jkiss.dbeaver.model.data.DBDDataFilter;
import org.jkiss.dbeaver.model.exec.DBCLogicalOperator;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.struct.DBSAttributeBase;
import org.jkiss.utils.CommonUtils;
@@ -41,6 +42,7 @@ public class WebSQLDataFilter {
private int offset;
private int limit;
private String where;
private boolean anyConstraint;
private final List<WebSQLDataFilterConstraint> constraints = new ArrayList<>();
public WebSQLDataFilter() {
@@ -57,9 +59,10 @@ public class WebSQLDataFilter {
this.limit = MAX_ROWS_NUMBER;
}
this.where = CommonUtils.toString(filterProps.get("where"), null);
this.anyConstraint = CommonUtils.toBoolean(filterProps.get("anyConstraint"));
Object constraints = filterProps.get("constraints");
if (constraints instanceof Collection) {
for (Object constrItem : (Collection<?>) constraints) {
if (constraints instanceof Collection<?> col) {
for (Object constrItem : col) {
if (constrItem instanceof Map) {
this.constraints.add(
new WebSQLDataFilterConstraint((Map<String, Object>) constrItem));
@@ -92,10 +95,18 @@ public class WebSQLDataFilter {
return where;
}
/**
* When true constraints are combined with OR instead of AND
*/
public boolean isAnyConstraint() {
return anyConstraint;
}
@NotNull
public static WebSQLDataFilter from(@NotNull DBDDataFilter filter) {
var webFilter = new WebSQLDataFilter();
webFilter.where = filter.getWhere();
webFilter.anyConstraint = filter.isAnyConstraint();
for (DBDAttributeConstraint constraint : filter.getConstraints()) {
webFilter.constraints.add(WebSQLDataFilterConstraint.from(constraint));
}
@@ -103,24 +114,32 @@ public class WebSQLDataFilter {
}
@NotNull
public DBDDataFilter makeDataFilter(@Nullable WebSQLResultsInfo resultInfo) throws DBException {
public DBDDataFilter makeDataFilter(
@NotNull DBRProgressMonitor monitor,
@Nullable WebSQLResultsInfo resultInfo
) throws DBException {
DBDDataFilter dataFilter = new DBDDataFilter();
dataFilter.setWhere(where);
dataFilter.setAnyConstraint(anyConstraint);
if (CommonUtils.isEmpty(constraints)) {
return dataFilter;
}
dataFilter.addConstraints(mapWebConstrainsToDbdConstrains(resultInfo));
dataFilter.addConstraints(mapWebConstrainsToDbdConstrains(monitor, resultInfo));
return dataFilter;
}
@NotNull
private List<DBDAttributeConstraint> mapWebConstrainsToDbdConstrains(@Nullable WebSQLResultsInfo resultInfo) throws DBException {
private List<DBDAttributeConstraint> mapWebConstrainsToDbdConstrains(
@NotNull DBRProgressMonitor monitor,
@Nullable WebSQLResultsInfo resultInfo
) throws DBException {
if (resultInfo == null) {
return getDbdConstraints();
}
List<DBDAttributeConstraint> constraints = generateEmptyConstrains(resultInfo);
fillEmptyConstrains(constraints);
WebSQLUtils.convertConstraintValues(monitor, resultInfo, constraints);
return constraints;
}
@@ -139,14 +158,42 @@ public class WebSQLDataFilter {
private void fillEmptyConstrains(@NotNull List<DBDAttributeConstraint> emptyConstraints) throws DBException {
for (WebSQLDataFilterConstraint webConstr : constraints) {
if (webConstr.getAttributePosition() >= emptyConstraints.size()) {
throw new DBException(MessageFormat.format("Incorrect column position ''{0}'' in order clause", webConstr.getAttributePosition()));
DBDAttributeConstraint dbConstr = findConstraintToFill(emptyConstraints, webConstr);
if (dbConstr.hasCondition()) {
// The attribute already has a condition from a previous constraint
dbConstr = new DBDAttributeConstraint(dbConstr);
dbConstr.reset();
emptyConstraints.add(dbConstr);
}
DBDAttributeConstraint dbConstr = emptyConstraints.get(webConstr.getAttributePosition());
fillEmptyConstraint(dbConstr, webConstr);
}
}
@NotNull
private DBDAttributeConstraint findConstraintToFill(
@NotNull List<DBDAttributeConstraint> emptyConstraints,
@NotNull WebSQLDataFilterConstraint webConstr
) throws DBException {
Integer attributePosition = webConstr.getAttributePosition();
if (attributePosition != null) {
if (attributePosition < 0 || attributePosition >= emptyConstraints.size()) {
throw new DBException(MessageFormat.format("Incorrect column position ''{0}'' in order clause", attributePosition));
}
return emptyConstraints.get(attributePosition);
}
// try by attribute name
String attributeName = webConstr.getAttributeName();
if (!CommonUtils.isEmpty(attributeName)) {
for (DBDAttributeConstraint dbConstr : emptyConstraints) {
if (CommonUtils.equalObjects(dbConstr.getAttributeName(), attributeName)) {
return dbConstr;
}
}
throw new DBException(MessageFormat.format("Constraint attribute ''{0}'' not found in result set", attributeName));
}
throw new DBException("Constraint must specify either attributePosition or attributeName");
}
@NotNull
private DBDAttributeConstraint fillEmptyConstraint(@NotNull DBDAttributeConstraint dbConstr,
@NotNull WebSQLDataFilterConstraint webConstr) {
@@ -168,6 +215,7 @@ public class WebSQLDataFilter {
return dbConstr;
}
@NotNull
private List<DBDAttributeConstraint> getDbdConstraints() {
List<DBDAttributeConstraint> dbConstraints = new ArrayList<>();
for (WebSQLDataFilterConstraint constraint : constraints) {
@@ -182,7 +230,10 @@ public class WebSQLDataFilter {
return dbConstraints;
}
private static int getOriginalVisualPosition(WebSQLDataFilterConstraint constraint, String attributeName) {
private static int getOriginalVisualPosition(
@NotNull WebSQLDataFilterConstraint constraint,
@NotNull String attributeName
) {
Integer attributePosition = constraint.getAttributePosition();
if (CommonUtils.isEmpty(attributeName)) {
@@ -18,6 +18,7 @@ package io.cloudbeaver.service.sql;
public record WebSQLGeneratorOptions(
boolean useFullyQualifiedNames,
boolean compactSql
boolean compactSql,
boolean showFullDdl
) {
}
@@ -195,7 +195,8 @@ public class WebSQLProcessor implements WebSessionProvider {
DBCExecutionContext context = getExecutionContext(dataContainer);
try {
final DBDDataFilter dataFilter = filter.makeDataFilter((resultId == null ? null : contextInfo.getResults(resultId)));
final DBDDataFilter dataFilter = filter.makeDataFilter(
monitor, (resultId == null ? null : contextInfo.getResults(resultId)));
if (dataFilter.hasFilters()) {
sql = context.getDataSource().getSQLDialect().addFiltersToQuery(
monitor,
@@ -346,7 +347,8 @@ public class WebSQLProcessor implements WebSessionProvider {
WebSQLExecuteInfo executeInfo = new WebSQLExecuteInfo();
DBCExecutionContext executionContext = DBUtils.getOrOpenDefaultContext(dataContainer, false);
DBDDataFilter dataFilter = filter.makeDataFilter((resultId == null ? null : contextInfo.getResults(resultId)));
DBDDataFilter dataFilter = filter.makeDataFilter(
monitor, (resultId == null ? null : contextInfo.getResults(resultId)));
DBExecUtils.tryExecuteRecover(monitor, connection.getDataSource(), param -> {
try (DBCSession session = executionContext.openSession(monitor, resolveQueryPurpose(dataFilter), "Read data from container")) {
try (
@@ -479,7 +481,7 @@ public class WebSQLProcessor implements WebSessionProvider {
sendTransactionalEvent(contextInfo);
}
WebSQLQueryResultSet updatedResultSet = new WebSQLQueryResultSet(webSession);
WebSQLQueryResultSet updatedResultSet = new WebSQLQueryResultSet();
updatedResultSet.setResultsInfo(resultsInfo);
updatedResultSet.setColumns(resultsInfo.getAttributes());
@@ -651,7 +653,7 @@ public class WebSQLProcessor implements WebSessionProvider {
DBDAttributeBinding[] allAttributes = resultsInfo.getAttributes();
DBDAttributeBinding[] keyAttributes = rowIdentifier.getAttributes().toArray(new DBDAttributeBinding[0]);
WebSQLQueryResultSet updatedResultSet = new WebSQLQueryResultSet(webSession);
WebSQLQueryResultSet updatedResultSet = new WebSQLQueryResultSet();
updatedResultSet.setResultsInfo(resultsInfo);
updatedResultSet.setColumns(resultsInfo.getAttributes());
@@ -1131,7 +1133,7 @@ public class WebSQLProcessor implements WebSessionProvider {
@NotNull WebSQLExecuteInfo executeInfo,
@NotNull DBDDataFilter filter
) throws DBException {
if (!filter.getConstraints().isEmpty() || !CommonUtils.isEmpty(filter.getWhere())) {
if (filter.getConstraintsCount() > 0 || !CommonUtils.isEmpty(filter.getWhere())) {
StringBuilder where = new StringBuilder();
SQLUtils.appendConditionString(
filter,
@@ -1268,10 +1270,23 @@ public class WebSQLProcessor implements WebSessionProvider {
throws DBException {
if (cellRow instanceof Map<?, ?> variables) {
if (variables.get(FILE_ID) != null) {
Path path = WebAppUtils.getWebPlatform()
String fileId = variables.get(FILE_ID).toString();
try {
// file id must be UUID
UUID.fromString(fileId);
} catch (IllegalArgumentException e) {
throw new DBException("File ID is invalid");
}
Path uploadFolder = WebAppUtils.getWebPlatform()
.getTempFolder(webSession.getProgressMonitor(), TEMP_FILE_FOLDER)
.resolve(webSession.getSessionId())
.resolve(variables.get(FILE_ID).toString());
.normalize();
Path path = uploadFolder.resolve(fileId).normalize();
// ensure the resolved path stays within the session upload directory
if (!path.startsWith(uploadFolder)) {
throw new DBException("File ID is invalid");
}
try {
var file = Files.newInputStream(path);
@@ -54,7 +54,7 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
private final WebDataFormat dataFormat;
@Nullable
private final DBDDataFilter dataFilter;
private final WebSQLQueryResultSet webResultSet;
private final WebSQLQueryResultSet webResultSet = new WebSQLQueryResultSet();
private DBDAttributeBinding[] bindings;
private DBCTrace trace;
@@ -71,7 +71,6 @@ 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);
@@ -21,19 +21,20 @@ 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 org.jkiss.dbeaver.model.struct.rdb.DBSCatalog;
import org.jkiss.dbeaver.model.struct.rdb.DBSSchema;
import java.util.List;
public class WebSQLQueryResultReference {
public class WebSQLQueryResultAssociation {
private static final Log log = Log.getLog(WebSQLQueryResultReference.class);
private static final Log log = Log.getLog(WebSQLQueryResultAssociation.class);
@Nullable
private final WebSession session;
@@ -41,18 +42,23 @@ public class WebSQLQueryResultReference {
private final DBSEntityAssociation association;
private final boolean reverse;
@NotNull
private final List<Integer> columnIndexList;
private final List<WebSQLReferenceColumnMapping> columnMapping;
public WebSQLQueryResultReference(
public WebSQLQueryResultAssociation(
@Nullable WebSession session,
@NotNull DBSEntityAssociation association,
boolean reverse,
@NotNull List<Integer> columnIndexList
@NotNull List<WebSQLReferenceColumnMapping> columnMapping
) {
this.session = session;
this.association = association;
this.reverse = reverse;
this.columnIndexList = columnIndexList;
this.columnMapping = columnMapping;
}
@Property
public boolean isReference() {
return reverse;
}
@NotNull
@@ -63,18 +69,36 @@ public class WebSQLQueryResultReference {
@Nullable
@Property
public String getTargetEntityName() {
public String getTargetCatalogName() {
DBSEntity targetEntity = getTargetEntity();
if (targetEntity == null) {
return null;
}
return DBUtils.getObjectFullName(targetEntity, DBPEvaluationContext.UI);
DBSCatalog catalog = DBUtils.getParentOfType(DBSCatalog.class, targetEntity);
return catalog == null ? null : catalog.getName();
}
@Nullable
@Property
public String getNodePath() {
public String getTargetSchemaName() {
DBSEntity targetEntity = getTargetEntity();
if (targetEntity == null) {
return null;
}
DBSSchema schema = DBUtils.getParentOfType(DBSSchema.class, targetEntity);
return schema == null ? null : schema.getName();
}
@Nullable
@Property
public String getTargetEntityName() {
DBSEntity targetEntity = getTargetEntity();
return targetEntity == null ? null : targetEntity.getName();
}
@Nullable
@Property
public String getTargetNodePath() {
if (session == null) {
return null;
}
@@ -94,8 +118,8 @@ public class WebSQLQueryResultReference {
@NotNull
@Property
public List<Integer> getColumnIndexList() {
return columnIndexList;
public List<WebSQLReferenceColumnMapping> getColumnMapping() {
return columnMapping;
}
@Nullable
@@ -16,7 +16,6 @@
*/
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;
@@ -36,10 +35,6 @@ 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;
@@ -55,8 +50,7 @@ public class WebSQLQueryResultSet {
private boolean readOnly;
private String readOnlyStatus;
public WebSQLQueryResultSet(@Nullable WebSession session) {
this.session = session;
public WebSQLQueryResultSet() {
}
@Property
@@ -79,23 +73,6 @@ public class WebSQLQueryResultSet {
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
@@ -0,0 +1,64 @@
/*
* 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 org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.meta.Property;
public class WebSQLReferenceColumnMapping {
private final int sourceColumnIndex;
@NotNull
private final String sourceColumnName;
private final int targetColumnIndex;
@NotNull
private final String targetColumnName;
public WebSQLReferenceColumnMapping(
int sourceColumnIndex,
@NotNull String sourceColumnName,
int targetColumnIndex,
@NotNull String targetColumnName
) {
this.sourceColumnIndex = sourceColumnIndex;
this.sourceColumnName = sourceColumnName;
this.targetColumnIndex = targetColumnIndex;
this.targetColumnName = targetColumnName;
}
@Property
public int getSourceColumnIndex() {
return sourceColumnIndex;
}
@NotNull
@Property
public String getSourceColumnName() {
return sourceColumnName;
}
@Property
public int getTargetColumnIndex() {
return targetColumnIndex;
}
@NotNull
@Property
public String getTargetColumnName() {
return targetColumnName;
}
}

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