Compare commits

...
Author SHA1 Message Date
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
Ainur 8eb012670d dbeaver/pro#9640 fix loading driver props for old api (#4404) 2026-06-18 14:09:28 +02:00
Ruslan MusaevandAinur 413d229028 dbeaver/pro#9568 move tests to ce (#4402)
* dbeaver/pro#9568 move tests to ce

* dbeaver/pro#9568 move tests to ce

* dbeaver/pro#9568 fix tests

* dbeaver/pro#9568 fix tests

* dbeaver/pro#9568 fix tests

---------

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

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

---------

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

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

* dbeaver/pro#7128 Fix nodes

* dbeaver/pro#7128 Return DS id in objectId

* dbeaver/pro#7128 disable test

* dbeaver/pro#7128 fixes connectionId and node Uri

* dbeaver/pro#7128 use new node path patterns

* dbeaver/pro#7128 FS in root nodes

* dbeaver/pro#7128 FS in root nodes

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

* dbeaver/pro#7128 fix getting connection part

* dbeaver/pro#7128 use node path by default

* dbeaver/pro#7128 remove unused import

* dbeaver/pro#7128 fix tests

* dbeaver/pro#7128 Fix web cloud explorer

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

* dbeaver/pro#7128 support folders new path

* dbeaver/pro#7128 add rm prefix variable

* dbeaver/pro#7128 adjust tests

* dbeaver/pro#7128 Remove deprecated node id property

* dbeaver/pro#7128 use uri in db objects

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

* makes isDatabaseObject less heuristic

* Revert "makes isDatabaseObject less heuristic"

This reverts commit f85acd91f9.

* local fix for folders open action

* disallow to open folders in object viewer

* uses helper to identify node for advanced view correct behavior

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

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

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

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

* dbeaver/pro#7128 isolates unit tests

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

This reverts commit 5bf6adbdd1.

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

* removes mocks

* skips the tests

---------

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

* dbeaver/pro#9120 add tests and fix configs

---------

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

* dbeaver/pro#7923 nullability and move function

---------

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

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

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

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

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

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

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

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

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

---------

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

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

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

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

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

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

---------

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

* dbeaver/pro#8981 fixes

* dbeaver/pro#8981 api ref panel

* dbeaver/pro#8981 api ref panel

* dbeaver/pro#8981 api ref panel

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

* dbeaver/pro#8981 fix tests

* dbeaver/pro#8981 fix review

* dbeaver/pro#8981 lazy loading fix

* dbeaver/pro#8981 separate association and refs

---------

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

* redesigns switch

* fixes dense example

* fixes animations

* removes outline for whole area of switch

* adds tied to the input label

* adds innerChecked

* adds composition to the Switch component

* refactors switch API to make it more composable

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

* fixes dense font weight

---------

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

Updated the contact email for reporting security vulnerabilities.

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

* dbeaver/pro#9508 review fixes

---------

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

* cleanup

* adds DataGridMenuContextProvider

* uses context menu position from the hook useContextMenuPosition

* closes menu if cell is not visible

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

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

* removes redundant event prevent default and stopPropogation

* fixes focus stealing issue

* hides menu on scroll

* cleanup

* pr fixes

* cleanup

* uses cell key to open table

* pr fixes

* simplifies the menu api

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

* cleanup

* removes isMenuOpened guard

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

* removes onClose handling and focus cell in sync workaround

* adds keyboard event handling for the context menu api

* compares cells with keys utils helper

* removes open menu at

* removes setContext

* cleanup

* simplifies interfaces

* fixes bug with menu not being able to rerender

* restores focus on close

* dbeaver/pro#9308 simplify logic

* adds closing menu on scroll

* pr fixes

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

* adds arialabel for icon button

* Revert "adds arialabel for icon button"

This reverts commit a830301f7a.

* adds span to make scrolling work

---------

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

* adds project info to the context menu

* adds project info form

* adds project options part info tab content

* separates logic with bootstraps

* removes unused locales

* pr fixes

* removes global and shared checkboxes

---------

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

* removes all resolutions

* updates deps

* removes CVEs

* updates deps

---------

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

* dbeaver/pro#8766 Boom

* dbeaver/pro#8766 Boom

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

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

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

return selection only when the cell is edited

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

---------

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

* migrates all scss to css

* removes all scss mentions

* fixes input colors

* fixes thumb and link colors

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

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

---------

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

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

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

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

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-27 20:38:22 +02:00
Serge Riderandkseniaguzeeva c14e8f5ce3 dbeaver/pro#9397 Initialize datasource registry flow fix (#4360)
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-05-26 17:46:12 +02:00
dbeaver-devops cc3cf21890 dbeaver/pro#9401 26.1.1 version bump 2026-05-25 20:54:14 +02:00
sergeyteleshevandEvgenia 961430f988 [CB] Menu is displayed under opened forms in the Administration page (#4358)
* dbeaver/pro#9362 fixes z-indexes issues for dialogs and notifications

* moves getPersistentElements to dialog level so all dialogs can handle it

* adds data-dialog-persistent-notification

* fixes resize controls layering

* fixes issue with panel buttons layering

* cleanup

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-25 17:53:03 +02:00
759b30a24b dbeaver/pro#9182 remove global secret controller for server apps (#4340)
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-05-25 17:52:50 +02:00
Sychev AndreyandEvgenia 225ce91fd7 dbeaver/pro#8486 feat: add DataReadonlyReason API for data sources (#4354)
* dbeaver/pro#8486 feat: add DataReadonlyReason API for data sources

Previously TableStatusIndicator relied on model.isReadonly() which lost reasons. This caused the "Read-only connection" tooltip to show in grouping panel even on
editable connections — the connection wasn't read-only, the
presentation was.

Each data source now declares its specific reason via getReadonlyReason(). UI maps the typed reason to an accurate
message

* dbeaver/pro#8486 refactor: remove DataReadonlyReason references and use hasFeature flag

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-25 12:36:20 +02:00
f7bdd80e9f dbeaver/pro#9131 use visual keys for copy & paste (#4350)
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-22 16:55:18 +02:00
sergeyteleshevandDaria Marutkina e42d34ac58 [CB] Ability to add a new MCP agent (#4329)
* dbeaver/pro#9126 initial commit for PR message

* adds validations

* adds json linter

* reverts safeParseSchema

* removes reset button

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-22 10:11:59 +02:00
sergeyteleshevandDaria Marutkina 89a1af2df9 [CB] Fix table spacing regression in admin UI (#4353)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-21 19:49:34 +02:00
sergeyteleshevandDaria Marutkina e4260b4cd2 [CB] API token generation dropdown doesn't fit to the island (#4351)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-21 19:47:45 +02:00
AinurandEvgenia 956ebaa515 dbeaver/pro#9306 disconnect event send fix (#4356)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-21 19:45:55 +02:00
alexandDaria Marutkina bccf47020e dbeaver/pro#9095 load active connections before checking execution context (#4355)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-21 18:22:42 +02:00
alexandEvgenia dcd2b608ea dbeaver/pro#9370 check all passwords, not only the first one (#4357)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-21 18:20:29 +02:00
84af14d2f7 Closes dbeaver/pro#9327 Extract LibSQL driver from build (#4344)
* Closes dbeaver/pro#9327 Extract LibSQL driver from build

* dbeaver/pro#9327 load libsql from maven for cloudbeaver ce

---------

Co-authored-by: MashaKorax <84867187+MashaKorax@users.noreply.github.com>
Co-authored-by: Ainur <ainur.iagudin@dbeaver.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-21 14:52:16 +02:00
sergeyteleshevandEvgenia f513f7dc44 [CB] Many warnings in the console when creating a new connection (#4352)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-21 14:50:12 +02:00
Sychev AndreyandDaria Marutkina ffa7f730de dbeaver/pro#8741 fix: update container label visibility logic when no… (#4347)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-21 12:24:15 +02:00
Sychev AndreyandDaria Marutkina 694d3b241f dbeaver/pro#9342 build: update node version to 24.13.1 in CI workflows (#4349)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-20 21:38:49 +02:00
9c8f3c47e6 [CB] Add shortcut for the cell context menu in data and metadata editors (#4332)
* dbeaver/pro#8424 adds shortcut top open/close context menu for the data editor

* moves helper into parseHotkeys + simplifies it

* adds unit tests

* adds autoFocusOnShow prop to support backward compatibility

* dbeaver/pro#8424 use current state

* adds few test cases for the extra buttons pressed

---------

Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-20 16:38:03 +02:00
sergeyteleshev f918c9e25e dbeaver/pro#8621 hides upload button for viewers roles and for disabled edit setting in data editor (#4348) 2026-05-20 16:37:43 +02:00
b84031bb84 dbeaver/cloudbeaver#4138 feat: support inline editing of multi-line v… (#4341)
* dbeaver/cloudbeaver#4138 feat: support inline editing of multi-line values in data grid cells

- TextEditor: switch <input> to <textarea rows=1> so newlines are preserved when editing. Suppress the trailing insertLineBreak from the Enter that opens the editor (would otherwise wipe the auto-selected value).
- ResultSetFormatAction.truncateText: substitute \n, \r, \r\n, \t with
  visible glyphs (↵, ␍, →) and collapse other control chars to a space
  in a single pass.

Perf note: the new replace should be faster than the prior
split('') + map + join. No intermediate array allocations, and the
callback runs only on matched chars (which are sparse in display text). \p{Cc} also expands coverage from charCode < 32 to include DEL and C1 controls (U+007F, U+0080–U+009F). (closer to Dbeaver behavior)

* dbeaver/pro#4138 feat: show raw text in title for text cells

* dbeaver/pro#4138 treat one cell differently

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com>
2026-05-20 14:28:08 +02:00
af651f9d10 dbeaver/pro#8459 trim username (#4343)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-19 19:00:53 +02:00
sergeyteleshevandDaria Marutkina 3245943f3d [CB] It is impossible to open edit forms from entity tables via keyboard (Users, Teams, Drivers, Providers, Secret Management) (#4345)
* dbeaver/pro#8404 adds onCellKeyDownTableOpen helper to all side modal tables

* replaces onKeyDown with Command

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-19 12:10:59 +02:00
Sychev AndreyandEvgenia fe01b23fce dbeaver/pro#9090 refactor: use new getters for sql execution plan (#4333)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-18 17:36:20 +02:00
alexandDaria Marutkina 8ec052e1b1 dbeaver/pro#9274 update whereFilter even if there is no result (#4336)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-18 13:48:51 +02:00
Daria Marutkina b10b407270 dbeaver/pro#9290 Update README file (#4346)
Added changelog for version 26.0.5, detailing new features, fixes, and updates across various components including authorization, data editor, SQL editor, database drivers, and security improvements.
2026-05-18 12:30:10 +02:00
alexandDaria Marutkina 5f5a6743eb dbeaver/pro#9133 use common GridSelectAction (#4342)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-18 11:22:26 +02:00
sergeyteleshev 4b30dc3ba5 [CB] Refresh of disconnected connection leads to 'Something went wrong' error (#4338)
* dbeaver/pro#8294 allows to refresh only connection root nodes for non-established connections

* renames variable

* checks if connection is connected to refresh it properly

* adds node check instead of connection directly
2026-05-15 13:27:33 +02:00
Ruslan Musaev 86e114d471 dbeaver/pro#9134 fix ldap error log (#4339) 2026-05-15 11:34:15 +02:00
dbeaver-devops d4d2e2d1fa dbeaver/pro#9290 26.1.0 version bump 2026-05-12 19:52:53 +02:00
6670cc81ae dbeaver/pro#7990 qmdb csv export (#4306)
* dbeaver/pro#7990 refactor: make onRemove prop optional in Tag component

* dbeaver/pro#7990 feat: add postForBlob helper function for using with export qm history servlets

* dbeaver/pro#7990 feat: replace postForBlob with submitForm for form submissions

Co-authored-by: Copilot <copilot@github.com>

* dbeaver/pro#7990 tests: add tests for submitForm and objectToFormFields functions

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-12 17:45:59 +02:00
f8a107256b 9089 core optimize command line execution (#4317)
* dbeaver/pro#9089 wip

* dbeaver/pro#9089 optimize cli execution

---------

Co-authored-by: Serge Rider <serge@jkiss.org>
Co-authored-by: Anton Radchenko <34737043+RadAnton@users.noreply.github.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-12 15:30:41 +02:00
4f796e5f9c [CB] Add Ask AI Feature for the Execution Plan (#4326)
* dbeaver/pro#9046 adds vertical tabs for execution plan to add more space for new button or panel with buttons

* adds ask AI feature for execution plan

* removes editor id and uses context state

* makes ai feature spotlighted in the ui

* rotates the icon also

* removes bold label

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-12 13:28:50 +02:00
fb6f3c8246 Dbeaver/cloudbeaver#4120 support token header (#4259)
* dbeaver/cloudbeaver#4120 support token header

* dbeaver/cloudbeaver#4120 support token header

* dbeaver/cloudbeaver#4120 support token header

* dbeaver/cloudbeaver#4120 support token header

* dbeaver/cloudbeaver#4120 support token header

* dbeaver/cloudbeaver#4120 support token header

* dbeaver/cloudbeaver#4120 support token header

* dbeaver/pro#4120 added tests, now session in session holder

* dbeaver/pro#4120 review fix naming

* dbeaver/pro#4120 review fixes

* dbeaver/cloudbeaver#4120 review fixes

* dbeaver/cloudbeaver#4120 session type now final

* dbeaver/cloudbeaver#4120 remove part from ce

* dbeaver/cloudbeaver#4120 remove part from ce

* dbeaver/cloudbeaver#4120 fix header name

* dbeaver/cloudbeaver#4120 review fix

* dbeaver/cloudbeaver#4120 review fix

* dbeaver/cloudbeaver#4120 fix review

---------

Co-authored-by: Serge Rider <serge@jkiss.org>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-11 16:15:39 +02:00
sergeyteleshev f3eaed955f [CB] Add ability to interrupt connection establishment (front-end) (#4321)
* dbeaver/pro#8810 adds cancel action for auth via link

* lint fix + handles edge case when no popup appeared

* reverts eslint fix

* adds synchronization to connection init with oauth cancelling to remove stuck loading state

* reverts abort logic

* removes onAuthCancelled
2026-05-11 16:14:13 +02:00
339f19ff24 dbeaver/pro#9096 add ability for restricting ssh connections (#4320)
* dbeaver/pro#9096 add ability for restricting ssh connections

* dbeaver/pro#9096 validate ssh tunnelling for te desktop

* dbeaver/pro#9096 remove unused constant

* dbeaver/pro#9096 show alert if ssh is disabled

* Revert "dbeaver/pro#9096 show alert if ssh is disabled"

This reverts commit cce94063b0.

* dbeaver/pro#9096 do not show ssh for TE product

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: naumov <iamemptyhuh@gmail.com>
2026-05-11 15:52:51 +02:00
Dmitrii BarnukovandDaria Marutkina 0229ed1327 dbeaver/pro#9241 updated postgres to 42.7.11 (#4330)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-09 06:57:13 +02:00
sergeyteleshevandDaria Marutkina 6a6c29f38e [CB] Auth form - change display of saved secrets from placeholder to asterisks (#4327)
* dbeaver/pro#8416 adds masks for connection fields with password

* adds placeholder for password with mask

* Revert "dbeaver/pro#8416 adds masks for connection fields with password"

This reverts commit 4f89c46207.

* keeps old translation for file

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-08 17:39:55 +02:00
Serge Rider c3054c1f26 Merge remote-tracking branch 'origin/devel' into devel 2026-05-08 17:22:46 +02:00
Serge Rider 20b129d5a2 Revert "dbeaver/cloudbeaver#4249 added session rotation (#4274)"
This reverts commit d79054a5d8.
2026-05-08 17:21:40 +02:00
alexandkseniaguzeeva a08b9c2f2b dbeaver/pro#9030 remove dead links (#4322)
* dbeaver/pro#9030 remove dead links

* dbeaver/pro#9030 show support only in administration part

---------

Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-05-08 12:16:29 +02:00
sergeyteleshev 3a250e2e7a dbeaver/pro#9239 fixes top bar so it doesn't overlap the context menu (#4328) 2026-05-07 17:39:51 +02:00
alexandEvgenia 951b51b403 dbeaver/pro#9132 use closest instead of current (#4325)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-07 17:39:22 +02:00
72fcd7bfe2 [CB] Bump NPM dependencies (#4270)
* dbeaver/pro#8943 updates yarn locks

* updates vite

* updates vite-plugin-svgr

* removes CVEs

* reverts vite to update it later

* updates yarn lock

* updates yarn lock

* updates uuid

* removes axios CVE

* reverts @codemirror/search in order to fix the issue with replace 1 symbol (broken focus next)

* locks versions of other codemirror packages in order to keep versions consistent

---------

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-05-07 11:25:25 +02:00
Dmitrii Barnukov 9ea3de9edf dbeaver/pro#9049 update oauth description (#4323) 2026-05-06 17:10:24 +02:00
sergeyteleshevandEvgenia 729d47fda1 [CB] SQL Query generation: Add the CREATE option for tables (#4316)
* dbeaver/pro#8327 adds CREATE menu item for result set generate SQL menu

* renames create to DDL

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-05 19:12:51 +02:00
Sychev AndreyandEvgenia ebda152b6b dbeaver/pro#9156 build: update packageManager version to yarn@4.14.1 … (#4309)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-05 16:09:30 +02:00
sergeyteleshevandEvgenia 430ae8e5cc [CB-6208] Dropdown is in front of the modal window (#4312)
* dbeaver/pro#5172 adds ui kit popover context with portal in order to reduce popover overlays

* makes menu to be under the dialog's backdrop

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-05 13:33:56 +02:00
sergeyteleshevandDaria Marutkina 97c12d026b dbeaver/pro#8750 adds fields length to the team for input fields (#4315)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-05 12:12:48 +02:00
Serge Rider ea0997d3b0 Merge remote-tracking branch 'origin/devel' into devel 2026-05-04 14:37:12 +02:00
Ainurandkseniaguzeeva 04a2eed596 dbeaver/pro#8724 save agreement id in app config (#4308)
* dbeaver/pro#8724 save agreement id in app config

* dbeaver/pro#8724 fix sending info to marketplace

---------

Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-05-04 14:35:08 +02:00
10d8741403 Make manifest.webmanifest dynamic based on CLOUDBEAVER_ROOT_URI dbeaver/cloudbeaver#4074 (#4293)
This PR implements dynamic generation of the manifest.webmanifest file to respect the CLOUDBEAVER_ROOT_URI environment variable configuration. Previously, the manifest was a static file with hardcoded values that didn't adapt to custom ro
ot URI configurations.

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-05-04 14:34:03 +02:00
Serge Rider ded139584c Merge remote-tracking branch 'origin/devel' into devel 2026-05-04 14:33:52 +02:00
alexandEvgenia 04b3bd9e2b dbeaver/pro#9073 rearrange context items (#4307)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-05-04 12:26:54 +02:00
Daria Marutkina 9303f4dacf dbeaver/pro#9160 Update README file (#4319)
Added changelog for version 26.0.4, detailing new features, improvements, and security updates.
2026-05-04 10:32:57 +02:00
Serge Rider 7b258d7ba6 Merge remote-tracking branch 'origin/devel' into devel 2026-05-04 10:22:22 +02:00
Serge Rider 76c2d4b7fe dbeaver/pro#9197 Data editor annotations (#4314) 2026-05-01 10:39:24 +02:00
Serge Rider 61d0c43bd2 dbeaver/pro#9202 GQL logging fix (#4318) 2026-05-01 10:34:43 +02:00
Serge Rider 0c07a5cd09 dbeaver/pro#9197 Data editor annotations 2026-04-30 14:09:07 +02:00
dbeaver-devops 3dcae7063c dbeaver/pro#9160 26.0.5 version bump 2026-04-28 19:32:51 +02:00
alexandEvgenia 635d2f8d7f dbeaver/pro#8473 add function confirmation (#4301)
* dbeaver/pro#8473 add function confirmation

* dbeaver/pro#8473 pass class to inner

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-04-28 18:37:01 +02:00
Dmitrii BarnukovandEvgenia cea06eecae dbeaver/pro#9047 added sql plan explain to CB (#4302)
* dbeaver/pro#9047 added sql plan explain to CB

* dbeaver/pro#9047 added nullability annotations

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-04-28 15:15:57 +02:00
ElizabethandDiana 0e402cd27d dbeaver/pro#8775 Introduce value path to getCellValue method (#4304)
Co-authored-by: Diana <31996417+uslss@users.noreply.github.com>
2026-04-28 15:12:38 +02:00
Dmitrii BarnukovandEvgenia d79054a5d8 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>
2026-04-28 12:32:41 +02:00
0903025942 [CB] SQL Query generation: add settings for the Generated SQL window (#4275)
* dbeaver/pro#8716 added generate sql options

* dbeaver/pro#8328 adds settings to generate sql dialog

* fixes error message

* removes refresh btn and settings title + notification

* dbeaver/pro#8328 add generator options

* show button "open in sql editor" for all sql generation popups

* adds regenerate query method for different context call

* cleanup

* pr fixes

* adds action.bound

* adds delayed loader

---------

Co-authored-by: Dmitrii <dmitrii.barnukov@dbeaver.com>
Co-authored-by: Dmitrii Barnukov <29237913+Nexus6v2@users.noreply.github.com>
Co-authored-by: Ruslan <pupkinvasya7000@gmail.com>
Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: Nikita Akilov <26031301+flambchap@users.noreply.github.com>
2026-04-28 07:52:24 +02:00
b36b11008f dbeaver/pro#8978 paste multiple values (#4299)
* dbeaver/pro#8978 paste multiple values

* dbeaver/pro#8978 do not use bound on fn method

---------

Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-04-27 17:30:14 +02:00
f8acdb58af dbeaver/pro#8107 [CB] increase the contrast of some elements (#4300)
* dbeaver/pro#8107 fix: caption contrast in disabled checkbox

* dbeaver/pro#8107 fix: contrast for NULL formatter element

* dbeaver/pro#8107 fix: update color for blob formatter to enhance contrast

* dbeaver/pro#8107 fix: enhance contrast for theme elements in dark mode

* dbeaver/pro#8107 fix: update title color for improved contrast

* dbeaver/pro#8107 fix: enhance button secondary colors for improved contrast

Co-authored-by: Copilot <copilot@github.com>

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-04-27 14:04:38 +02:00
Sychev AndreyandCopilot 4e4292376e dbeaver/pro#8961 fix: make checkboxes clickable in main toolbar dropdown menu (#4303)
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 09:22:44 +02:00
serge-rider d5860dc9e8 dbeaver/pro#9005 Optimize manifests 2026-04-25 17:09:19 +02:00
Ruslan Musaev 5301adaed4 dbeaver/pro#5680 removed swagger deps for rest api documentation (#4282) 2026-04-24 16:31:12 +02:00
Serge Rider fc9ef33e1d Merge remote-tracking branch 'origin/devel' into devel 2026-04-22 16:38:10 +02:00
Serge Rider 2a8f34682f dbeaver/pro#9005 Driver provider model 2026-04-22 16:37:33 +02:00
9c6f301fa9 dbeaver/pro#7562 [CB] keep data editor filter state during session (#4261)
* dbeaver/pro#7562 feat: persist data editor filter state after reconnect

* dbeaver/pro#7562 feat: update data viewer to persist filter state

* dbeaver/pro#7562 feat: enhance constraint resolution in WebSQLDataFilter

allow using attrName without attrPosition

* dbeaver/pro#7562 refactor: don't use options

replaced private restorePendingState()  with public restoreViewState(pinnedColumnNames, columnOrderNames?) that takes data directly as arguments

* dbeaver/pro#7562 fix: stale constraints in UI

bug with skipping constraints if any without a position.
if (!prevColumn) {
        return;
      }

* dbeaver/pro#7562 refactor: add method to retrieve pinned column names in GridViewAction

* dbeaver/pro#7562 refactor: simplify state validation using schema

* dbeaver/pro#7562 refactor: simplify state management in useDataViewerPanel

* dbeaver/pro#7562 refactor: move persisted state to IDataViewerPageState

* dbeaver/pro#7562 fix: initial pageState

* dbeaver/pro#7562 refactor: simplify getPinnedColumnNames method in GridViewAction

* dbeaver/pro#7562 refactor: shadowed column renamed to c for clarity

* dbeaver/pro#7562 refactor: change type of value in IPersistedConstraint to unknown

* dbeaver/pro#7562 refactor: remove manual state operations

* dbeaver/pro#7562 refactor: introduce DatabasePersistedStateAction for centralized state persistence

* dbeaver/pro#7562 fix

* dbeaver/pro#7562 refactor: replace reaction with when

* Revert "dbeaver/pro#7562 feat: enhance constraint resolution in WebSQLDataFilter"

This reverts commit 3e0e86f4b2.

Last test showed that we don't need any additional logic on BE. It handles all constraints properly.

* dbeaver/pro#7562 fix: make pinColumns optional

* dbeaver/pro#7562 refactor: simplify pinned column handling

* dbeaver/pro#7562 refactor: initialize store in DatabasePersistedStateAction constructor as observable

* dbeaver/pro#7562 refactor: rename getColumnNameByPosition to getColumnNameAt for clarity

* dbeaver/pro#7562 refactor: move DatabasePersistedStateAction to source level, simplify persisted state sync

- Make DatabasePersistedStateAction a standalone class on DatabaseDataSource instead of per-result action
- Convert options to getter
- Auto-sync constraints to persisted state via autorun + action batching
- Restore view state (pinned columns, column order) in GridViewAction constructor

* dbeaver/pro#7562 refactor: replace DatabasePersistedStateAction with DatabasePersistedStateStore and update related references

* dbeaver/pro#7562 refactor: simplify view-state persistence

- DatabasePersistedStateStore: pure KV (observable.ref + actions)
- DatabaseDataSource: add loadPersistedState() + onPersistedStateLoaded() hook
- DatabaseDataConstraintAction: own the constraint persist
- ResultSetDataSource: thin dispatcher
- GridViewAction: columnsOrder/pinnedColumns become computed getters

* dbeaver/pro#7562 refactor: create universal ColumnReference

* dbeaver/pro#7562 refactor: simplify persistence flow, remove extra guards, simplify persisted data filter state handling

* dbeaver/pro#7562 refactor: simplify GridViewAction column-ref handling and layout resolution

* dbeaver/pro#7562 refactor: per-key reactive persisted state store, drop constraint persistence hacks

* dbeaver/pro#7562 refactor: simplify setOptions method to use superclass implementation

* dbeaver/pro#7562 add missing fields

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com>
2026-04-22 14:52:59 +02:00
sergeyteleshev a2ccd0b0f7 [CB] Support real tab characters in SQL Editor (#4298)
* dbeaver/pro#7864 adds tabulation with actual tab indent for code mirror

* adds EDITOR_INDENT_UNIT to unify indent for sql editor and vqb
2026-04-22 12:35:53 +02:00
acc3e42f1c dbeaver/pro#8352 do not unlink connection (#4278)
* dbeaver/pro#8352 do not unlink connection

* dbeaver/pro#8352 simplify logic

* dbeaver/pro#8352 add connection bar

* dbeaver/pro#8352 revert opened state

* dbeaver/pro#8352 do not fetch hints if not connected

* dbeaver/pro#8352 fix build

* dbeaver/pro#8352 move database icon to core package

---------

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-04-22 12:32:58 +02:00
Sychev Andrey 075fa916f0 dbeaver/pro#8909 fix: collapsible side bar buttons are not visible wi… (#4297) 2026-04-22 11:47:33 +02:00
alexandDaria Marutkina f7b96db2de dbeaver/pro#8895 apply overflow to the grid root div (#4296)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-04-22 11:38:26 +02:00
alex dff9adaf34 dbeaver/pro#9084 update gql schema (#4294)
* dbeaver/pro#9084 update gql schema

* dbeaver/pro#9084 move fields
2026-04-21 10:24:25 +02:00
Ruslan Musaev 86c50a1557 dbeaver/pro#8711 sql execution plan improvements (#4287)
* dbeaver/pro#8711 sql execution plan improvements

* dbeaver/pro#8711 sql execution plan improvements
2026-04-20 17:38:43 +02:00
alexandDaria Marutkina 7d6c05f50c dbeaver/pro#8707 remove folder feature from ui (#4267)
* dbeaver/pro#8707 remove folder feature from ui

* dbeaver/pro#8707 remove folder tree ui for navigator

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-04-20 16:24:43 +02:00
Viktor Kirst 022778f95c dbeaver/pro#9061 Support custom lock manager id in servlet applications (#4291)
* dbeaver/pro#9061 Support custom lock manager id in servlet applications

* dbeaver/pro#9061 Remove unused metadata lock manager overload
2026-04-20 14:52:49 +02:00
Daria Marutkina d752d8ac26 dbeaver/pro#9037 Update README file (#4292)
Added changelog for version 26.0.3, detailing new features, fixes, and security updates.
2026-04-20 11:47:05 +02:00
serge-rider e1a029b238 dbeaver/pro#9060 Optimize manifest dependencies 2026-04-19 16:04:30 +02:00
Serge Rider 644dc2a136 dbeaver/pro#9048 CB NPE fix + QM deadlock fix (#4288) 2026-04-16 21:13:18 +02:00
Alexander Skoblikov 768fe00f6a dbeaver/pro#8839 remove referer (#4279) 2026-04-16 10:35:22 +02:00
dbeaver-devops 1e1f684379 dbeaver/pro#9037 26.0.4 version bump 2026-04-15 22:46:10 +02:00
alexandDaria Marutkina ac3ce9e085 dbeaver/pro#8728 add pricing policy (#4281)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-04-15 16:30:06 +02:00
Sychev AndreyandEvgenia 3cc9c3ce86 dbeaver/pro#8274 feat: add key icons for key columns in data editor (#4283)
* dbeaver/pro#8274 feat: add key icons for key columns in data editor

* dbeaver/pro#8274 feat: change key icon. put to core

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-04-15 16:29:02 +02:00
1660e62391 Save scripts to custom locations (use import/export buttons) (#4258)
* dbeaver/pro#7880 adds MVP for saving script to cloud storage

* removes registerScriptExportController

* finalizes the ui

* adds missing locales

* introduces footer slot to use CommonDialog api

* adds locales

* moves success message to the tab components itself

* pr small fixes

* saves file with timestamp

* send notification with correct filename

* fixes locales

* uses different approach for handling dialog save

* cleanup

* self review fixes

* adds lazy import for export dialog

* uses form API to save the dialog

* fixes imports (tab -> panel)

* uses react state for filename

* build fix

* handles form validation

* sanitizes file name

* removes redundant form layout

* fixes bug: allows to load tree root with useTreeData

* uses context api to resolve export script dialog instead of throwing errors approach

* dbeaver/pro#7880 do not show notification on local export

* dbeaver/pro#7880 remove notification from local export

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: naumov <iamemptyhuh@gmail.com>
2026-04-15 15:12:32 +02:00
Sychev AndreyandEvgenia c27fca442b dbeaver/pro#8687 fix(formatNumber): fix unit selection for floats and… (#4276)
- Use Math.log10 instead of toString().length to count integer digits — toString() on a float
  like 4036.29 has length 7, which incorrectly bucketed thousands-range values into millions
- Fix exponent formula for 3n-digit numbers (e.g. 100000 gave exponent=6→M instead of 3→k)
- Apply precision param d below 1000 (was hardcoded 2); default to 2
- Add formatNumber.test.ts

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-04-15 15:10:57 +02:00
318100300f Dbeaver/pro#8044 add api for database user preference (#4104)
* dbeaver/pro#8044 add api for database user preference

* dbeaver/pro#8044 set to prefstore

* dbeaver/pro#8044 add api for database user preference

* dbeaver/pro#8044 add api for database pref

* dbeaver/pro#8044 add api for database pref

* dbeaver/pro#8044 add api for database pref

* dbeaver/pro#8044 refactor api for database preferences

* dbeaver/pro#8044 refactor api for database preferences

* dbeaver/pro#8044 refactor api for database preferences

* dbeaver/pro#8044 add api for database user preference

* dbeaver/pro#8044 set to prefstore

* dbeaver/pro#8044 add api for database user preference

* dbeaver/pro#8044 add api for database pref

* dbeaver/pro#8044 add api for database pref

* dbeaver/pro#8044 add api for database pref

* dbeaver/pro#8044 refactor api for database preferences

* dbeaver/pro#8044 refactor api for database preferences

* dbeaver/pro#8044 refactor api for database preferences

* dbeaver/pro#8044 review fixes

* dbeaver/pro#8044 review fixes

---------

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-04-15 13:57:51 +02:00
9c9e8e4496 dbeaver/pro#8716 added generate sql options (#4262)
* dbeaver/pro#8716 added generate sql options

* dbeaver/pro#8716 extract typed options

* dbeaver/pro#8716 added annotations

---------

Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-04-14 17:09:12 +02:00
alexandmr-anton-t 4789acb76a dbeaver/pro#8996 remove cve (#4280)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-04-14 13:08:55 +02:00
alexandmr-anton-t 39ad4568d0 dbeaver/pro#8962 add block padding to the menus (#4273)
* dbeaver/pro#8962 add block padding to the menus

* dbeaver/pro#8962 adjust padding size

* dbeaver/pro#8962 make inner radius smaller

* dbeaver/pro#8962 use separate tokens

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-04-13 17:15:02 +02:00
Serge Rider 860b8e2e16 dbeaver/pro#8559 Workbench launch redesign (#4277) 2026-04-10 20:16:35 +02:00
sergeyteleshevandDaria Marutkina 4b9ba00663 [CB] Multi-paste support in Data Editor (#4271)
* dbeaver/pro#8670 adds simple multiple paste for the selected cells

* cleanup

* moves logic to useGridSelectedCellsPaste

* cleanup

* adds getSelectedElementsWithFocused

* cleanup

* pr fixes

* adds readonly guard for paste cells

* adds check for is cell editable

* cleanup

* Revert "cleanup"

This reverts commit 309e254453.

* Revert "adds check for is cell editable"

This reverts commit 00b6b63cfa.

* allows to copy blobs base64 but forbids to paste to blob cell

* reuses isCellEditable

* fixes source

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-04-09 19:39:43 +02:00
MalteandDaria Marutkina 897dd994ba Pass arguments from entrypoint script (#4269)
* dbeaver/cloudbeaver#4268 fix order of arguments

This allows overriding the -web-config argument.

* dbeaver/cloudbeaver#4268 pass arguments to run-cloudbeaver-server.sh

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-04-09 19:39:21 +02:00
e81a150287 Dbeaver/pro#8655 cb ai functions support (#4254)
* dbeaver/pro#8655 added function confirmation and result events

* dbeaver/pro#8655 refactor action function processing

* dbeaver/pro#8655 support function role

* dbeaver/pro#8655 revert ai function changes

* Revert "dbeaver/pro#8655 support function role"

This reverts commit 7343af5e97.

* dbeaver/pro#8655 simplify types

* dbeaver/pro#8867 Add custom WS event handlers. Move AI-related function top pro.

* dbeaver/pro#8655 Remove async tasks from chat workflow

---------

Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: Serge Rider <serge@jkiss.org>
Co-authored-by: Serge Rider <serge@dbeaver.com>
Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com>
2026-04-09 19:38:15 +02:00
0d17208be4 dbeaver/pro#8746 add useQuery hook (#4241)
* dbeaver/pro#8746 add useQuery hook

* dbeaver/pro#8746 add language tokens

* dbeaver/pro#8746 set loading to false in case of abort

* dbeaver/pro#8746 use more advanced useQuery

* dbeaver/pro#8746 fix code style

* dbeaver/pro#8746 remove useQuery hook

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: Serge Rider <serge@jkiss.org>
2026-04-08 20:22:06 +02:00
9c1678c256 dbeaver/pro#8824 Inject lock manager into CloudBeaver RM controllers (#4242)
* dbeaver/pro#8824 Inject lock manager into CloudBeaver RM controllers

* dbeaver/pro#8824 Use application lock manager provider in servlet applications

* dbeaver/pro#8824 Use default application lock manager

* dbeaver/pro#8824 Fix codestyle warnings

---------

Co-authored-by: Daria Markaryan <43252954+DariaMarkaryan@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: Serge Rider <serge@jkiss.org>
2026-04-08 19:23:38 +02:00
Ainurandkseniaguzeeva 321d45bd48 dbeaver/pro#7912 support external oauth in te desktop (#4255)
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-04-08 17:15:40 +02:00
alex 0f33fb5514 dbeaver/pro#4158 add special filter operators (#4264)
* dbeaver/pro#4158 add special filter operators

* dbeaver/pro#4158 rename arg
2026-04-08 13:15:13 +02:00
sergeyteleshevandDaria Marutkina e7648b1b4c [CB] Toggle switches are rendered incorrectly (#4272)
* dbeaver/pro#8946 adds specifity for dense switches

* wraps with field

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-04-08 13:04:34 +02:00
45cfded51f [CB] DB Navigator - add subgroup to context menu to reduce the list of actions (#4246)
* dbeaver/pro#8236 adds submenus for the connection tree

* adds disabled state for submenu with no items

* removes variable

* loads first menus then draws it

* reverts submenu fix

* splits manage handlers from root handlers

* removes loader for manage menu to prevent bug

* cleanup

* removes action leaks for tables

* removes leak of the action delete

---------

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-04-08 11:06:12 +02:00
Ruslan Musaev 9822147d68 dbeaver/pro#8954 fix cloudbeaver driver properties (#4266) 2026-04-07 15:49:40 +02:00
Ruslan MusaevandEvgenia 3b0f52f3ed dbeaver/pro#5124 fix case-insensitive username in import (#4251)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-04-07 13:47:11 +02:00
Daria Marutkina 3b032a275d dbeaver/pro#8893 Update README file (#4263)
* dbeaver/pro#8893 Update README file

Added changelog entry for version 26.0.2 with UI improvements, authentication fixes, database updates, and security vulnerability resolutions.

* Update README.md
2026-04-06 11:17:33 +02:00
Sychev Andrey a11ca992fe dbeaver/pro#7251 feat: add skip navigation feature for improved acces… (#4245)
* dbeaver/pro#7251 feat: add skip navigation feature for improved accessibility

* dbeaver/pro#7251 refactor: replace direct import with lazy loading for SkipNavLinks and SkipNavShortcutsLink

* dbeaver/pro#7251 refactor: use css modules for skip nav styles

* dbeaver/pro#7251 refactor: remove SkipNavBootstrap
2026-04-06 10:59:22 +02:00
serge-rider 5138c73ae5 dbeaver/pro#8894 Fix object search + annotations 2026-04-03 15:28:05 +02:00
sergeyteleshevandmr-anton-t ea36247797 [CB] Replace material ui with native css variables and tailwind utilities (#4188)
* dbeaver/pro#8433 initial version of material ui refactor

* fixes arrow colors

* fixes arrow colors 2

* fixes colors

* pr fixes

* removes layers due to it loses styles specificity race

* fixes autocomplete styles

* adds variables for typography

* moves switch logic to module styles

* removes elevation styles

* removes unused code

* migrates ripple effect to tw class

* Revert "migrates ripple effect to tw class"

This reverts commit 78229ac007.

* uses classname theme-typography for button

* adds elevation styles

* adds elevation styles 2

* moves theme ripple

* reverts scss for sql editor

* reverts scss for sql editor 2

* adds tailwindcss utilities + example

* Revert "adds tailwindcss utilities + example"

This reverts commit 20ca1b787c.

* creates tailwindcss plugin with shadow tokens + spread it across the app

* moves typography to the tailwindcss styles

* refactors sql editor styles

* moves styles from tailwindcss folder to styles

* moves ripple to tailwindcss plugin

* removes unused files

* removes unused file index

* removes redundant tailwindcss import

* adds animation layer for tailwindcss

* refactors animations

* build fix

* removes redundant imports of tailwindcss for styles

* removes layer components to keep some styles for switch

* removes animation from ripple effect

* makes ripple effect more contrast

* fixes fonts across the app to json view has correct font-mono from the tailwindcss box

* deletes module ts

* Revert "removes layer components to keep some styles for switch"

This reverts commit ee8b0e199e.

* fixes race for switch bold style

* changes ripple from 7% to 9%

* Reduce hover opacity in ripple effect (#4260)

* Reduce hover opacity in ripple effect

* Update ripple.css

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-04-02 18:24:19 +02:00
Serge Rider a6a1916299 dbeaver/pro#8894 Fix tests 2026-04-02 16:09:06 +02:00
Viktor Kirst b8699e6bcb dbeaver/pro#8896 Fix driver description (#4257) 2026-04-02 16:01:58 +02:00
Serge Rider 8cff50b0cf dbeaver/pro#8894 Workspace dispose and session cleanup fixes (#4256)
* dbeaver/pro#8894 Workspace dispose and session cleanup fixes

* dbeaver/pro#8894 Fix session cleanup

* Revert "dbeaver/pro#8894 Fix session cleanup"

This reverts commit 170882f69d.

* dbeaver/pro#8894 Fix session cleanup
2026-04-02 11:54:52 +02:00
dbeaver-devops b06a96dc31 dbeaver/pro#8893 26.0.3 version bump 2026-03-31 23:17:03 +02:00
Viktor KirstandMashaKorax 446bd81e08 dbeaver/pro#8392 Add H2 2.4 JDBC driver (#4227)
* dbeaver/pro#8392 Add H2 2.4 JDBC driver

* dbeaver/pro#8392 Fixed h2 driver description

* dbeaver/pro#8392 Add H2 V.2.4 module

* dbeaver/pro#8392 Fixed module name

* dbeaver/pro#8392 Rename H2 v2.4 driver to v3

---------

Co-authored-by: MashaKorax <84867187+MashaKorax@users.noreply.github.com>
2026-03-31 17:28:50 +02:00
Dmitrii Barnukov 09863ea260 dbeaver/pro#7963 extended openurl event, added client event (#4247)
* dbeaver/pro#7963 extended openurl event, added client event

* dbeaver/pro#7963 fix typo
2026-03-31 16:52:46 +02:00
sergeyteleshev 34b4556773 [CB] New and repeat passwords can be different (#4234)
* dbeaver/pro#8572 fixes password validity on blur

* Revert "dbeaver/pro#8572 fixes password validity on blur"

This reverts commit cefd174254.

* adds context to the form for correct validation + handles validation on change also to keep the actual validity state

* adds context check to prevent validation inconsistent behavior

* adds revalidate to handle depended validations cases

* adds useFormCustomInputValidation

* cleanup

* uses useFormCustomInputValidation to avoid regression after refactor
2026-03-31 14:07:47 +02:00
alexandmr-anton-t 9b4c874e51 dbeaver/pro#8805 do not move focus on programmatic tab select (#4248)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-03-31 14:00:57 +02:00
Serge Rider ddbc9789e7 Merge remote-tracking branch 'origin/devel' into devel 2026-03-30 16:43:02 +02:00
Serge Rider 810d0215cb dbeaver#40616 update sql server driver to 13.4.0 2026-03-30 16:42:39 +02:00
alexandEvgenia 3dadc17ff1 dbeaver/pro#8644 add temp icon for base sql tab (#4243)
* dbeaver/pro#8644 add temp icon for base sql tab

* dbeaver/pro#8644 replace old icons

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-03-30 12:20:27 +02:00
sergeyteleshevandEvgenia 8c69985083 dbeaver/pro#8518 adds message for string parameters (#4233)
* dbeaver/pro#8518 adds message for string parameters

* aligned message with dbeaver: "string values must be quoted"

* reverts branding and uses it optionally for bind parameters popup

* renames prop

* makes few versions of alert

* Revert "makes few versions of alert"

This reverts commit 5b9f09009e.

* adds gray message

* adds icon

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-03-27 19:04:39 +01:00
1d5b475482 dbeaver/pro#8553 sql execution plan diagram (#4218)
* dbeaver/pro#8553 feat: add execution plan diagram component

* dbeaver/pro#8553 feat: enhance ExecutionPlanDiagram component with selectedNodeId prop

* dbeaver/pro#8553 feat: add flex-shrink to metric value for better layout

* dbeaver/pro#8553 refactor: remove queryString from IPlanData interface

* dbeaver/pro#8553 feat: integrate execution plan diagram component

* dbeaver/pro#8553 refactor: execution plan diagram

separate state, add l10n, make toolbar composable

* dbeaver/pro#8553 feat: implement keyboard navigation for execution plan nodes

* dbeaver/pro#8553 feat: add translation support for execution plan diagram labels

* dbeaver/pro#8553 fix: execution plan node dimensions

* dbeaver/pro#8553 refactor: move formatNumber to helpers to use it

* dbeaver/pro#8553 feat: add translation support for execution plan diagram API

* dbeaver/pro#8553 docs: add README and API documentation for execution plan diagram component

* dbeaver/pro#8553 feat: adjust cost bar height and collapse indicator position

* dbeaver/pro#8553 feat: update PRIMARY_AXIS_WEIGHT for improved directional navigation

* dbeaver/pro#8553 feat: refactor diagram actions and add collapse state management

* dbeaver/pro#8553 refactor: move execution plan diagram to separate EE plugin

* dbeaver/pro#8553 docs: remove redundant comment

* dbeaver/pro#8553 docs: update comments for source and target anchors in ILayoutEdge interface

* dbeaver/pro#8553 docs: update comment in ILayoutNode interface

* dbeaver/pro#8553 refactor: simplify heavy route computation

* dbeaver/pro#8553 refactor: optimize node handling in computePlanLayout function

* dbeaver/pro#8553 refactor: improve tree structure handling in usePlanTree and buildTree functions

* dbeaver/pro#8553 refactor: simplify usePlanTree

* dbeaver/pro#8553 refactor: extract actions from useExecutionPlanDiagramState

* dbeaver/pro#8553 refactor: improve configuration in execution plan diagram

* dbeaver/pro#8553 refactor: simplify metric rendering in PlanNode component

* dbeaver/pro#8553 feat: collapse by hotkey, not an extra element

* dbeaver/pro#8553 feat: add tooltip hints for plan node interactions

* dbeaver/pro#8553 refactor: remove high cost calculation and add node kind attribute

* dbeaver/pro#8553 style: fix formatting in index.ts for ISqlExecutionPlanViewProps export

* dbeaver/pro#8553 ci: remove unused dependency on @dbeaver/ui-kit

* dbeaver/pro#8553 refactor: remove SQL execution plan diagram translations from locale files

* dbeaver/pro#8553 refactor: update translation keys for SQL execution plan components

* dbeaver/pro#8553 refactor: rename components

* dbeaver/pro#8553 style: const

* dbeaver/pro#8553 refactor: reset execution plan diagram state properties

* dbeaver/pro#8553 refactor: update component imports and extract a component

* dbeaver/pro#8553 refactor: rename PlanNodeKind to EPlanNodeKind and update references

* dbeaver/pro#8553 feat: add zoom icons and update action references

* dbeaver/pro#8553 refactor: enhance translation functionality by adding args interpolation

* dbeaver/pro#8553 fix: Polynomial regular expression used on uncontrolled data

* dbeaver/pro#8553 refactor: update ActionsCapture to use ref for toolbar actions

* dbeaver/pro#8553 refactor: use jsx

* dbeaver/pro#8553 refactor: simplify

* dbeaver/pro#8553 refactor: replace useContext with use in DiagramContext

* dbeaver/pro#8553 refactor: convert ExecutionPlanEdge to memoized component

* dbeaver/pro#8553 refactor: convert ExecutionPlanNode to memoized component

* dbeaver/pro#8553 refactor: unify ref prop in ExecutionPlanNode and ExecutionPlanViewport

* dbeaver/pro#8553 refactor: extract ExecutionPlanViewportContent component and clean up CSS

* dbeaver/pro#8553 refactor: convert ExecutionPlanNodeMetric to memoized component

* dbeaver/pro#8553 refactor: improve usePlanLayout hook by utilizing useEffectEvent for stable callback

* dbeaver/pro#8553 refactor: simplify diagram actions handling in createExecutionPlanDiagram

* dbeaver/pro#8553 refactor:  move out execution plan diagram component from

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com>
2026-03-27 16:07:31 +01:00
sergeyteleshev b779b9d855 [CB] Fix high CVE-2026-33671 in picomatch (#4244)
* dbeaver/pro#8836 fixes picomatch CVE

* fixes happy-dom CVE
2026-03-27 14:24:43 +01:00
Sychev Andrey df676913a0 dbeaver/pro#8808 feat: add shortcut for search in data viewer (#4239)
* dbeaver/pro#8808 feat: add shortcut for search in data viewer

* dbeaver/pro#8808 feat: update shortcut ID for search functionality
2026-03-27 14:22:14 +01:00
fab9d1dd43 dbeaver/pro#7806 remove MenuBarSmallItem (#4224)
* dbeaver/pro#7806 remove MenuBarSmallItem

* dbeaver/pro#7806 remove reakit

* dbeaver/pro#7806 remove extra class

* dbeaver/pro#7806 increase base padding and background color

* dbeaver/pro#7806 use more safe style

---------

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-03-27 14:18:35 +01:00
36dfdc8b33 dbeaver/pro#8825 Add tests for translating SQL CREATE/ALTER TABLE sta… (#4237)
* dbeaver/pro#8825 Add tests for translating SQL CREATE/ALTER TABLE statements with ASCII columns

* dbeaver/pro#8825 improve script normalization

* dbeaver/pro#8825 Update `SQLQueryTranslatorTest` to include `NOT NULL` constraint for ASCII column validation

---------

Co-authored-by: Ivan Gagarkin <gagarkin@Ivans-MacBook-Pro.local>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-03-25 13:51:45 +01:00
Alexander Skoblikovandkseniaguzeeva 8b0a20cfd7 dbeaver/pro#8768 fix federated logout (#4230)
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2026-03-25 08:22:55 +01:00
Sychev AndreyandEvgenia 9553c30ee5 dbeaver/pro#8739 feat: add isEnabled option to tree filter for enhanc… (#4236)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-03-24 19:26:36 +01:00
Sychev AndreyandDaria Marutkina 9c36651aa0 dbeaver/pro#8760 feat: set max width for search panel in SQL editor (#4231)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-03-24 16:44:14 +01:00
sergeyteleshev 24a251cd5b [CB] Fix high CVE-2026-33228 in flatted (2) (#4228) 2026-03-24 16:43:43 +01:00
alexandEvgenia a44ba89cd9 dbeaver/pro#8339 show array value as array (#4229)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-03-24 15:00:59 +01:00
44077ad27a dbeaver/pro#8260 Provide a method to create or restore a WebSession… (#4222)
* dbeaver/pro#8260 Provide a method to create or restore a `WebSession` from `smAccessToken`

* dbeaver/pro#8260 Annotations and constants

---------

Co-authored-by: Ivan Gagarkin <gagarkin@Ivans-MacBook-Pro.local>
Co-authored-by: Serge Rider <serge@jkiss.org>
Co-authored-by: serge-rider <serge@dbeaver.com>
2026-03-24 10:56:19 +01:00
Dmitrii BarnukovandEvgenia 3ae74763ff dbeaver/pro#8635 added message description (#4220)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-03-23 16:42:49 +01:00
Daria Marutkina 45a63f70ee dbeaver/pro#8732 Update README file (#4232)
Added detailed changelog for version 26.0.1, including updates to Data Editor, SQL Editor, general improvements, driver updates, and security fixes.
2026-03-23 12:42:04 +01:00
1bafe29e99 dbeaver/pro#7696 feat: replace slide component with dialog for improv… (#4192)
* dbeaver/pro#7696 feat: replace slide component with dialog for improved UI interaction

* dbeaver/pro#7696 refactor: update persistent elements getter

* dbeaver/pro#7696 fix: notifications persistence

* dbeaver/pro#7696 fix: add min-width to container in RightArea and Administration CSS

* dbeaver/pro#7696 refactor: remove persist prop from SideBarPanel component

make SideBarPanel persist by default

---------

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-03-19 18:11:31 +01:00
Dmitrii Barnukov 10ad601f8b dbeaver/pro#5562 revert hide password if not applicable (#4225) 2026-03-19 10:31:47 +01:00
db9f145609 [CB] refactor data editor features readonly states [2] (#4213)
* dbeaver/pro#8299 adds features to data sources

* adds feature flags for data sources

* adds features to mobx

* pr fixes

* pr fixes

* fixes hasFeature

---------

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-03-18 17:10:16 +01:00
d920227db0 dbeaver/pro#8470 migrate reakit to the ariakit (#4209)
* dbeaver/pro#8470 migrate reakit to the ariakit

* dbeaver/pro#8470 fix format

* dbeaver/pro#8470 remove deprecated TabsBox

* dbeaver/pro#8470 control currentTabId via effect

* dbeaver/pro#8470 restore navigationSettings.hasMultipleRows

* dbeaver/pro#8470 fallback to null for defaultSelectedId

* dbeaver/pro#8470 remove getComputed

---------

Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
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-03-18 17:08:38 +01:00
874760f780 dbeaver/pro#8306 split event enums in gql schema (#4202)
* dbeaver/pro#8306 split event enums in gql schema

* dbeaver/pro#8306 use events from ee sdk

---------

Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-03-18 12:37:15 +01:00
dbeaver-devops 0e286f383f dbeaver/pro#8732 26.0.2 version bump 2026-03-17 20:06:58 +01:00
40385f3b1c [CB] Reuse DnD from base grid component for Data Editor (#4201)
* dbeaver/pro#7159 adds dnd to column body cells + introduces new drop column styles

* adds line on dnd

* removes rdg-cell-column-drop class

* cleanup for class names

* renames to useDnD box

* cleanup

* renames hook

* fixes colors scheme + border line index

---------

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-03-17 18:50:05 +01:00
sergeyteleshevandEvgenia 4bc74883e0 [CB] Improve fuzzy search in autocompletion in the SQL Editor (#4204)
* dbeaver/pro#8418 adds fuzzy search for sql editor proposals

* sorts proposals so startsWith and includes word is showed first

* tunes DEFAULT_FUZZY_THRESHOLD

* tunes DEFAULT_FUZZY_THRESHOLD

* Revert "tunes DEFAULT_FUZZY_THRESHOLD"

This reverts commit 7ac8757215.

* fixes ranging and threshold

* rename variable

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-03-17 18:39:19 +01:00
33d1d97fb7 dbeaver/pro#8515 sync properties state (#4214)
* dbeaver/pro#8515 sync properties state

* dbeaver/pro#8515 fixed null to empty strings props conversion

* dbeaver/pro#8515 do not send property if its in default value

* Revert "dbeaver/pro#8515 fixed null to empty strings props conversion"

This reverts commit 5c4f181edf.

* dbeaver/pro#8515 fix firefox icon

* dbeaver/pro#8515 change prop order

* dbeaver/pro#8515 add comment

---------

Co-authored-by: Dmitrii Barnukov <dmitrii.barnukov@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>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-03-17 17:35:45 +01:00
296e03ec74 7023-cb-find-and-replace-new (#4085)
* dbeaver/pro#7023 feat: add SearchPanel component and related styles to UI-kit
add chevron Icon to UI-kit (used in Search Panel)

* dbeaver/pro#7023 feat: add SearchPanel story

* dbeaver/pro#7023 feat: update SearchPanel integration in CodeMirror

* dbeaver/pro#7023 feat: add onReplaceToggle prop to SearchPanel for toggle functionality

* dbeaver/pro#7023 fix: replace-input styles

* dbeaver/pro#7023 feat: implement search functionality in DataGrid v2

* dbeaver/pro#7023 fix: update react-data-grid workspace reference in yarn.lock

* dbeaver/pro#7023 feat: don't focus cell on replace

* dbeaver/pro#7023 feat: add searchReadOnly prop to DataGrid and GridSearchPanel components

* dbeaver/pro#7023 fix: update styles for search panel

* dbeaver/pro#7023 feat: add theme variables for improved styling in storybook

* dbeaver/pro#7023 feat: update search panel styles in datagrid

* dbeaver/pro#7023 feat: improve search functionality by resetting active match index and triggering search on find actions

* dbeaver/pro#7023 refactor: useGridSearch

* dbeaver/pro#7023 fix: search panel styles

* dbeaver/pro#7023 feat: implement grid search state management

* dbeaver/pro#7023 feat: add refresh functionality to search components

* dbeaver/pro#7023 feat: add history action to refresh search results on undo/redo

* dbeaver/pro#7023 feat: add batch cell update functionality to data grid

That functionality comes in handy when we want to use Undo and Replace All functions together:
Before that commit we used to perform N operations where N is a number of replaced cells. And history would record them as atomic operations, so pressing Undo would revert only the last replace.
setMany enables batch editing and batch reverting, so now we can Replace All and then revert it by using one Undo operation

* dbeaver/pro#7023 refactor: remove unnecessary eslint disable comment in useGridSearch

* dbeaver/pro#7023 feat: select affected cell on history change

* dbeaver/pro#7023 feat: add searchReadOnly prop to DataGridTable for conditional read-only state

* dbeaver/pro#7023 refactor: simplify isReadOnly logic in GridSearchPanel

* dbeaver/pro#7023 refactor: use constants for flags

* dbeaver/pro#7023 refactor: update focus and refresh methods in GridSearchPanel

* dbeaver/pro#7023 refactor: consolidate grid search state update logic

* dbeaver/pro#7023 refactor: update scrollToCell method signature and related usages

* dbeaver/pro#7023 refactor: update handleCellChangeBatch to use ICellChange type

* dbeaver/pro#7023 refactor: remove unwanted code

* dbeaver/pro#7023 feat: refactor search functionality to use standalone functions

* dbeaver/pro#7023 feat: implement useDataGridSearch hook to avoid bloating the  DataGrid component with the search-related logic

* dbeaver/pro#7023 feat: update search functionality to use onReplace for cell value updates

* dbeaver/pro#7023 feat: use Activity component for search panel visibility in DataGrid

* dbeaver/pro#7023 refactor: use common value transformation fn in set and setMany methods

* dbeaver/pro#7023 refactor: rename methods for "unnkeyed" cache

* dbeaver/pro#7023 refactor: unify cell edit history handling and remove batch edit references

* dbeaver/pro#7023 fix: update searchReadOnly logic

* dbeaver/pro#7023 refactor: replace rowsCountRef with useState (Error: Cannot access refs during render)

* dbeaver/pro#7023 refactor: expose getCellClassName via useImperativeHandle

Replaced onCellClassNameChange prop + useEffect with useImperativeHandle as suggested in code review. Using useState setter directly as callback ref either caused infinite re-renders (without deps) or lost context synchronization entirely (with stable deps). Settled on useCallback ref that sets both useRef (for method access) and useState (for context propagation).

* dbeaver/pro#7023 fix: Cmd+F not re-focusing the search panel when already open

* dbeaver/pro#7023 refactor:  cell edit history compression

* dbeaver/pro#7023 fix: refresh search state on revert

* dbeaver/pro#7023 refactor: move focus blocking during replace into grid component

Previously DataGridTable checked isReplacing() before syncing
focus/selection, leaking grid internals
Now the grid's selectCell blocks during replacing internally and
supports deferred mode (setTimeout + rAF) for post-render focus sync.

* dbeaver/pro#7023 refactor: restructure grid search logic with useReducer and improve state management

* dbeaver/pro#7023 refactor: improve grid search

moved ref synchronization out of render,
unified search+scroll into one shared path,
added a SET_MATCHES no-op guard to reduce unnecessary updates,
removed duplicate open=false persistence from useDataGridSearch (single owner now in useGridSearch).

* dbeaver/pro#7023 refactor: update search state types

* dbeaver/pro#7023 refactor: restructure grid search state interfaces for improved clarity

* dbeaver/pro#7023 refactor: optimize match retrieval logic after removal in grid search

* dbeaver/pro#7023 refactor: sync refs and computed props in one effect

* dbeaver/pro#7023 fix: Esc double handling between SearchPanel and CaptureView hotkeys

* dbeaver/pro#7023 refactor: fix review comments in useGridSearch

* dbeaver/pro#7023 refactor: unify hotkey stop propagation via shared EventContext/EventStopPropagationFlag

* dbeaver/pro#7023 refactor: pass reactive cellText to useGridSearch

* dbeaver/pro#7023 refactor: update grid search reducer actions and navigation logic

reuse SET_ACTIVE_MATCH and computeActiveMatchIdx for consistency

* dbeaver/pro#7023 refactor: improve equality check for cell matches

* dbeaver/pro#7023 feat: implement cell change tracking subscriptions and cleanup logic

* dbeaver/pro#7023 refactor: remove redundant clearTrackedCellSubscriptions

Cleanup implemented in useTrackedGridSearchCellSubscriptions hook

* dbeaver/pro#7023 fix: cancel stale deferred selectCell to prevent infinite focus loop

When multiple rows are deleted, competing deferred selectCell timeouts
cause focusSyncRef mismatch and endless focus-change cycle.

* dbeaver/pro#7023 fix: update keyboard shortcut for search to use 'KeyF'

* dbeaver/pro#7023 feat: make search disabled by default

* dbeaver/pro#7023 fix: layout

* dbeaver/pro#7023 fix: update scrollBox selection method in Table component

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Viktor Kirst <viktor.kirst@dbeaver.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-03-17 17:28:02 +01:00
2abdbce8ff dbeaver/pro#7566 feat: add settings functionality to the new tree (#4184)
* dbeaver/pro#7566 feat: add settings functionality to the new tree

Implemented settings as a part of Toolbar. The key reason was that the TreeToolbar sets different contexts for all other actions related to tree and easily extendible as a menu service. It might limit in some weird cases, but for now it looks like a valid decision

Settings are made as unknown intentionally to be polymorphic. We could use Record<string, unknown> instead, but I don't see it really improving anything.

* dbeaver/pro#7566 refactor: introduce ITreeSettings interface

add filter with settings connection via useTreeFilterState hook

* dbeaver/pro#7566 refactor: remove redundant hooks, update ITreeSettings

* dbeaver/pro#7566 refactor: move actions to cloudbeaver part

* dbeaver/pro#7566 feat: add universal hook that persists any tree settings via UserDataService as a flat key-value record

* dbeaver/pro#7566 refactor: remove unused childrenTransformers and stateTransformers from ITreeFilterState

* dbeaver/pro#7566 feat: enhance tree settings functionality with replace method and user data synchronization

* dbeaver/pro#7566 refactor: remove toolbarFilter from ITreeFilterState interface

* dbeaver/pro#7566 refactor: simplify useUserTreeSettings

* dbeaver/pro#7566 fix: lint rule and defaults inconsistency

* dbeaver/pro#7566 fix: defaults

* dbeaver/pro#7566 fix: update Russian translations for navigation tree settings

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-03-17 17:21:35 +01:00
Tsimafei Labanovich 051695451b dbeaver/pro#8267 Fix git comments (#4189) 2026-03-17 12:28:21 +00:00
sergeyteleshevandEvgenia 5b7a7fc3d6 [CB] Disable constraint on SQL generation for LOBs (#4215)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-03-17 12:30:28 +01:00
sergeyteleshev 0ba013e7ad [CB] fix high CVE-2026-32141 in flatted (#4217) 2026-03-17 12:27:11 +01:00
Dmitrii Barnukov 2d2f7476b4 dbeaver/pro#5562 removed auth password from props if not applicable (#4216) 2026-03-17 08:33:01 +01:00
23a0ad49b9 dbeaver/pro#8627 Redesign node extensions (#4212)
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-03-16 19:58:57 +01:00
Dmitrii BarnukovandEvgenia d21a5aac7f dbeaver/pro#8637 enable search inside names by default (#4219)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-03-16 17:30:02 +01:00
f9cd51db0f [CB] add ability for data grid selection to apply for context menu (#4196)
* dbeaver/pro#8187 Adds right click for the cell menu

* supports multiple pin/unpin columns

* applies selection for the features: duplicate, delete, revert, set to NULL

* fixes pin column for 1 column selected

* renames to multiple pin/unpin columns

* reverts sandwich

* adds multiple add row action

* dbeaver/pro#8601 TE + Git fixes (#4193)

* dbeaver/pro#8601 Fix event handlers on RM server

* dbeaver/pro#8601 Fix RM node icons

* dbeaver/pro#8601 Fix RM project obtain

* dbeaver/pro#8592 support new headers (#4190)

* dbeaver/pro#7657 avoid blicking by debounce loading state (#4174)

* dbeaver/pro#7657 avoid blicking by debounce loading state

* dbeaver/pro#7657 use existing hooks

* dbeaver/pro#7657 keep the old behaviour

---------

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

* dbeaver/pro#8298 fix: preserve query param order, fix incorrect repla… (#4164)

* dbeaver/pro#8298 fix: preserve query param order, fix incorrect replacement, add tooltip

* dbeaver/pro#8298 refactor: rename ordered prop to sortByName and update sorting logic

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>

* fix: on selected cell menu click opens menu

* fix: duplicates on right click 1 row at time

* removes selection on cell click + removes menu sandwich for selected multiple cells

* value panel, upload, download features available only for 1 selected cell

* adds translations for the multiple rows/columns actions

---------

Co-authored-by: Serge Rider <serge@jkiss.org>
Co-authored-by: Alexander Skoblikov <aleksandr.skoblikov@dbeaver.com>
Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
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-03-15 10:59:32 +01:00
GeorgiiandDiana 3687a645b6 dbeaver/pro#8591 clickhouse driver update (#4195)
Signed-off-by: Georgii Gvinepadze <fireshowel@gmail.com>
Co-authored-by: Diana <31996417+uslss@users.noreply.github.com>
2026-03-13 15:05:34 +01:00
c142996db0 dbeaver/pro#8497 added constraint attribute name (#4206)
* dbeaver/pro#8497 added constraint attribute name

* dbeaver/pro#8497 fix: deleteConstraint call

we can use ! here because we check for currentConstraint in the code above which we get by comparing constraint.attributePosition === attributePosition, so we can be sure the attributePosition prop is present

---------

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-03-13 14:45:10 +01:00
Dmitrii BarnukovandAinur 36d2b46496 dbeaver/pro#8331 added sql generation for large objects (#4208)
* dbeaver/pro#8331 added sql generation for large objects

* dbeaver/pro#8331 check only relevant columns

---------

Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
2026-03-13 12:46:43 +01:00
Ruslan Musaevandmr-anton-t cb8b1cfc52 dbeaver/pro#8467 fix npe read only status (#4183)
* dbeaver/pro#8467 fix npe read only status

* dbeaver/pro#8467 fix navigator model read write lock

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-03-13 12:13:24 +01:00
743afcaad7 dbeaver/pro#7565 feat: implement drag-and-drop functionality in the n… (#4179)
* dbeaver/pro#7565 feat: implement drag-and-drop functionality in the new tree component

* dbeaver/pro#7565 refactor: optimize node state handling in drag-and-drop functionality

this will avoid additional re-renders in some cases

* dbeaver/pro#7565 refactor: simplify drag-and-drop context handling in useNodeDnD

* dbeaver/pro#7565 fix: add extra check to avoid redundant calls

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com>
2026-03-12 23:38:15 +01:00
sergeyteleshevandDaria Marutkina 52c78e1aee [CB] Fix high CVE-2025-45141 in tar (#4211)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-03-12 23:25:16 +01:00
alexandmr-anton-t ff275c36b6 dbeaver/pro#8493 add fetch size tooltip (#4203)
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2026-03-12 15:42:13 +01:00
9a94639167 [CB] Error in the console when opening the admin panel (#4194)
* dbeaver/pro#5495 removes formState hack in ServerConfig

* Revert "dbeaver/pro#5495 removes formState hack in ServerConfig"

This reverts commit 1635e60b38.

* respects formState if it is null

* cleanup

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Viktor Kirst <viktor.kirst@dbeaver.com>
2026-03-11 16:41:17 +01:00
alex 74d09ee4dc dbeaver/pro#8665 always show tabs for the right panel (#4210) 2026-03-11 16:37:31 +01:00
360aaed795 [CB] Bump NPM dependencies (#4186)
* dbeaver/pro#8307 updates yarn lock

* updates eslint

* updates graphql libs

* updates @types/whatwg-mimetype

* updates whatwg-mimetype

* covers evaluate with unit tests

* updates subscript

* removes Rollup CVE

* removes CVE for storybook

* removes CVE for koa

* removes CVE for minimatch

* removes CVE for ajv

* removes redundant types

* cleanup

* updates yarn.lock

* reverts eslint

* updates yarn.lock

* updates yarn lock

* reverts subscript version

* fixes immutable CVE

* fixes serialize-javascript CVE

* updates yarn lock

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: Serge Rider <serge@jkiss.org>
2026-03-11 13:27:17 +01:00
912ca3fdcd dbeaver/pro#8394 fix ability to test ssh config for the existing conn… (#4166)
* dbeaver/pro#8394 fix ability to test ssh config for the existing connection

* dbeaver/pro#8394 pass the same config to test tunnel

* dbeaver/pro#8394 fix checking not empty for ssh password

* dbeaver/pro#8394 fix after review

---------

Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-03-11 12:33:04 +01:00
Ruslan MusaevandDaria Marutkina 32bbfc3a37 dbeaver/pro#8149 fix audit logging ip (#4182)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-03-11 12:32:38 +01:00
1e05ad9eea dbeaver/pro#8223 add new events for messages (#4110)
* dbeaver/pro#8223 add new events for messages

* dbeaver/pro#7138 add error alert variant

* dbeaver/pro#7138 add size for the icon

---------

Co-authored-by: naumov <iamemptyhuh@gmail.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-03-11 12:32:03 +01:00
Serge RiderandEvgenia 4b516c770d dbeaver/pro#8351 Disable devel mode by default (#4136)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-03-11 12:31:37 +01:00
Serge Rider 4e1fed8177 dbeaver/pro#8626 Refactor QM model init (#4200) 2026-03-10 18:59:22 +01:00
alexandEvgenia 4b30d1073b dbeaver/pro#8355 throttle hover and menu showing (#4176)
* dbeaver/pro#8355 throttle hover and menu showing

* Revert "dbeaver/pro#8355 throttle hover and menu showing"

This reverts commit a88f7aee8b.

* dbeaver/pro#8355 mark hover update as non urgent and non-blocking

* dbeaver/pro#8355 use useHover hook for data grid

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-03-10 15:48:48 +01:00
269128360d dbeaver/pro#8298 fix: preserve query param order, fix incorrect repla… (#4164)
* dbeaver/pro#8298 fix: preserve query param order, fix incorrect replacement, add tooltip

* dbeaver/pro#8298 refactor: rename ordered prop to sortByName and update sorting logic

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2026-03-09 17:28:02 +01:00
alexandEvgenia 07db19635e dbeaver/pro#7657 avoid blicking by debounce loading state (#4174)
* dbeaver/pro#7657 avoid blicking by debounce loading state

* dbeaver/pro#7657 use existing hooks

* dbeaver/pro#7657 keep the old behaviour

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2026-03-09 14:12:16 +01:00
Alexander Skoblikov 41d9ac4296 dbeaver/pro#8592 support new headers (#4190) 2026-03-05 17:57:52 +01:00
Serge Rider a536a93ea3 dbeaver/pro#8601 TE + Git fixes (#4193)
* dbeaver/pro#8601 Fix event handlers on RM server

* dbeaver/pro#8601 Fix RM node icons

* dbeaver/pro#8601 Fix RM project obtain
2026-03-05 17:54:46 +01:00
Alexander Skoblikov aefdca73fa dbeaver/pro#8557 log null host (#4187) 2026-03-04 12:18:26 +01:00
sergeyteleshevandDaria Marutkina 5d402d0c20 Confusing "something went wrong" in case of network issues (#4172)
* dbeaver/pro#8468 adds server is not available network error

* removes localization

* renames method for network state issues

* adds comment

* adds explanations for dev and prod mode errors

* supports multiple browser with network errors

* moves logic to a single method + reuses for events

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-03-03 10:41:04 +01:00
Alexander SkoblikovandSerge Rider fcd1924853 dbeaver/pro#8479 annotation for non executable options (#4170)
Co-authored-by: Serge Rider <serge@jkiss.org>
2026-03-03 09:47:29 +01:00
Malcolm-B-Breaks 05c0aafb53 [CB] Small edit in wording (#4154) 2026-03-02 13:54:14 +01:00
Daria Marutkina 159b1b8bd6 dbeaver/pro#8483 Update README file (#4181)
Updated changelog for version 26.0.0, detailing new features, improvements, and fixes since version 25.3.0.
2026-03-02 11:19:37 +01:00
Tsimafei Labanovich f03c08bde7 dbeaver/pro#8120 Fix kerbeors properrties (#4178) 2026-02-27 11:16:01 +00:00
serge-rider 1b07a66192 dbeaver/pro#8105 Cleanup Http constants 2026-02-27 07:20:39 +01:00
Serge Rider ce1686e0eb dbeaver/pro#8504 Deadlock fix (#4175)
* dbeaver/pro#8504 Deadlock fix

* dbeaver/pro#8504 Deadlock fix
2026-02-26 12:17:58 +01:00
091160a59a dbeaver/pro#8441 fix ssl parameters (#4162)
Co-authored-by: Serge Rider <serge@jkiss.org>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2026-02-25 16:12:00 +01:00
dbeaver-devops 47d2377b39 dbeaver/pro#8483 26.0.1 version bump 2026-02-24 12:27:49 +01:00
1366 changed files with 26431 additions and 12450 deletions
+1 -1
View File
@@ -46,7 +46,7 @@ jobs:
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: "lts/*"
node-version: "24.13.1"
cache: "yarn"
cache-dependency-path: |
cloudbeaver/webapp/yarn.lock
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: "lts/*"
node-version: "24.13.1"
cache: "yarn"
cache-dependency-path: "./cloudbeaver/webapp/yarn.lock"
+4 -5
View File
@@ -6,6 +6,7 @@ on:
- opened
- synchronize
- reopened
- ready_for_review
push:
branches: [devel]
@@ -24,11 +25,6 @@ jobs:
project-deps: ./cloudbeaver/project.deps
timeout-minutes: 5
# build-server:
# name: Server
# uses: ./.github/workflows/backend-build.yml
# secrets: inherit
build-frontend:
name: Frontend
uses: ./.github/workflows/frontend-build.yml
@@ -38,6 +34,9 @@ jobs:
name: Server
uses: dbeaver/dbeaver-common/.github/workflows/java-checkstyle.yml@devel
secrets: inherit
permissions:
contents: read
pull-requests: read
lint-frontend:
name: Frontend
+1 -2
View File
@@ -14,7 +14,6 @@
"redhat.vscode-yaml",
"streetsidesoftware.code-spell-checker",
"streetsidesoftware.code-spell-checker-russian",
"syler.sass-indented",
"github.copilot-chat",
"yzhang.markdown-all-in-one",
"GraphQL.vscode-graphql-syntax",
@@ -22,4 +21,4 @@
"esbenp.prettier-vscode",
"devnaumov.licensify"
]
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vscode-java-configs",
"packageManager": "yarn@4.9.2",
"packageManager": "yarn@4.14.1",
"devDependencies": {
"typescript": "^5.8.3"
}
+93 -87
View File
@@ -5,13 +5,16 @@
Cloud Database Manager - Community Edition.
CloudBeaver is a web server that provides a rich web interface. The server itself is a Java application, and the web part is written in TypeScript and React.
It is free to use and open-source (licensed under [Apache 2](https://github.com/dbeaver/cloudbeaver/blob/devel/LICENSE) license).
See our [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
<a><img src="https://github.com/dbeaver/cloudbeaver/wiki/images/connection-creation-demo.png" width="400"/></a>
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/gis-demo.png" width="400"/>
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/data-transfer-demo.png" width="400"/>
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/sql-editor-demo.png" width="400"/>
## Documentation
- [GitHub WIKI](https://github.com/dbeaver/cloudbeaver/wiki)
- [Official documentation](https://dbeaver.com/docs/cloudbeaver/)
## Run in Docker
- [Official Docker repository](https://hub.docker.com/r/dbeaver/cloudbeaver)
@@ -19,97 +22,100 @@ See our [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
## Demo server
You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
You can see a live demo of CloudBeaver server here: https://demo.cloudbeaver.io
[Database access instructions](https://github.com/dbeaver/cloudbeaver/wiki/Demo-Server)
## Changelog
### 25.3.5 2026-02-16
### 26.1.1 2026-06-22
### Changes since 26.1.0:
- General: Added a "Project Info" tab containing the project description to the project context menu.
- Security:
- 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.
- Fixed the critical vulnerability (CVE-2026-9277) in the shell-quote library (updated to version 1.8.4).
### 26.1.0 2026-06-01
### Changes since 26.0.0:
- Administration:
- Added support for custom startup arguments when running CloudBeaver in a container. Administrators can specify configuration file locations to adapt the application to their environment. (thanks to @MalteHei)
- Updated the manifest.webmanifest file to generate dynamically based on the CLOUDBEAVER_ROOT_URI environment variable. (thanks to @houssemexo26)
- Authorization:
- Added HTTP header authentication support for GraphQL queries. Users can provide API tokens via Authorization or X-Api-Key headers, allowing external data tools to authenticate without modifying request bodies.
- Fixed a bug in the authentication form: the existing password is now required for password changes.
- SQL Editor:
- Updated the SQL Editor to use real tab characters for indentation. The application displayed tabs as four-space gaps with a distinct visual marker.
- Added an ability to view and edit scripts without connecting to the database.
- Added the ability to open generated queries right from the Generate SQL window.
- Added fuzzy search to SQL Editor auto-completion for object names. The feature allows users to find tables and containers even with typos or incorrect letter ordering.
- Added the tooltip about quoting rules for the Bind variables parameters dialog.
- Fixed keyword autocompletion for partially typed keywords to avoid redundant letters.
- Data Editor:
- Added the ability to automatically generate INSERT, SELECT, DELETE, and UPDATE statements for the selected values in the Data Editor;
- Added a status indicator icon in the top-left corner of the Data Editor with tooltips explaining table editability. The icon shows whether the table has a primary key, is read-only due to connection settings, or contains only read-only columns.
- Navigator:
- The Connection Info tab was added so that all users can see basic information about the current connection;
- Added the ability to duplicate connection configuration in the project navigation tree. The "Clone connection" feature is available in the context menu.
- Added the ability 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.
- Accessibility:
- Added the Skip to content option for quick keyboard access to the Navigator Tree, editors, and shortcuts list tab to improve application accessibility.
- Improved keyboard navigation for context menu and buttons for Data Editor, SQL Editor, and Navigator tree.
- Added the Ctrl + / keyboard shortcut to open the context menu for selected cells in the Data Editor.
- Fixed the ability to switch tabs in the Data Editor using the keyboard.
- Fixed contrast for elements across different application parts in the light and dark themes to meet WCAG requirements.
- General:
- The missing pg_dump utility no longer causes errors during initialization or during deployment updates when PostgreSQL is selected as the inner database;
- DuckDB: Driver version was updated to 1.4.4.0;
- PostgreSQL: Added DDL display support for PostgreSQL policies.
### 25.3.4 2026-02-02
- Editors:
- Added undo and redo functionality to the Data Editor for cell edits, row operations, and other data modifications. The history retains the last 50 actions across the Data Editor, result sets, and related panels;
- The SQL preview panel has been added to the Bind parameters/variables dialog in the SQL Editor, allowing users to review the SQL query with changed values on the fly;
- The Tab key can now be used for autocompletion in the SQL editor in the same way as the Enter key;
- Fixed an application freeze caused by fetch size canceling for large tables in the Result Set;
- Fixed an issue where pressing the Delete key during inline cell editing incorrectly marked the entire row for deletion.
- Navigator:
- Added independent view preference settings for database connections at both the user and connection levels. Users can configure their preferred navigator view separately from connection-specific settings;
- Added "Show children of tables" option to Navigator tree settings. When enabled, the navigator displays nested objects such as columns under tables and views. The setting applies globally across all connections and remains disabled by default;
- Added the ability to rename connections via context menu in the Navigator Tree;
- General:
- Expanded pointer target areas for icons in the navigator, editors, and tabs to a minimum of pixels per accessibility standards. Improved interaction accuracy and reduced errors for users with motor control limitations;
- The "Database Native" authentication type has been renamed to "Username/password" for all connectors;
- ClickHouse: Fixed an issue with displaying arrays of UUID, IPv4/IPv6, and Map types.
### 25.3.3 2026-01-19
- For security reasons, session expiration leads to a complete logout for a user and clears all data from the screen;
- Added "Use local formatting" setting to Data Editor. Users can choose how to display numbers and dates: using the OS locale, a custom locale, or keeping values unformatted. This formatting applies only to displayed values. Data in the database remains unchanged;
- Added the shortcut Ctrl/Cmd + . to cancel operations in Data Editor;
- CloudBeaver now supports long polling as a fallback when WebSockets are unavailable or blocked, ensuring reliable communication for metadata updates and SQL execution;
- Fixed a keyboard navigation issue for panels to keep the focus inside;
- Clickhouse: Spatial data support was added.
### 25.3.2 2026-01-05
- The ability to pin columns has been added to the Data Editor, allowing users to keep key columns (e.g., IDs, names) visible while scrolling through wide tables;
- Added a new setting in the SQL Editor to highlight spaces, tabs, and other whitespace characters to help users read, debug, and maintain their scripts. It is turned off by default and can be configured in personal preferences;
- Redesigned the connection configuration page. Form fields and sections have been reorganized to provide more space for input and reduce visual clutter;
- Clickhouse: driver has been updated to version 0.9.5;
- Oracle: Added a new "Set Username to OS_USER" option in the Misc section of Oracle connection settings. When turned on, the tool automatically uses the current database username as the operating system user identifier in the session metadata.
### 25.3.1 2025-12-22
- Implemented an ability to add parameters and variables to queries in the SQL Editor. This feature allows for the reuse of queries by changing parameters at the execution stage. It is enabled by default and can be configured in personal preferences;
- Added support for mapping users to CloudBeaver teams based on LDAP memberOf group membership;
- Added support for previous browser versions, but not older than three years.
### 25.3.0 2025-12-01
### Changes since 25.2.0:
- Administration:
- Permission configuration for connections has been simplified, providing a clearer appearance and improved performance;
- LDAP authentication now supports case-insensitive Distinguished Names (DNs), allowing login with mixed-case DNS.
- Security:
- Fixed a security issue in the Spatial Viewer for the HTML content to prevent a third-party script execution;
- Fixed the critical vulnerability (CVE-2025-61927) in the Happy DOM library. The library was updated to version 20.0.2.
- SQL Editor:
- A confirmation prompt has been added to the SQL Editor to prevent accidental execution of potentially dangerous queries. You can customize this behavior in Preferences > SQL Editor;
- A search field has been added to the catalog/schema selector on the main toolbar for easy navigation;
- Alphabetical order has been added to the autocomplete values in the SQL Editor to simplify object search;
- Added support for temporary tables in semantic analysis.
- Data Editor:
- The ability to review the database query after applying filters and sorting in the Data Editor, Grouping panel, and Result sets has been added. The new Show SQL button is placed on the Filter panel;
- The value panel in the Data Editor has been redesigned to give users more space for viewing and editing data. Tab buttons for different data type visualizations are now aligned on a single level, creating a cleaner layout;
- Added visualization for column reordering in the Data Editor;
- Fixed an issue in the Data Editor where resizing one column in a wide table caused previously resized columns to revert to their original width. Users can resize multiple columns as intended.
- General:
- A reorder tabs option has been added to the application, allowing users to set editor tabs in custom positions;
- Keyboard navigation has been improved across the application. Additional shortcuts are available in the main info panel (top-right corner) to help discover them faster. Relevant keyboard shortcuts are also shown in the tooltips of Data Editor buttons;
- Boolean values have been replaced with intuitive checkboxes instead of the true/false text in the metadata editor, making editing easier and faster;
- The database navigator now automatically hides empty subfolders in shared projects, reducing visual clutter;
- Databases and drivers:
- ClickHouse: query canceling support has been added;
- DuckDB: fixed an issue where catalog selection was lost upon disconnection;
- PostgreSQL: fixed support for GIS data types;
- SQL Server: the driver has been updated to version 13.2.1;
- The "Show all databases" setting has been added on the connection page for MySQL and MariaDB.
## Contribution
As a community-driven open-source project, we warmly welcome contributions through GitHub pull requests.
[We are happy to reward](https://dbeaver.com/help-dbeaver/) our most active contributors every major sprint.
- Marked icons for non-saved (temporary) scripts with the hourglass symbol to add visual distinction from the saved ones.
- Added the ability to set to NULL any value in the Driver Properties.
- Fixed custom driver properties display for the connection page.
- Fixed data export in SQL format for values containing single quotes.
- Databases and drivers:
- ClickHouse:
- Fixed the display of Array(JSON) types in the data grid.
- Updated driver version to 0.9.7.
- Fixed IP address display in the Data Editor by removing the leading slash.
- Added support for the "prompt=false" connection property for DB2 for IBM i to turn off interactive prompts and prevent related errors.
- DuckDB:
- Fixed map rendering for GEOMETRY types with CRS parameters.
- Fixed geometry type display for database versions > 1.5.
- H2 database:
- Added new embedded driver version 2.4.2.
- Updated server driver to version 2.4.2.
- Firebird: Expanded database-specific SQL dialect coverage for the SQL Editor. Added more keywords, built-in functions, and SQL generators.
- MySQL: Fixed query boundaries recognition in the SQL Editor for scripts with the DELIMITER keyword.
- PostgreSQL:
- Updated driver to version 42.7.11
- Fixed missing INOUT parameters in CHECK generation for procedures and functions.
- Fixed handling of 24:xx values in time columns.
- Fixed an error when connecting via URL if the database name contains a hyphen.
- SQL Server:
- Fixed DDL generation for external tables.
- Fixed the database connectivity issue after the Java update.
- Security:
- Added an administrative setting to restrict SSH tunneling capabilities. Administrators can now limit tunnel configuration to authorized users, reducing the risk of unauthorized network access.
- Fixed the critical vulnerability (CVE-2025-62718) in the axios library. The library was updated to version 1.15.0.
- Fixed the high vulnerability (CVE-2026-4800) in the lodash library. The library was updated to version 4.18.0.
- Fixed the high vulnerability (CVE-2026-39363) in the vite library. The library was updated to version 7.3.2.
- Fixed the high vulnerability (CVE-2026-42035) in the axios library. The library was updated to version 1.15.1.
- Fixed the high vulnerability (CVE-2026-3505) in the bcpg-jdk18on library. The library was updated to version 1.84.0.
- Fixed the high vulnerability (CVE-2026-33671) in the picomatch library. The library was updated to version 2.3.2.
- Fixed the high vulnerability (CVE-2026-33943) in the happy-dom library. The library was updated to version 20.8.8.
- Fixed the high vulnerability (CVE-2025-45141) in the tar library. The library was updated to version 7.5.11.
- Fixed the high vulnerability (CVE-2026-27148) in the storybook library. The library was updated to version 10.2.10.
- Fixed the high vulnerability (CVE-2026-27606) in the rollup library. The library was updated to version 4.59.0.
- Fixed the high vulnerability (CVE-2026-27904) in the minimatch library. The library was updated to version 3.1.3.
- Fixed the high vulnerability (CVE-2026-27959) in the koa library. The library was updated to version 2.16.4.
- Fixed the high vulnerability (CVE-2026-32141) in the flatted library. The library was updated to version 4.4.0.
- Fixed the medium vulnerability (CVE-2026-41305) in the postcss library. The library was updated to version 8.5.14.
+1 -13
View File
@@ -1,18 +1,6 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | --------- |
| 22.x | yes |
| 23.x | yes |
| 24.x | yes |
## Reporting a Vulnerability
Please report (suspected) security vulnerabilities to devops@dbeaver.com.
You will receive a response from us within 48 hours.
Please report (suspected) security vulnerabilities to <security@dbeaver.com>.
If the issue is confirmed, we will release a patch as soon as possible, depending on complexity, but historically, within a few days.
+61
View File
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.cloudbeaver</groupId>
<artifactId>config-generator</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.13.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>io.cloudbeaver.ConfigGenerator</mainClass>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<executions>
<execution>
<id>copy-external-config</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.outputDirectory}/config</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/../../config/template</directory>
<includes>
<include>cloudbeaver-base.conf</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,235 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.io.Reader;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
public class ConfigGenerator {
private static final Gson gson = new GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.create();
private static final String EMPTY_STRING = "''";
public static void main(String[] args) throws Exception {
String baseConfigPath = System.getProperty("config.base");
if (baseConfigPath == null || baseConfigPath.isEmpty()) {
URL resource = ConfigGenerator.class.getClassLoader().getResource("config/cloudbeaver-base.conf");
if (resource == null) {
System.out.println("Base config path is required");
return;
}
baseConfigPath = resource.toURI().getPath();
if (System.getProperty("os.name").toLowerCase().contains("win")) {
// Fix for Windows in dev mode. Resource URI starts with / (e.g. /C:/some/folder)
// And this breaks Java NIO
if (baseConfigPath.startsWith("/")) {
baseConfigPath = baseConfigPath.substring(1);
}
}
}
String outputPath = System.getProperty("config.output");
if (outputPath == null || outputPath.isEmpty()) {
System.out.println("Output config path is required");
return;
}
String configPatches = System.getProperty("config.patches");
List<String> overrideConfigs = configPatches == null ? List.of() : Arrays.asList(configPatches.split(","));
Map<String, Object> config = readConfigurationFile(baseConfigPath);
System.out.println("Base config loaded");
for (String overrideConfig : overrideConfigs) {
if (Files.exists(Path.of(overrideConfig))) {
System.out.println("Applying override config: " + overrideConfig);
} else {
System.out.println("Override config not found, skipping: " + overrideConfig);
continue;
}
System.out.println("Processing override config: " + overrideConfig);
Map<String, Object> overrideConfigMap = readConfigurationFile(overrideConfig);
System.out.println("Override config loaded: " + overrideConfig);
applyProductConfig(config, overrideConfigMap);
System.out.println("Override config applied: " + overrideConfig);
}
Map<String, Object> finalMap = convertToOutputFormat(config);
System.out.println("Config converted to output format");
writeConfigurationFile(finalMap, outputPath);
System.out.println("Config written to: " + outputPath);
}
private static Map<String, Object> readConfigurationFile(String filePath) throws Exception {
Path path = Path.of(filePath);
if (!Files.exists(path)) {
throw new IllegalArgumentException("Configuration file not found: " + filePath);
}
try (Reader reader = Files.newBufferedReader(path)) {
return gson.fromJson(reader, TypeToken.getParameterized(Map.class, String.class, Object.class).getType());
}
}
private static void applyProductConfig(Map<String, Object> baseConfig, Map<String, Object> productConfig) {
if (productConfig.containsKey("add")) {
Map<String, Object> add = (Map<String, Object>) productConfig.get("add");
deepMergeMap(baseConfig, add);
}
if (productConfig.containsKey("remove")) {
Map<String, Object> remove = (Map<String, Object>) productConfig.get("remove");
deepRemoveMap(baseConfig, remove);
}
}
private static void deepMergeMap(Map<String, Object> target, Map<String, Object> source) {
for (Map.Entry<String, Object> entry : source.entrySet()) {
Object sourceValue = entry.getValue();
Object targetValue = target.get(entry.getKey());
if (sourceValue instanceof Map && targetValue instanceof Map) {
deepMergeMap((Map<String, Object>) targetValue, (Map<String, Object>) sourceValue);
} else {
target.put(entry.getKey(), sourceValue);
}
}
}
private static void deepRemoveMap(Map<String, Object> target, Map<String, Object> source) {
for (Map.Entry<String, Object> entry : source.entrySet()) {
Object sourceValue = entry.getValue();
Object targetValue = target.get(entry.getKey());
if (sourceValue instanceof Map && targetValue instanceof Map) {
deepRemoveMap((Map<String, Object>) targetValue, (Map<String, Object>) sourceValue);
} else {
target.remove(entry.getKey());
}
}
}
private static void writeConfigurationFile(Map<String, Object> map, String filePath) throws Exception {
JsonElement tree = gson.toJsonTree(map);
StringBuilder sb = new StringBuilder();
writeElement(tree, sb, 0);
Files.write(Path.of(filePath), sb.toString().getBytes());
}
private static void writeElement(JsonElement elem, StringBuilder sb, int count) {
String indent = " ";
if (elem.isJsonObject()) {
sb.append("{\n");
JsonObject obj = elem.getAsJsonObject();
var iterator = obj.entrySet().iterator();
while (iterator.hasNext()) {
var entry = iterator.next();
sb.repeat(indent, count + 1).append(entry.getKey()).append(": ");
writeElement(entry.getValue(), sb, count + 1);
if (iterator.hasNext()) {
sb.append(",");
}
sb.append("\n");
}
sb.repeat(indent, count).append("}");
} else if (elem.isJsonArray()) {
sb.append("[\n");
JsonArray array = elem.getAsJsonArray();
for (int i = 0; i < array.size(); i++) {
sb.repeat(indent, count + 1);
writeElement(array.get(i), sb, count + 1);
if (i < array.size() - 1) {
sb.append(",");
}
sb.append("\n");
}
sb.repeat(indent, count).append("]");
} else {
String valueStr = gson.toJson(elem).replace("\n", "\n" + indent);
sb.append(valueStr);
}
}
private static Map<String, Object> convertToOutputFormat(Map<String, Object> map) {
Map<String, Object> result = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
result.put(entry.getKey(), convertValue(entry.getValue()));
}
return result;
}
private static Object convertValue(Object value) {
if (value instanceof Map) {
Map<String, Object> map = (Map<String, Object>) value;
if (!map.isEmpty() && map.containsKey("value")) {
if (map.containsKey("env")) {
String env = (String) map.get("env");
Object defValue = map.get("value");
return "${" + env + ":" + formatDefaultValue(defValue) + "}";
} else {
return convertValue(map.get("value"));
}
}
Map<String, Object> result = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
result.put(entry.getKey(), convertValue(entry.getValue()));
}
return result;
} else if (value instanceof List) {
List<Object> list = (List<Object>) value;
List<Object> result = new ArrayList<>();
for (Object item : list) {
result.add(convertValue(item));
}
return result;
}
return value;
}
private static String formatDefaultValue(Object value) {
return switch (value) {
case null -> EMPTY_STRING;
case String str -> str.isEmpty() ? EMPTY_STRING : str;
case Boolean b -> value.toString();
case Number number -> {
if (number instanceof Double || number instanceof Float) {
long longVal = number.longValue();
if (longVal == number.doubleValue()) {
yield String.valueOf(longVal);
}
}
yield number.toString();
}
// TODO: handle list elements if needed
default -> value.toString();
};
}
}
-99
View File
@@ -1,99 +0,0 @@
{
server: {
serverPort: "${CLOUDBEAVER_WEB_SERVER_PORT:8978}",
forceHttps: "${CLOUDBEAVER_FORCE_HTTPS:false}",
contentRoot: "web",
driversLocation: "drivers",
sslConfigurationPath:"${CLOUDBEAVER_SSL_CONF_PATH:workspace/.data/ssl-config.xml}",
rootURI: "${CLOUDBEAVER_ROOT_URI:/}",
serviceURI: "/api/",
supportedHosts: [],
productSettings: {
# Global properties
core.theming.theme: "${CLOUDBEAVER_CORE_THEMING_THEME:system}",
core.localization.language: "${CLOUDBEAVER_CORE_LOCALIZATION:en}",
plugin.sql-editor.autoSave: "${CLOUDBEAVER_SQL_EDITOR_AUTOSAVE:true}",
plugin.sql-editor.disabled: "${CLOUDBEAVER_SQL_EDITOR_DISABLED:false}",
# max size of the file that can be uploaded to the editor (in kilobytes)
plugin.sql-editor.maxFileSize: "${CLOUDBEAVER_SQL_EDITOR_MAX_FILE_SIZE:10240}",
plugin.log-viewer.disabled: "${CLOUDBEAVER_LOG_VIEWER_DISABLED:false}",
plugin.log-viewer.logBatchSize: "${CLOUDBEAVER_LOG_VIEWER_LOG_BATCH_SIZE:1000}",
plugin.log-viewer.maxLogRecords: "${CLOUDBEAVER_LOG_VIEWER_MAX_LOG_RECORDS:2000}",
sql.proposals.insert.table.alias: "${CLOUDBEAVER_SQL_PROPOSALS_INSERT_TABLE_ALIAS:PLAIN}",
SQLEditor.ContentAssistant.experimental.mode: "${CLOUDBEAVER_SQL_EDITOR_CONTENT_ASSISTANT_EXPERIMENTAL_MODE:NEW}"
},
expireSessionAfterPeriod: "${CLOUDBEAVER_EXPIRE_SESSION_AFTER_PERIOD:1800000}",
bindSessionToIp: "${CLOUDBEAVER_BIND_SESSION_TO_IP:disable}",
develMode: "${CLOUDBEAVER_DEVEL_MODE:false}",
enableSecurityManager: false,
sm: {
enableBruteForceProtection: "${CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED:true}",
maxFailedLogin: "${CLOUDBEAVER_MAX_FAILED_LOGINS:10}",
minimumLoginTimeout: "${CLOUDBEAVER_MINIMUM_LOGIN_TIMEOUT:1}",
blockLoginPeriod: "${CLOUDBEAVER_BLOCK_PERIOD:300}",
passwordPolicy: {
minLength: "${CLOUDBEAVER_POLICY_MIN_LENGTH:8}",
requireMixedCase: "${CLOUDBEAVER_POLICY_REQUIRE_MIXED_CASE:true}",
minNumberCount: "${CLOUDBEAVER_POLICY_MIN_NUMBER_COUNT:1}",
minSymbolCount: "${CLOUDBEAVER_POLICY_MIN_SYMBOL_COUNT:0}"
}
},
database: {
driver: "${CLOUDBEAVER_DB_DRIVER:h2_embedded_v2}",
url: "${CLOUDBEAVER_DB_URL:jdbc:h2:${workspace}/.data/cb.h2v2.dat}",
schema: "${CLOUDBEAVER_DB_SCHEMA:''}",
user: "${CLOUDBEAVER_DB_USER:''}",
password: "${CLOUDBEAVER_DB_PASSWORD:''}",
initialDataConfiguration: "${CLOUDBEAVER_DB_INITIAL_DATA:conf/initial-data.conf}",
pool: {
minIdleConnections: "${CLOUDBEAVER_DB_MIN_IDLE_CONNECTIONS:4}",
maxIdleConnections: "${CLOUDBEAVER_DB_MAX_IDLE_CONNECTIONS:10}",
maxConnections: "${CLOUDBEAVER_DB_MAX_CONNECTIONS:100}",
validationQuery: "${CLOUDBEAVER_DB_VALIDATION_QUERY:SELECT 1}"
},
backupEnabled: "${CLOUDBEAVER_DB_BACKUP_ENABLED:true}"
}
},
app: {
anonymousAccessEnabled: "${CLOUDBEAVER_APP_ANONYMOUS_ACCESS_ENABLED:true}",
anonymousUserRole: user,
defaultUserTeam: "${CLOUDBEAVER_APP_DEFAULT_USER_TEAM:user}",
grantConnectionsAccessToAnonymousTeam: "${CLOUDBEAVER_APP_GRANT_CONNECTIONS_ACCESS_TO_ANONYMOUS_TEAM:false}",
supportsCustomConnections: "${CLOUDBEAVER_APP_SUPPORTS_CUSTOM_CONNECTIONS:false}",
showReadOnlyConnectionInfo: "${CLOUDBEAVER_APP_READ_ONLY_CONNECTION_INFO:false}",
systemVariablesResolvingEnabled: "${CLOUDBEAVER_SYSTEM_VARIABLES_RESOLVING_ENABLED:false}",
forwardProxy: "${CLOUDBEAVER_APP_FORWARD_PROXY:false}",
publicCredentialsSaveEnabled: "${CLOUDBEAVER_APP_PUBLIC_CREDENTIALS_SAVE_ENABLED:true}",
adminCredentialsSaveEnabled: "${CLOUDBEAVER_APP_ADMIN_CREDENTIALS_SAVE_ENABLED:true}",
resourceManagerEnabled: "${CLOUDBEAVER_APP_RESOURCE_MANAGER_ENABLED:true}",
resourceQuotas: {
resourceManagerFileSizeLimit: "${CLOUDBEAVER_RESOURCE_QUOTA_RESOURCE_MANAGER_FILE_SIZE_LIMIT:500000}",
sqlMaxRunningQueries: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_MAX_RUNNING_QUERIES:100}",
sqlResultSetRowsLimit: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_RESULT_SET_ROWS_LIMIT:100000}",
sqlTextPreviewMaxLength: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_TEXT_PREVIEW_MAX_LENGTH:4096}",
sqlBinaryPreviewMaxLength: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_BINARY_PREVIEW_MAX_LENGTH:261120}"
},
enabledAuthProviders: [
"local"
],
disabledBetaFeatures: [
]
}
}
+271
View File
@@ -0,0 +1,271 @@
{
server: {
serverPort: {
env: "CLOUDBEAVER_WEB_SERVER_PORT",
value: 8978
},
forceHttps: {
env: "CLOUDBEAVER_FORCE_HTTPS",
value: false
},
contentRoot: {
env: "CLOUDBEAVER_CONTENT_ROOT",
value: "web"
},
driversLocation: {
env: "CLOUDBEAVER_DRIVERS_LOCATION",
value: "drivers"
},
sslConfigurationPath: {
env: "CLOUDBEAVER_SSL_CONF_PATH",
value: "workspace/.data/ssl-config.xml"
},
rootURI: {
env: "CLOUDBEAVER_ROOT_URI",
value: "/"
},
serviceURI: {
env: "CLOUDBEAVER_SERVICE_URI",
value: "/api/"
},
supportedHosts: [],
productSettings: {
core.theming.theme: {
env: "CLOUDBEAVER_CORE_THEMING_THEME",
value: "system"
},
core.localization.language: {
env: "CLOUDBEAVER_CORE_LOCALIZATION",
value: "en"
},
plugin.sql-editor.autoSave: {
env: "CLOUDBEAVER_SQL_EDITOR_AUTOSAVE",
value: true
},
plugin.sql-editor.disabled: {
env: "CLOUDBEAVER_SQL_EDITOR_DISABLED",
value: false
},
plugin.sql-editor.maxFileSize: {
env: "CLOUDBEAVER_SQL_EDITOR_MAX_FILE_SIZE",
value: 10240
},
plugin.log-viewer.disabled: {
env: "CLOUDBEAVER_LOG_VIEWER_DISABLED",
value: false
},
plugin.log-viewer.logBatchSize: {
env: "CLOUDBEAVER_LOG_VIEWER_LOG_BATCH_SIZE",
value: 1000
},
plugin.log-viewer.maxLogRecords: {
env: "CLOUDBEAVER_LOG_VIEWER_MAX_LOG_RECORDS",
value: 2000
},
sql.proposals.insert.table.alias: {
env: "CLOUDBEAVER_SQL_PROPOSALS_INSERT_TABLE_ALIAS",
value: "PLAIN"
},
SQLEditor.ContentAssistant.experimental.mode: {
env: "CLOUDBEAVER_SQL_EDITOR_CONTENT_ASSISTANT_EXPERIMENTAL_MODE",
value: "NEW"
}
},
expireSessionAfterPeriod: {
env: "CLOUDBEAVER_EXPIRE_SESSION_AFTER_PERIOD",
value: 1800000
},
bindSessionToIp: {
env: "CLOUDBEAVER_BIND_SESSION_TO_IP",
value: "disable"
},
develMode: {
env: "CLOUDBEAVER_DEVEL_MODE",
value: false
},
sm: {
enableBruteForceProtection: {
env: "CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED",
value: true
},
maxFailedLogin: {
env: "CLOUDBEAVER_MAX_FAILED_LOGINS",
value: 10
},
minimumLoginTimeout: {
env: "CLOUDBEAVER_MINIMUM_LOGIN_TIMEOUT",
value: 1
},
blockLoginPeriod: {
env: "CLOUDBEAVER_BLOCK_PERIOD",
value: 300
},
passwordPolicy: {
minLength: {
env: "CLOUDBEAVER_POLICY_MIN_LENGTH",
value: 8
},
requireMixedCase: {
env: "CLOUDBEAVER_POLICY_REQUIRE_MIXED_CASE",
value: true
},
minNumberCount: {
env: "CLOUDBEAVER_POLICY_MIN_NUMBER_COUNT",
value: 1
},
minSymbolCount: {
env: "CLOUDBEAVER_POLICY_MIN_SYMBOL_COUNT",
value: 0
}
}
},
database: {
driver: {
env: "CLOUDBEAVER_DB_DRIVER",
value: "h2_embedded_v2"
},
url: {
env: "CLOUDBEAVER_DB_URL",
value: "jdbc:h2:${workspace}/.data/cb.h2v2.dat"
},
schema: {
env: "CLOUDBEAVER_DB_SCHEMA",
value: ""
},
user: {
env: "CLOUDBEAVER_DB_USER",
value: ""
},
password: {
env: "CLOUDBEAVER_DB_PASSWORD",
value: ""
},
initialDataConfiguration: {
env: "CLOUDBEAVER_DB_INITIAL_DATA",
value: "conf/initial-data.conf"
},
pool: {
minIdleConnections: {
env: "CLOUDBEAVER_DB_MIN_IDLE_CONNECTIONS",
value: 4
},
maxIdleConnections: {
env: "CLOUDBEAVER_DB_MAX_IDLE_CONNECTIONS",
value: 10
},
maxConnections: {
env: "CLOUDBEAVER_DB_MAX_CONNECTIONS",
value: 100
},
validationQuery: {
env: "CLOUDBEAVER_DB_VALIDATION_QUERY",
value: "SELECT 1"
}
},
backupEnabled: {
env: "CLOUDBEAVER_DB_BACKUP_ENABLED",
value: true
}
}
},
app: {
anonymousAccessEnabled: {
env: "CLOUDBEAVER_APP_ANONYMOUS_ACCESS_ENABLED",
value: true
},
anonymousUserRole: {
env: "CLOUDBEAVER_APP_ANONYMOUS_USER_ROLE",
value: "user"
},
defaultUserTeam: {
env: "CLOUDBEAVER_APP_DEFAULT_USER_TEAM",
value: "user"
},
grantConnectionsAccessToAnonymousTeam: {
env: "CLOUDBEAVER_APP_GRANT_CONNECTIONS_ACCESS_TO_ANONYMOUS_TEAM",
value: false
},
supportsCustomConnections: {
env: "CLOUDBEAVER_APP_SUPPORTS_CUSTOM_CONNECTIONS",
value: false
},
showReadOnlyConnectionInfo: {
env: "CLOUDBEAVER_APP_READ_ONLY_CONNECTION_INFO",
value: false
},
systemVariablesResolvingEnabled: {
env: "CLOUDBEAVER_SYSTEM_VARIABLES_RESOLVING_ENABLED",
value: false
},
forwardProxy: {
env: "CLOUDBEAVER_APP_FORWARD_PROXY",
value: false
},
publicCredentialsSaveEnabled: {
env: "CLOUDBEAVER_APP_PUBLIC_CREDENTIALS_SAVE_ENABLED",
value: true
},
adminCredentialsSaveEnabled: {
env: "CLOUDBEAVER_APP_ADMIN_CREDENTIALS_SAVE_ENABLED",
value: true
},
resourceManagerEnabled: {
env: "CLOUDBEAVER_APP_RESOURCE_MANAGER_ENABLED",
value: true
},
resourceQuotas: {
resourceManagerFileSizeLimit: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_RESOURCE_MANAGER_FILE_SIZE_LIMIT",
value: 500000
},
sqlMaxRunningQueries: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_MAX_RUNNING_QUERIES",
value: 100
},
sqlResultSetRowsLimit: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_RESULT_SET_ROWS_LIMIT",
value: 100000
},
sqlTextPreviewMaxLength: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_TEXT_PREVIEW_MAX_LENGTH",
value: 4096
},
sqlBinaryPreviewMaxLength: {
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_BINARY_PREVIEW_MAX_LENGTH",
value: 261120
}
},
enabledAuthProviders: ["local"],
disabledBetaFeatures: []
}
}
+3 -1
View File
@@ -18,7 +18,6 @@ cd ../..
echo "Pull dbeaver platform"
[ ! -d dbeaver ] && git clone --depth 1 https://github.com/dbeaver/dbeaver.git
[ ! -d dbeaver-common ] && git clone --depth 1 https://github.com/dbeaver/dbeaver-common.git
[ ! -d dbeaver-jdbc-libsql ] && git clone --depth 1 https://github.com/dbeaver/dbeaver-jdbc-libsql.git
cd cloudbeaver/deploy
@@ -32,6 +31,9 @@ if [[ "$?" -ne 0 ]] ; then
fi
cd ../../../deploy
echo "Generate cloudbeaver.conf file"
mvn -f ../apps/config-generator compile exec:java -Dconfig.output="cloudbeaver/conf/cloudbeaver.conf"
echo "Copy server packages"
cp -rp ../server/product/web-server/target/products/io.cloudbeaver.product/all/all/all/* ./cloudbeaver/server
+3 -1
View File
@@ -20,7 +20,6 @@ echo Pull dbeaver platform
IF NOT EXIST dbeaver git clone https://github.com/dbeaver/dbeaver.git
IF NOT EXIST dbeaver-common git clone https://github.com/dbeaver/dbeaver-common.git
IF NOT EXIST dbeaver-jdbc-libsql git clone https://github.com/dbeaver/dbeaver-jdbc-libsql.git
cd cloudbeaver\deploy
@@ -43,6 +42,9 @@ copy ..\config\DefaultConfiguration\GlobalConfiguration\.dbeaver\data-sources.js
move drivers cloudbeaver >NUL
echo Generate cloudbeaver.conf file
call mvn -f ..\apps\config-generator compile exec:java -Dconfig.output="cloudbeaver\conf\cloudbeaver.conf" || goto :error
echo "Build static content"
mkdir .\cloudbeaver\web
+3 -2
View File
@@ -1,4 +1,5 @@
FROM ubuntu:24.04
# Ubuntu 24.04 (Noble Numbat)
FROM ubuntu:noble
MAINTAINER DBeaver Corp, devops@dbeaver.com
@@ -31,7 +32,7 @@ RUN set -eux; \
ENV LANG=en_US.UTF-8
ENV JAVA_HOME=/opt/java/openjdk
COPY --from=eclipse-temurin:21 $JAVA_HOME $JAVA_HOME
COPY --from=eclipse-temurin:25-jdk-noble $JAVA_HOME $JAVA_HOME
ENV PATH="${JAVA_HOME}/bin:${PATH}"
RUN set -eux; \
+3 -3
View File
@@ -13,7 +13,7 @@ if [ "$(id -u)" -eq 0 ]; then
chown -R $DBEAVER_UID:$DBEAVER_GID $PWD/workspace
# Execute run-cloudbeaver-server.sh as the dbeaver user with the JAVA_HOME and PATH environment variables
exec su "$TARGET_USER" -c "JAVA_HOME=$JAVA_HOME PATH=$PATH ./run-cloudbeaver-server.sh"
exec su "$TARGET_USER" -c "JAVA_HOME=$JAVA_HOME PATH=$PATH ./run-cloudbeaver-server.sh $*"
else
exec ./run-cloudbeaver-server.sh
fi
exec ./run-cloudbeaver-server.sh "$@"
fi
+1 -1
View File
@@ -35,7 +35,7 @@ exec java ${JAVA_OPTS} \
--add-opens=java.sql/java.sql=ALL-UNNAMED \
-jar ${launcherJar} \
-product io.cloudbeaver.product.ce.product \
-web-config conf/cloudbeaver.conf \
"$@" \
-web-config conf/cloudbeaver.conf \
-nl en \
-registryMultiLanguage
-1
View File
@@ -1,3 +1,2 @@
dbeaver-common
dbeaver-jdbc-libsql
dbeaver
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Model
Bundle-SymbolicName: io.cloudbeaver.model.cli;singleton:=true
Bundle-Version: 1.0.5.qualifier
Bundle-Release-Date: 20260302
Bundle-Version: 1.0.13.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.model.cli;visibility:=reexport
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.model.cli</artifactId>
<version>1.0.5-SNAPSHOT</version>
<version>1.0.13-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -18,27 +18,27 @@ package io.cloudbeaver.model.cli;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.cli.*;
import org.jkiss.dbeaver.model.cli.model.CommandLineAuthenticator;
import org.jkiss.dbeaver.model.cli.ApplicationCommandLine;
import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
import org.jkiss.dbeaver.model.cli.CLIContextImpl;
import org.jkiss.dbeaver.model.cli.CLIRunMeta;
import org.jkiss.dbeaver.model.cli.registry.CLICommandDescriptor;
import picocli.CommandLine;
import java.util.List;
public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationInstanceController> {
@Nullable
private final CommandLineAuthenticator authenticator;
private final CloudBeaverMixin mixin;
public CloudBeaverCommandLine(@Nullable CommandLineAuthenticator authenticator) {
public CloudBeaverCommandLine() {
super();
this.authenticator = authenticator;
this.mixin = new CloudBeaverMixin();
}
public CloudBeaverCommandLine(
@Nullable CommandLineAuthenticator authenticator,
@NotNull CloudBeaverMixin mixin
) {
super();
this.authenticator = authenticator;
this.mixin = mixin;
}
@@ -48,9 +48,6 @@ public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationIn
@NotNull CLIContextImpl context,
@NotNull CLIRunMeta runMeta
) {
if (authenticator != null) {
context.setContextParameter(CLIConstants.CONTEXT_PARAM_AUTHENTICATOR, authenticator);
}
return new CloudBeaverTopLevelCommand(applicationInstanceController, context, runMeta);
}
@@ -59,9 +56,10 @@ public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationIn
protected CommandLine initCommandLine(
@Nullable ApplicationInstanceController applicationInstanceController,
@NotNull CLIContextImpl context,
@NotNull CLIRunMeta runMeta
@NotNull CLIRunMeta runMeta,
@NotNull List<CLICommandDescriptor> commandsToExecute
) {
var cmd = super.initCommandLine(applicationInstanceController, context, runMeta);
var cmd = super.initCommandLine(applicationInstanceController, context, runMeta, commandsToExecute);
cmd.addMixin("cloudbeaver", mixin);
return cmd;
}
@@ -29,7 +29,7 @@ public class CloudBeaverInstanceServer extends ApplicationInstanceServer<Applica
public CloudBeaverInstanceServer() throws IOException {
super(ApplicationInstanceController.class);
this.commandLine = new CloudBeaverCommandLine(null);
this.commandLine = new CloudBeaverCommandLine();
}
public CloudBeaverInstanceServer(@NotNull CloudBeaverCommandLine commandLine) throws IOException {
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,12 +16,14 @@
*/
package io.cloudbeaver.model.cli;
import org.jkiss.dbeaver.model.cli.model.NonExecutableOption;
import picocli.CommandLine;
/**
* CloudBeaver specific mixin options
*/
public class CloudBeaverMixin {
@NonExecutableOption
@CommandLine.Option(
names = {"-web-config"},
arity = "1",
@@ -22,10 +22,12 @@ import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
import org.jkiss.dbeaver.model.cli.CLIContextImpl;
import org.jkiss.dbeaver.model.cli.CLIRunMeta;
import org.jkiss.dbeaver.model.cli.command.AbstractTopLevelCommand;
import org.jkiss.dbeaver.model.cli.model.NonExecutableOption;
import picocli.CommandLine;
@CommandLine.Command(name = "cbvr", description = "CloudBeaver commands")
public class CloudBeaverTopLevelCommand extends AbstractTopLevelCommand {
@NonExecutableOption
@CommandLine.Option(
names = {NOSPASH_OPTION},
hidden = true,
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Model
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
Bundle-Version: 1.0.96.qualifier
Bundle-Release-Date: 20260302
Bundle-Version: 1.0.104.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
@@ -17,7 +17,6 @@ Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
org.jkiss.bundle.logback;visibility:=reexport,
org.jkiss.bundle.graphql;visibility:=reexport,
org.jkiss.bundle.apache.dbcp,
com.google.gson;visibility:=reexport,
jakarta.servlet-api;bundle-version:="6.0.0";visibility:=reexport
Export-Package: io.cloudbeaver,
io.cloudbeaver.auth,
@@ -29,6 +28,11 @@ Export-Package: io.cloudbeaver,
io.cloudbeaver.websocket.event.client,
io.cloudbeaver.websocket.event.task,
io.cloudbeaver.model,
io.cloudbeaver.model.api.annotation,
io.cloudbeaver.model.api.annotation.enums,
io.cloudbeaver.model.api.annotation.media,
io.cloudbeaver.model.api.annotation.responses,
io.cloudbeaver.model.api.annotation.tags,
io.cloudbeaver.model.app,
io.cloudbeaver.model.config,
io.cloudbeaver.model.apilog,
@@ -25,6 +25,11 @@
label="Default secret controller" description="Default void secret controller"/>
</extension>
<extension point="io.cloudbeaver.feature">
<feature id="ssh" label="SSH tunnels for databases" description="Enables SSH tunnels for secure database connections, allowing access to remote databases through encrypted channels" enabledByDefault="true"/>
</extension>
<extension point="org.jkiss.dbeaver.ws.event">
<event topicId="cb_session_task" id="cb_session_task_info_updated"
class="io.cloudbeaver.websocket.event.task.WSSessionTaskInfoEvent"/>
@@ -46,6 +51,8 @@
class="io.cloudbeaver.websocket.event.client.WSSessionTaskConfirmationEvent"/>
<clientEvent id="cb_client_session_task_with_parameters_confirmation"
class="io.cloudbeaver.websocket.event.client.WSSessionTaskWithParametersConfirmationEvent"/>
<clientEvent id="cb_client_cancel_action"
class="io.cloudbeaver.websocket.event.client.WSSessionCancelActionClientEvent"/>
</extension>
</plugin>
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.model</artifactId>
<version>1.0.96-SNAPSHOT</version>
<version>1.0.104-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.
@@ -27,6 +27,7 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
import org.jkiss.dbeaver.registry.DataSourcePreferenceStore;
import org.jkiss.utils.CommonUtils;
public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C extends DataSourceDescriptor> {
@@ -93,6 +94,9 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
input.isSharedCredentials()
);
dataSource.setConnectionReadOnly(input.isReadOnly());
DataSourcePreferenceStore preferenceStore = dataSource.getPreferenceStore();
preferenceStore.clear();
preferenceStore.setProperties(input.getDefaultUserPreferences());
}
@NotNull
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package io.cloudbeaver;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.utils.WebEventUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBPEvent;
@@ -46,7 +47,7 @@ public class WebDataSourceConnectEventListener implements DBPEventListener {
new WSDataSourceConnectEvent(
container.getProject().getId(),
container.getId(),
webSession.getSessionId(),
WebEventUtils.getSmSessionId(webSession),
webSession.getUserId()
)
);
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.net.DBWNetworkProfile;
import org.jkiss.dbeaver.model.net.DBWNetworkProfileManager;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.secret.DBSSecretController;
@@ -258,28 +259,12 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
dataSourceRegistry.removeSavedFilter(filterName);
}
@Nullable
@Override
public DBWNetworkProfile getNetworkProfile(@Nullable String source, @NotNull String name) {
return dataSourceRegistry.getNetworkProfile(source, name);
}
@NotNull
@Override
public List<DBWNetworkProfile> getNetworkProfiles() {
public DBWNetworkProfileManager getNetworkProfiles() {
return dataSourceRegistry.getNetworkProfiles();
}
@Override
public void updateNetworkProfile(@NotNull DBWNetworkProfile profile) {
dataSourceRegistry.updateNetworkProfile(profile);
}
@Override
public void removeNetworkProfile(@NotNull DBWNetworkProfile profile) {
dataSourceRegistry.removeNetworkProfile(profile);
}
@Nullable
@Override
public DBAAuthProfile getAuthProfile(@NotNull String id) {
@@ -377,6 +362,12 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
return dataSourceRegistry.getPreferenceStore();
}
@Override
public void initializeDataSources() {
dataSourceRegistry.initializeDataSources();
}
@Nullable
@Override
public DataSourceParseResults loadDataSources(
@NotNull List<DBPDataSourceConfigurationStorage> storages,
@@ -392,6 +383,7 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
dataSourceRegistry.saveDataSources();
}
@NotNull
@Override
public DataSourceConfigurationManager getConfigurationManager() {
return dataSourceRegistry.getConfigurationManager();
@@ -407,12 +399,12 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
}
@Override
public void persistSecrets(DBSSecretController secretController) throws DBException {
public void persistSecrets(@NotNull DBSSecretController secretController) throws DBException {
dataSourceRegistry.persistSecrets(secretController);
}
@Override
public void resolveSecrets(DBSSecretController secretController) throws DBException {
public void resolveSecrets(@NotNull DBSSecretController secretController) throws DBException {
dataSourceRegistry.resolveSecrets(secretController);
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,8 +23,8 @@ import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBPEvent;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.security.user.SMObjectPermissions;
import java.util.Set;
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,11 +29,11 @@ import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.DBPObjectSettingsProvider;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistryCache;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.navigator.DBNModel;
import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.rm.RMUtils;
import org.jkiss.dbeaver.model.security.SMControllerUtils;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
@@ -252,14 +252,14 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
}
case WSDataSourceEvent.UPDATED -> {
if (event.getProperty() == WSDataSourceProperty.CONFIGURATION) {
WebDataSourceUtils.disconnectDataSource(webSession, ds);
WebDataSourceUtils.disconnectDataSource(webSession, ds, true);
}
if (event.getProperty() != WSDataSourceProperty.INTERNAL) {
sendDataSourceUpdatedEvent = true;
}
}
case WSDataSourceEvent.DELETED -> {
WebDataSourceUtils.disconnectDataSource(webSession, ds);
WebDataSourceUtils.disconnectDataSource(webSession, ds, false);
if (registry instanceof DBPDataSourceRegistryCache dsrc) {
dsrc.removeDataSourceFromList(ds);
}
@@ -345,7 +345,7 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
@NotNull
private WebConnectionInfo closeAndDeleteConnection(@NotNull WebConnectionInfo connectionInfo) throws DBWebException {
DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer();
WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer);
WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer, false);
DBPDataSourceRegistry registry = getDataSourceRegistry();
registry.removeDataSource(dataSourceContainer);
removeConnection(dataSourceContainer);
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.meta.Property;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -64,6 +65,9 @@ public class WebConnectionConfig {
private Boolean defaultAutoCommit;
private String defaultCatalogName;
private String defaultSchemaName;
private String connectionType;
@NotNull
private Map<String, String> defaultUserPreferences = new LinkedHashMap<>();
public WebConnectionConfig() {
}
@@ -111,10 +115,13 @@ public class WebConnectionConfig {
String configType = JSONUtils.getString(params, "configurationType");
configurationType = configType == null ? null : DBPDriverConfigurationType.valueOf(configType);
Map<String, Object> stringObjectUserPrefMap = JSONUtils.getObject(params, "defaultUserPreferences");
stringObjectUserPrefMap.forEach((key, value) -> defaultUserPreferences.put(key, value.toString()));
networkHandlersConfig = new ArrayList<>();
for (Map<String, Object> nhc : JSONUtils.getObjectList(params, "networkHandlersConfig")) {
networkHandlersConfig.add(new WebNetworkHandlerConfigInput(nhc));
}
connectionType = JSONUtils.getString(params, "connectionType");
}
@Property
@@ -367,4 +374,22 @@ public class WebConnectionConfig {
public void setDefaultSchemaName(String defaultSchemaName) {
this.defaultSchemaName = defaultSchemaName;
}
@NotNull
public Map<String, String> getDefaultUserPreferences() {
return defaultUserPreferences;
}
public void setDefaultUserPreferences(@NotNull Map<String, String> defaultUserPreferences) {
this.defaultUserPreferences = defaultUserPreferences;
}
@Nullable
public String getConnectionType() {
return connectionType;
}
public void setConnectionType(@Nullable String connectionType) {
this.connectionType = connectionType;
}
}
@@ -39,12 +39,15 @@ import org.jkiss.dbeaver.model.connection.DBPDriverConfigurationType;
import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNative;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
import org.jkiss.dbeaver.model.navigator.DBNDataSource;
import org.jkiss.dbeaver.model.navigator.DBNDatabaseNode;
import org.jkiss.dbeaver.model.navigator.DBNUtils;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
import org.jkiss.dbeaver.model.preferences.DBPPropertySource;
import org.jkiss.dbeaver.model.rm.RMConstants;
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
import org.jkiss.dbeaver.model.runtime.DBRRunnableParametrized;
import org.jkiss.dbeaver.registry.DataSourcePreferenceStore;
import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor;
import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
@@ -211,7 +214,8 @@ public class WebConnectionInfo {
@Property
public String getNodePath() {
return DBNDataSource.makeDataSourceItemPath(dataSourceContainer);
DBNDatabaseNode dsNode = DBNUtils.getNodeByObject(dataSourceContainer);
return dsNode == null ? null : dsNode.getNodeUri();
}
@Property
@@ -293,6 +297,16 @@ public class WebConnectionInfo {
return dataSourceContainer.getNavigatorSettings().getOriginalSettings();
}
@Property
@NotNull
public Map<String, String> getDefaultUserPreferences() {
DBPPreferenceStore preferenceStore = dataSourceContainer.getPreferenceStore();
if (preferenceStore instanceof DataSourcePreferenceStore dataSourcePreferenceStore) {
return dataSourcePreferenceStore.getProperties();
}
return Collections.emptyMap();
}
@Property
@NotNull
public List<WebDataFormat> getSupportedDataFormats() {
@@ -572,10 +586,16 @@ 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.
*/
public void setCredentialsSavedInSession(@Nullable Boolean credentialsSavedInSession) {
this.credentialsSavedInSession = credentialsSavedInSession;
}
}
@@ -16,6 +16,8 @@
*/
package io.cloudbeaver.model;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.net.DBWHandlerType;
import org.jkiss.utils.CommonUtils;
@@ -28,12 +30,14 @@ import java.util.Map;
*/
public class WebNetworkHandlerConfigInput {
@NotNull
private final Map<String, Object> cfg;
public WebNetworkHandlerConfigInput(Map<String, Object> cfg) {
public WebNetworkHandlerConfigInput(@NotNull Map<String, Object> cfg) {
this.cfg = cfg;
}
@Nullable
public DBWHandlerType getType() {
return CommonUtils.valueOf(DBWHandlerType.class, JSONUtils.getString(cfg, "type"), null);
}
@@ -42,6 +46,7 @@ public class WebNetworkHandlerConfigInput {
return JSONUtils.getString(cfg, "id");
}
@Nullable
public Boolean isEnabled() {
if (cfg.containsKey("enabled")) {
return JSONUtils.getBoolean(cfg, "enabled");
@@ -62,11 +67,13 @@ public class WebNetworkHandlerConfigInput {
return JSONUtils.getString(cfg, "password");
}
@Deprecated // use secured properties
// Deprecated. Use secured properties
@Nullable
public String getKey() {
return JSONUtils.getString(cfg, "key");
}
@Nullable
public Boolean isSavePassword() {
if (cfg.containsKey("savePassword")) {
return JSONUtils.getBoolean(cfg, "savePassword");
@@ -75,10 +82,12 @@ public class WebNetworkHandlerConfigInput {
}
}
@Nullable
public Map<String, Object> getProperties() {
return JSONUtils.getObjectOrNull(cfg, "properties");
}
@Nullable
public Map<String, String> getSecureProperties() {
var secureProperties = JSONUtils.getObjectOrNull(cfg, "secureProperties");
if (secureProperties == null) {
@@ -0,0 +1,41 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.api.annotation;
import io.cloudbeaver.model.api.annotation.responses.ApiResponse;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Operation {
String summary() default "";
String description() default "";
String operationId() default "";
String method() default "";
Parameter[] parameters() default {};
ApiResponse[] responses() default {};
}
@@ -0,0 +1,51 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.api.annotation;
import io.cloudbeaver.model.api.annotation.enums.Explode;
import io.cloudbeaver.model.api.annotation.enums.ParameterIn;
import io.cloudbeaver.model.api.annotation.enums.ParameterStyle;
import io.cloudbeaver.model.api.annotation.media.ArraySchema;
import io.cloudbeaver.model.api.annotation.media.Schema;
import java.lang.annotation.*;
@Repeatable(Parameters.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
public @interface Parameter {
String name() default "";
String description() default "";
boolean required() default false;
String example() default "";
ParameterIn in() default ParameterIn.DEFAULT;
Schema schema() default @Schema;
ArraySchema array() default @ArraySchema;
ParameterStyle style() default ParameterStyle.DEFAULT;
Explode explode() default Explode.DEFAULT;
boolean hidden() default false;
}
@@ -0,0 +1,29 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.api.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
public @interface Parameters {
Parameter[] value();
}
@@ -0,0 +1,34 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.api.annotation.enums;
public enum Explode {
TRUE("true"),
FALSE("false"),
DEFAULT("default");
private final String value;
Explode(final String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
@@ -0,0 +1,36 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.api.annotation.enums;
public enum ParameterIn {
DEFAULT("default"),
QUERY("query"),
HEADER("header"),
PATH("path"),
COOKIE("cookie");
private final String value;
ParameterIn(final String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
@@ -0,0 +1,39 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.api.annotation.enums;
public enum ParameterStyle {
MATRIX("matrix"),
LABEL("label"),
FORM("form"),
SIMPLE("simple"),
SPACEDELIMITED("spaceDelimited"),
PIPEDELIMITED("pipeDelimited"),
DEEPOBJECT("deepObject"),
DEFAULT("default");
private final String value;
ParameterStyle(final String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
@@ -0,0 +1,29 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.api.annotation.media;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
public @interface ArraySchema {
Schema schema() default @Schema;
}
@@ -0,0 +1,29 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.api.annotation.media;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface Content {
Schema schema() default @Schema;
}
@@ -0,0 +1,37 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.api.annotation.media;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
public @interface Schema {
String description() default "";
String example() default "";
String type() default "";
Class<?> implementation() default Void.class;
boolean required() default false;
}
@@ -0,0 +1,35 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.api.annotation.responses;
import io.cloudbeaver.model.api.annotation.media.Content;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface ApiResponse {
String responseCode() default "default";
String description() default "";
Content[] content() default {};
}
@@ -0,0 +1,31 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.api.annotation.tags;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Tag {
String name() default "";
String description() default "";
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,10 +25,13 @@ import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBFileController;
import org.jkiss.dbeaver.model.app.DBPLockManagerProvider;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.auth.SMSessionContext;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.fs.lock.LockManager;
import org.jkiss.dbeaver.model.fs.lock.shared.SharedFileLockManager;
import org.jkiss.dbeaver.model.impl.app.ApplicationRegistry;
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
@@ -48,7 +51,7 @@ import java.util.Map;
/**
* Servlet application
*/
public abstract class BaseServletApplication extends BaseApplicationImpl implements ServletApplication {
public abstract class BaseServletApplication extends BaseApplicationImpl implements ServletApplication, DBPLockManagerProvider {
public static final String DEFAULT_CONFIG_FILE_PATH = "/etc/cloudbeaver.conf";
public static final String CUSTOM_CONFIG_FOLDER = "custom";
@@ -59,6 +62,7 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
private String instanceId;
@NotNull
@Override
public RMController createResourceController(
@NotNull SMCredentialsProvider credentialsProvider,
@@ -73,10 +77,16 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
throw new IllegalStateException("File controller is not supported by " + getClass().getSimpleName());
}
@Nullable
@NotNull
@Override
public Path getDefaultWorkingFolder() {
return getServerConfigurationController().getWorkspacePath();
public LockManager createLockManager(@NotNull String applicationId, @NotNull Path metadataFolder) throws DBException {
return new SharedFileLockManager(applicationId, metadataFolder);
}
@NotNull
@Override
public LockManager createLockManager() throws DBException {
return new SharedFileLockManager(getApplicationInstanceId());
}
@Override
@@ -202,6 +212,8 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
@Override
public Object start(IApplicationContext context) {
initializeApplicationServices();
setWorkspacePath(getServerConfigurationController().getWorkspacePath());
try {
startServer();
} catch (Exception e) {
@@ -238,13 +250,6 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
return BaseWorkspaceImpl.readWorkspaceIdProperty();
}
@NotNull
@Override
public Path getWorkspaceDirectory() {
return getServerConfigurationController().getWorkspacePath();
}
public String getApplicationId() {
try {
return getApplicationInstanceId();
@@ -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();
@@ -115,13 +115,6 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
return ((DBNResourceManagerRoot) getParentNode()).getResourceController();
}
@NotNull
@Deprecated
@Override
public String getNodeItemPath() {
return getParentNode().getNodeItemPath() + "/" + getName();
}
@Nullable
@Override
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
@@ -31,8 +31,6 @@ import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.rm.RMResource;
import org.jkiss.dbeaver.model.rm.RMResourceType;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.registry.ResourceTypeDescriptor;
import org.jkiss.dbeaver.registry.ResourceTypeRegistry;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.IOUtils;
@@ -44,7 +42,7 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
private final RMResource resource;
DBNResourceManagerResource(DBNNode parentNode, RMResource resource) {
DBNResourceManagerResource(@NotNull DBNNode parentNode, @NotNull RMResource resource) {
super(parentNode);
this.resource = resource;
}
@@ -71,6 +69,7 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
@Override
public DBPImage getNodeIcon() {
if (resource.isFolder()) {
/*
if (getParentNode() instanceof DBNResourceManagerResource) {
return DBIcon.TREE_FOLDER;
}
@@ -82,11 +81,12 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
if (folderResType != null) {
return folderResType.getFolderIcon();
}
*/
return DBIcon.TREE_FOLDER;
} else {
var fileExtension = IOUtils.getFileExtension(getNodeDisplayName());
if (!CommonUtils.isEmpty(fileExtension)) {
RMProject project = getProjectNode();
RMProject project = getRmProject();
if (project != null) {
for (RMResourceType rt : project.getResourceTypes()) {
if (ArrayUtils.contains(rt.getFileExtensions(), fileExtension)) {
@@ -99,7 +99,8 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
}
}
private RMProject getProjectNode() {
@Nullable
public RMProject getRmProject() {
for (DBNNode node = this; node != null; node = node.getParentNode()) {
if (node instanceof DBNResourceManagerProject) {
return ((DBNResourceManagerProject) node).getProject();
@@ -159,13 +160,6 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
throw new DBException("Can't detect resource root node");
}
@NotNull
@Deprecated
@Override
public String getNodeItemPath() {
return getParentNode().getNodeItemPath() + "/" + getNodeDisplayName();
}
@Nullable
@Override
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
@@ -115,14 +115,6 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
return projects;
}
@NotNull
@Deprecated
@Override
public String getNodeItemPath() {
// Path doesn't include project name
return NodePathType.ext.getPrefix() + getNodeDisplayName();
}
@Nullable
@Override
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
@@ -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.
@@ -26,7 +26,9 @@ import org.jkiss.dbeaver.model.DBPDataSourceFolder;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
import org.jkiss.dbeaver.model.fs.lock.LockManager;
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
import org.jkiss.dbeaver.model.rm.RMController;
import org.jkiss.dbeaver.model.rm.RMEvent;
import org.jkiss.dbeaver.model.rm.RMEventManager;
@@ -53,11 +55,11 @@ public abstract class BaseLocalResourceController implements RMController {
@NotNull
protected final DBPWorkspace workspace;
@NotNull
protected final FileLockController lockController;
protected final LockManager lockController;
protected BaseLocalResourceController(
@NotNull DBPWorkspace workspace,
@NotNull FileLockController lockController
@NotNull LockManager lockController
) {
this.workspace = workspace;
this.lockController = lockController;
@@ -65,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;
@@ -108,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,
@@ -157,7 +157,7 @@ public abstract class BaseLocalResourceController implements RMController {
@NotNull String configuration,
@Nullable List<String> dataSourceIds
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "updateProjectDataSources")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("updateProjectDataSources"))) {
DBPProject project = getWebProject(projectId, false);
return doFileWriteOperation(
projectId, project.getMetadataFolder(false),
@@ -188,7 +188,7 @@ public abstract class BaseLocalResourceController implements RMController {
@NotNull String projectId,
@NotNull String[] dataSourceIds
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "deleteDataSources")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteDataSources"))) {
DBPProject project = getWebProject(projectId, false);
doFileWriteOperation(projectId, project.getMetadataFolder(false), () -> {
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
@@ -213,7 +213,7 @@ public abstract class BaseLocalResourceController implements RMController {
@NotNull String projectId,
@NotNull String folderPath
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
DBPProject project = getWebProject(projectId, false);
log.debug("Creating data source folder '" + folderPath + "' in project '" + projectId + "'");
doFileWriteOperation(projectId, project.getMetadataFolder(false),
@@ -241,7 +241,7 @@ public abstract class BaseLocalResourceController implements RMController {
@NotNull String[] folderPaths,
boolean dropContents
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
DBPProject project = getWebProject(projectId, false);
doFileWriteOperation(projectId, project.getMetadataFolder(false),
() -> {
@@ -267,7 +267,7 @@ public abstract class BaseLocalResourceController implements RMController {
@NotNull String oldPath,
@NotNull String newPath
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
DBPProject project = getWebProject(projectId, false);
log.debug("Moving data source folder from '" + oldPath + "' to '" + newPath + "' in project '" + projectId + "'");
doFileWriteOperation(projectId, project.getMetadataFolder(false),
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,24 +33,29 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.SMCredentials;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.fs.lock.LockManager;
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
import org.jkiss.dbeaver.model.navigator.DBNLocalFolder;
import org.jkiss.dbeaver.model.net.DBWNetworkProfile;
import org.jkiss.dbeaver.model.rm.*;
import org.jkiss.dbeaver.model.security.SMAdminController;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.sql.DBQuotaException;
import org.jkiss.dbeaver.model.websocket.event.MessageType;
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
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;
import org.jkiss.dbeaver.registry.ResourceTypeRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.IOUtils;
import org.jkiss.utils.Pair;
@@ -90,9 +95,10 @@ public class LocalResourceController extends BaseLocalResourceController {
@NotNull Path rootPath,
@NotNull Path userProjectsPath,
@NotNull Path sharedProjectsPath,
@NotNull Supplier<SMAdminController> smControllerSupplier
@NotNull Supplier<SMAdminController> smControllerSupplier,
@NotNull LockManager lockController
) throws DBException {
super(workspace, new FileLockController(ServletAppUtils.getServletApplication().getApplicationInstanceId()));
super(workspace, lockController);
this.credentialsProvider = credentialsProvider;
this.rootPath = rootPath;
this.userProjectsPath = userProjectsPath;
@@ -101,7 +107,7 @@ public class LocalResourceController extends BaseLocalResourceController {
this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName();
this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers();
this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath);
this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath, lockController);
}
@NotNull
@@ -280,7 +286,7 @@ public class LocalResourceController extends BaseLocalResourceController {
throw new DBException("Error creating shared project path", e);
}
}
validateResourcePath(name);
GeneralUtils.validateResourceNameUnconditionally(name);
validateProjectName(null, name);
var projectPath = sharedProjectsPath.resolve(name);
if (Files.exists(projectPath)) {
@@ -303,7 +309,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@Override
public RMProject updateProject(@NotNull String projectId, @NotNull RMProjectInfo projectInfo) throws DBException {
validateProjectName(projectId, projectInfo.getName());
try (var ignoredLock = lockController.lock(projectId, "updateProject")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("updateProject"))) {
RMLocalProject project = getWebProject(projectId, false);
Path targetPath = getProjectPath(projectId);
if (!Files.exists(targetPath)) {
@@ -330,7 +336,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@Override
public void deleteProject(@NotNull String projectId) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "deleteProject")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteProject"))) {
Path targetPath = getProjectPath(projectId);
if (!Files.exists(targetPath)) {
log.error(MessageFormat.format("Project folder ''{0}'' is not found", projectId));
@@ -403,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;
}
@@ -497,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<>();
@@ -608,7 +653,6 @@ public class LocalResourceController extends BaseLocalResourceController {
)
);
}
}
@NotNull
@@ -699,7 +743,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@NotNull String resourcePath,
boolean isFolder
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "createResource")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createResource"))) {
validateResourcePath(resourcePath);
Path targetPath = getTargetPath(projectId, resourcePath);
if (Files.exists(targetPath)) {
@@ -731,7 +775,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@NotNull String oldResourcePath,
@NotNull String newResourcePath
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "moveResource")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("moveResource"))) {
var normalizedOldResourcePath = CommonUtils.normalizeResourcePath(oldResourcePath);
var normalizedNewResourcePath = CommonUtils.normalizeResourcePath(newResourcePath);
if (log.isDebugEnabled()) {
@@ -803,7 +847,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@Override
public void deleteResource(@NotNull String projectId, @NotNull String resourcePath, boolean recursive) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "deleteResource")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteResource"))) {
if (log.isDebugEnabled()) {
log.debug("Removing resource from '" + resourcePath + "' in project '" + projectId + "'" + (recursive ? " recursive" : ""));
}
@@ -891,7 +935,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@NotNull byte[] data,
boolean forceOverwrite
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "setResourceContents")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("setResourceContents"))) {
validateResourcePath(resourcePath);
Number fileSizeLimit = ServletAppUtils.getServletApplication()
.getAppConfiguration()
@@ -936,7 +980,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@NotNull String propertyName,
@Nullable Object propertyValue
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "resourcePropertyUpdate")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("resourcePropertyUpdate"))) {
validateResourcePath(resourcePath);
RMLocalProject webProject = getWebProject(projectId, false);
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
@@ -957,7 +1001,7 @@ public class LocalResourceController extends BaseLocalResourceController {
@NotNull String resourcePath,
@NotNull Map<String, Object> properties
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "resourcePropertyUpdate")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("resourcePropertyUpdate"))) {
validateResourcePath(resourcePath);
RMLocalProject webProject = getWebProject(projectId, false);
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
@@ -1103,6 +1147,7 @@ public class LocalResourceController extends BaseLocalResourceController {
RMProjectName project = WebRMUtils.parseProjectName(projectId);
RMProjectType type = project.getType();
String projectName = project.getName();
GeneralUtils.validateResourceNameUnconditionally(projectName);
switch (type) {
case GLOBAL:
if (!projectName.equals(globalProjectName)) {
@@ -1204,12 +1249,14 @@ public class LocalResourceController extends BaseLocalResourceController {
return getProjectPath(projectId).toAbsolutePath().relativize(path).toString().replace('\\', IPath.SEPARATOR);
}
@NotNull
public static Builder builder(
SMCredentialsProvider credentialsProvider,
DBPWorkspace workspace,
Supplier<SMAdminController> smControllerSupplier
@NotNull SMCredentialsProvider credentialsProvider,
@NotNull DBPWorkspace workspace,
@NotNull LockManager lockController,
@NotNull Supplier<SMAdminController> smControllerSupplier
) {
return new Builder(workspace, credentialsProvider, smControllerSupplier);
return new Builder(workspace, credentialsProvider, lockController, smControllerSupplier);
}
@Override
@@ -1225,10 +1272,13 @@ public class LocalResourceController extends BaseLocalResourceController {
protected Path rootPath;
protected Path userProjectsPath;
protected Path sharedProjectsPath;
protected LockManager lockController;
protected Builder(
DBPWorkspace workspace, SMCredentialsProvider credentialsProvider,
Supplier<SMAdminController> smControllerSupplier
@NotNull DBPWorkspace workspace,
@NotNull SMCredentialsProvider credentialsProvider,
@NotNull LockManager lockController,
@NotNull Supplier<SMAdminController> smControllerSupplier
) {
this.workspace = workspace;
this.credentialsProvider = credentialsProvider;
@@ -1236,25 +1286,38 @@ public class LocalResourceController extends BaseLocalResourceController {
this.rootPath = RMUtils.getRootPath();
this.userProjectsPath = RMUtils.getUserProjectsPath();
this.sharedProjectsPath = RMUtils.getSharedProjectsPath();
this.lockController = lockController;
}
public Builder setRootPath(Path rootPath) {
@NotNull
public Builder setRootPath(@NotNull Path rootPath) {
this.rootPath = rootPath;
return this;
}
public Builder setUserProjectsPath(Path userProjectsPath) {
@NotNull
public Builder setUserProjectsPath(@NotNull Path userProjectsPath) {
this.userProjectsPath = userProjectsPath;
return this;
}
public Builder setSharedProjectsPath(Path sharedProjectsPath) {
@NotNull
public Builder setSharedProjectsPath(@NotNull Path sharedProjectsPath) {
this.sharedProjectsPath = sharedProjectsPath;
return this;
}
@NotNull
public LocalResourceController build() throws DBException {
return new LocalResourceController(workspace, credentialsProvider, rootPath, userProjectsPath, sharedProjectsPath, smController);
return new LocalResourceController(
workspace,
credentialsProvider,
rootPath,
userProjectsPath,
sharedProjectsPath,
smController,
lockController
);
}
}
@@ -17,14 +17,15 @@
package io.cloudbeaver.model.rm.local;
import com.google.gson.reflect.TypeToken;
import io.cloudbeaver.utils.ServletAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
import org.jkiss.dbeaver.model.fs.lock.LockManager;
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
import org.jkiss.dbeaver.model.rm.RMProjectInfo;
import org.jkiss.dbeaver.model.rm.RMProjectType;
@@ -45,12 +46,12 @@ public class ProjectsMetadataInfo {
private final Map<String, RMProjectInfo> projectsInfo = new LinkedHashMap<>();
private final Path projectsPath;
private final FileLockController lockController;
private final LockManager lockController;
public ProjectsMetadataInfo(@NotNull Path projectsPath) throws DBException {
public ProjectsMetadataInfo(@NotNull Path projectsPath, @NotNull LockManager lockController) throws DBException {
this.projectsPath = projectsPath;
this.lockController = new FileLockController(ServletAppUtils.getServletApplication().getApplicationInstanceId());
this.lockController = lockController;
readProjectInfos(projectsPath);
}
@@ -139,7 +140,7 @@ public class ProjectsMetadataInfo {
}
private void saveProjectsInfo() {
try (var lock = lockController.lock(PROJECTS_INFO_FILE_NAME, "saveProjectsInfo")) {
try (var lock = lockController.lock(LockTarget.of(PROJECTS_INFO_FILE_NAME), LockOptions.of("saveProjectsInfo"))) {
log.info("Saving project information");
Files.writeString(projectsPath.resolve(PROJECTS_INFO_FILE_NAME), JSONUtils.GSON.toJson(projectsInfo));
} catch (IOException e) {
@@ -148,4 +149,4 @@ public class ProjectsMetadataInfo {
log.error("Error locking file " + PROJECTS_INFO_FILE_NAME + ": " + e.getMessage());
}
}
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,14 +55,21 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
@NotNull
protected final ServletApplication application;
protected volatile long lastAccessTime;
@NotNull
private final SessionType sessionType;
private final List<CBWebSessionEventHandler> sessionEventHandlers = new CopyOnWriteArrayList<>();
private WebSessionEventsFilter eventsFilter;
private final WebSessionWorkspace workspace;
public BaseWebSession(@NotNull String id, @NotNull ServletApplication application) throws DBException {
public BaseWebSession(
@NotNull String id,
@NotNull ServletApplication application,
@NotNull SessionType sessionType
) throws DBException {
this.id = id;
this.application = application;
this.sessionType = sessionType;
this.createTime = System.currentTimeMillis();
this.lastAccessTime = this.createTime;
this.workspace = createWebWorkspace();
@@ -102,7 +109,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
}
}
public abstract void addSessionError(Throwable exception);
public abstract void addSessionError(@NotNull Throwable exception);
public void addEventHandler(@NotNull CBWebSessionEventHandler handler) {
synchronized (sessionEventHandlers) {
@@ -116,11 +123,21 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
}
}
public synchronized boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
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);
}
public synchronized void refreshUserData() {
public void refreshUserData() {
try {
userContext.refreshPermissions();
if (userContext.isAuthorizedInSecurityManager()) {
@@ -150,8 +167,8 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
return workspace.getAuthContext();
}
protected void clearSessionContext() {
this.workspace.getAuthContext().clear();
protected synchronized void clearSessionContext() {
this.workspace.getAuthContext().clearContext(false);
this.workspace.getAuthContext().addSession(this);
}
@@ -176,27 +193,33 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
return lastAccessTime;
}
public synchronized void touchSession() {
@NotNull
public SessionType getSessionType() {
return sessionType;
}
public void touchSession() {
this.lastAccessTime = System.currentTimeMillis();
}
@NotNull
public synchronized WebUserContext getUserContext() {
public WebUserContext getUserContext() {
return userContext;
}
@Override
public void close() {
super.close();
cleanUpSession(true);
super.close();
}
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
cleanUpSession(sendSessionExpiredEvent);
super.close();
}
private void cleanUpSession(boolean sendSessionExpiredEvent) {
application.getEventController().addEvent(new WSEventDeleteTempFile(getSessionId()));
application.getEventController().addEvent(new WSEventDeleteTempFile(getUserContext().getSmSessionId()));
synchronized (sessionEventHandlers) {
var sessionExpiredEvent = new WSSessionExpiredEvent();
for (CBWebSessionEventHandler sessionEventHandler : sessionEventHandlers) {
@@ -226,7 +249,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
this.eventsFilter = eventsFilter;
}
public boolean isProjectAccessible(String projectId) {
public boolean isProjectAccessible(@NotNull String projectId) {
return userContext.getAccessibleProjectIds().contains(projectId);
}
@@ -243,7 +266,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
userContext.getAccessibleProjectIds().remove(projectId);
}
public abstract void addSessionMessage(WebServerMessage message);
public abstract void addSessionMessage(@NotNull WebServerMessage message);
@Property
public boolean isValid() {
@@ -0,0 +1,23 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.session;
public enum SessionType {
WEB,
HEADLESS,
API_TOKEN
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,16 +31,16 @@ public class WebHeadlessSession extends BaseWebSession {
@NotNull String id,
@NotNull ServletAuthApplication application
) throws DBException {
super(id, application);
super(id, application, SessionType.HEADLESS);
}
@Override
public void addSessionError(Throwable exception) {
public void addSessionError(@NotNull Throwable exception) {
}
@Override
public void addSessionMessage(WebServerMessage message) {
public void addSessionMessage(@NotNull WebServerMessage message) {
}
@@ -17,6 +17,7 @@
package io.cloudbeaver.model.session;
import jakarta.servlet.http.HttpServletRequest;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.utils.HttpConstants;
@@ -31,12 +32,16 @@ public class WebHttpRequestInfo {
private final String lastRemoteAddress;
@Nullable
private final String lastRemoteUserAgent;
@NotNull
private final SessionType sessionType;
public WebHttpRequestInfo(HttpServletRequest request) {
this.id = request.getSession() == null ? null : request.getSession().getId();
this.locale = request.getAttribute("locale");
this.lastRemoteAddress = request.getRemoteAddr();
this.lastRemoteUserAgent = request.getHeader(USER_AGENT);
this(
request.getSession() == null ? null : request.getSession().getId(),
request.getAttribute("locale"),
request.getRemoteAddr(),
request.getHeader(USER_AGENT)
);
}
public WebHttpRequestInfo(
@@ -44,11 +49,22 @@ public class WebHttpRequestInfo {
@Nullable Object locale,
@Nullable String lastRemoteAddress,
@Nullable String lastRemoteUserAgent
) {
this(id, locale, lastRemoteAddress, lastRemoteUserAgent, SessionType.WEB);
}
public WebHttpRequestInfo(
@Nullable String id,
@Nullable Object locale,
@Nullable String lastRemoteAddress,
@Nullable String lastRemoteUserAgent,
@NotNull SessionType sessionType
) {
this.id = id;
this.locale = locale;
this.lastRemoteAddress = lastRemoteAddress;
this.lastRemoteUserAgent = lastRemoteUserAgent;
this.sessionType = sessionType;
}
@Nullable
@@ -70,4 +86,9 @@ public class WebHttpRequestInfo {
public String getLastRemoteUserAgent() {
return lastRemoteUserAgent;
}
@NotNull
public SessionType getSessionType() {
return sessionType;
}
}
@@ -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.
@@ -64,6 +64,8 @@ import java.time.Instant;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
/**
* Web session.
@@ -99,6 +101,7 @@ public class WebSession extends BaseWebSession
private final List<WebAuthInfo> authTokens = new ArrayList<>();
private DBNModel navigatorModel;
private final ReadWriteLock navigatorModelLock = new ReentrantReadWriteLock(true);
private final DBRProgressMonitor progressMonitor = new SessionProgressMonitor();
private final Map<String, DBWSessionHandler<WebSession>> sessionHandlers;
private final WebDataSourceConnectEventListener connectListener = new WebDataSourceConnectEventListener(this);
@@ -113,7 +116,8 @@ public class WebSession extends BaseWebSession
CommonUtils.toString(requestInfo.getLocale()),
application,
sessionHandlers,
requestInfo.getLastRemoteAddress()
requestInfo.getLastRemoteAddress(),
requestInfo.getSessionType()
);
updateSessionParameters(requestInfo);
}
@@ -125,7 +129,18 @@ public class WebSession extends BaseWebSession
@NotNull Map<String, DBWSessionHandler<WebSession>> sessionHandlers,
@Nullable String remoteAddr
) throws DBException {
super(id, application);
this(id, locale, application, sessionHandlers, remoteAddr, SessionType.WEB);
}
protected WebSession(
@NotNull String id,
@Nullable String locale,
@NotNull ServletApplication application,
@NotNull Map<String, DBWSessionHandler<WebSession>> sessionHandlers,
@Nullable String remoteAddr,
@NotNull SessionType sessionType
) throws DBException {
super(id, application, sessionType);
if (CommonUtils.isEmpty(remoteAddr)) {
throw new DBException("Remote address cannot be empty");
}
@@ -185,11 +200,12 @@ public class WebSession extends BaseWebSession
this.cacheExpired = cacheExpired;
}
public synchronized WebUser getUser() {
public WebUser getUser() {
return this.userContext.getUser();
}
public synchronized Map<String, String> getUserMetaParameters() {
@NotNull
public Map<String, String> getUserMetaParameters() {
var user = getUser();
if (user == null) {
return Map.of();
@@ -201,20 +217,20 @@ public class WebSession extends BaseWebSession
return allMetaParams;
}
public synchronized String getUserId() {
public String getUserId() {
return userContext.getUserId();
}
public synchronized boolean hasPermission(String perm) {
return getSessionPermissions().contains(DBWConstants.PERMISSION_ADMIN) ||
getSessionPermissions().contains(perm);
Set<String> sessionPermissions = getSessionPermissions();
return sessionPermissions.contains(DBWConstants.PERMISSION_ADMIN) || sessionPermissions.contains(perm);
}
public synchronized boolean isAuthorizedInSecurityManager() {
public boolean isAuthorizedInSecurityManager() {
return userContext.isAuthorizedInSecurityManager();
}
public synchronized Set<String> getSessionPermissions() {
public Set<String> getSessionPermissions() {
if (userContext.getUserPermissions() == null) {
refreshSessionAuth();
}
@@ -222,12 +238,12 @@ public class WebSession extends BaseWebSession
}
@NotNull
public synchronized SMController getSecurityController() {
public SMController getSecurityController() {
return userContext.getSecurityController();
}
@NotNull
public synchronized SMAdminController getAdminSecurityController() throws DBException {
public SMAdminController getAdminSecurityController() throws DBException {
if (!hasPermission(DBWConstants.PERMISSION_ADMIN)) {
throw new DBException("Admin permissions required");
}
@@ -235,17 +251,17 @@ public class WebSession extends BaseWebSession
}
@NotNull
public synchronized RMController getRmController() {
public RMController getRmController() {
return userContext.getRmController();
}
@NotNull
public synchronized DBFileController getFileController() {
public DBFileController getFileController() {
return userContext.getFileController();
}
@Override
public synchronized void refreshUserData() {
public void refreshUserData() {
super.refreshUserData();
refreshSessionAuth();
@@ -253,7 +269,7 @@ public class WebSession extends BaseWebSession
}
// Note: for admin use only
public synchronized void resetUserState(boolean needResetUserCache) throws DBException {
public void resetUserState(boolean needResetUserCache) throws DBException {
clearAuthTokens();
if (needResetUserCache) {
try {
@@ -267,7 +283,7 @@ public class WebSession extends BaseWebSession
clearSessionContext();
}
public synchronized void resetUserState() throws DBException {
public void resetUserState() throws DBException {
resetUserState(true);
}
@@ -286,21 +302,25 @@ public class WebSession extends BaseWebSession
}
private void initNavigatorModel() {
navigatorModelLock.writeLock().lock();
try {
// Cleanup current data
if (this.navigatorModel != null) {
this.navigatorModel.dispose();
this.navigatorModel = null;
}
this.globalProject = null;
// Cleanup current data
if (this.navigatorModel != null) {
this.navigatorModel.dispose();
this.navigatorModel = null;
loadProjects();
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), getWorkspace());
this.navigatorModel.setModelAuthContext(getWorkspace().getAuthContext());
this.navigatorModel.initialize();
this.locale = Locale.getDefault().getLanguage();
} finally {
navigatorModelLock.writeLock().unlock();
}
this.globalProject = null;
loadProjects();
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), getWorkspace().getProjects());
this.navigatorModel.setModelAuthContext(getWorkspace().getAuthContext());
this.navigatorModel.initialize();
this.locale = Locale.getDefault().getLanguage();
}
private void loadProjects() {
@@ -368,11 +388,16 @@ public class WebSession extends BaseWebSession
}
private void resetNavigationModel() {
getWorkspace().getProjects().forEach(WebSessionProjectImpl::dispose);
navigatorModelLock.writeLock().lock();
try {
getWorkspace().getProjects().forEach(WebSessionProjectImpl::dispose);
if (this.navigatorModel != null) {
this.navigatorModel.dispose();
this.navigatorModel = null;
if (this.navigatorModel != null) {
this.navigatorModel.dispose();
this.navigatorModel = null;
}
} finally {
navigatorModelLock.writeLock().unlock();
}
}
@@ -415,15 +440,25 @@ public class WebSession extends BaseWebSession
@Nullable
public DBNModel getNavigatorModel() {
return navigatorModel;
navigatorModelLock.readLock().lock();
try {
return navigatorModel;
} finally {
navigatorModelLock.readLock().unlock();
}
}
@NotNull
public DBNModel getNavigatorModelOrThrow() throws DBWebException {
if (navigatorModel != null) {
return navigatorModel;
navigatorModelLock.readLock().lock();
try {
if (navigatorModel != null) {
return navigatorModel;
}
throw new DBWebException("Navigator model is not found in session");
} finally {
navigatorModelLock.readLock().unlock();
}
throw new DBWebException("Navigator model is not found in session");
}
/**
* Returns and clears progress messages
@@ -437,7 +472,7 @@ public class WebSession extends BaseWebSession
}
}
public synchronized void updateInfo(boolean isOldHttpSessionUsed) {
public void updateInfo(boolean isOldHttpSessionUsed) {
log.trace("Update session lifetime " + getSessionId() + " for user " + getUserId());
touchSession();
if (isOldHttpSessionUsed) {
@@ -469,7 +504,6 @@ public class WebSession extends BaseWebSession
public void close() {
try {
resetNavigationModel();
resetSessionCache();
} catch (Throwable e) {
log.error(e);
}
@@ -486,7 +520,6 @@ public class WebSession extends BaseWebSession
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
try {
resetNavigationModel();
resetSessionCache();
} catch (Throwable e) {
log.error(e);
}
@@ -501,6 +534,7 @@ public class WebSession extends BaseWebSession
super.close(clearTokens, sendSessionExpiredEvent);
}
@NotNull
private List<WebAuthInfo> clearAuthTokens() throws DBException {
ArrayList<WebAuthInfo> tokensCopy;
synchronized (authTokens) {
@@ -585,11 +619,13 @@ public class WebSession extends BaseWebSession
}
}
@NotNull
public WebAsyncTaskInfo createAndRunAsyncTask(@NotNull String taskName, @NotNull WebAsyncTaskProcessor<?> runnable) {
WebAsyncTaskInfo asyncTask = createAsyncTask(taskName);
return runAsyncTask(asyncTask, runnable);
}
@NotNull
public WebAsyncTaskInfo runAsyncTask(@NotNull WebAsyncTaskInfo asyncTask, @NotNull WebAsyncTaskProcessor<?> runnable) {
AbstractJob job = new AbstractCancelableJob(asyncTask.getName()) {
@NotNull
@@ -630,11 +666,11 @@ public class WebSession extends BaseWebSession
return asyncTask;
}
public void addSessionError(Throwable exception) {
public void addSessionError(@NotNull Throwable exception) {
addSessionMessage(new WebServerMessage(exception));
}
public void addSessionMessage(WebServerMessage message) {
public void addSessionMessage(@NotNull WebServerMessage message) {
synchronized (sessionMessages) {
sessionMessages.add(message);
}
@@ -645,14 +681,15 @@ public class WebSession extends BaseWebSession
message.getMessage()));
}
public void addInfoMessage(String message) {
public void addInfoMessage(@NotNull String message) {
addSessionMessage(new WebServerMessage(MessageType.INFO, message));
}
public void addWarningMessage(String message) {
public void addWarningMessage(@NotNull String message) {
addSessionMessage(new WebServerMessage(MessageType.WARNING, message));
}
@NotNull
public List<WebServerMessage> readLog(Integer maxEntries, Boolean clearLog) {
synchronized (sessionMessages) {
List<WebServerMessage> messages = new ArrayList<>();
@@ -700,7 +737,7 @@ public class WebSession extends BaseWebSession
}
}
@NotNull
public List<WebAuthInfo> getAllAuthInfo() {
synchronized (authTokens) {
return new ArrayList<>(authTokens);
@@ -752,7 +789,7 @@ public class WebSession extends BaseWebSession
});
}
private void removeAuthInfo(WebAuthInfo oldAuthInfo) {
private void removeAuthInfo(@NotNull WebAuthInfo oldAuthInfo) {
oldAuthInfo.closeAuth();
synchronized (authTokens) {
authTokens.remove(oldAuthInfo);
@@ -778,6 +815,7 @@ public class WebSession extends BaseWebSession
return oldInfo;
}
@Nullable
public List<DBACredentialsProvider> getContextCredentialsProviders() {
return getAdapters(DBACredentialsProvider.class);
}
@@ -865,11 +903,11 @@ public class WebSession extends BaseWebSession
return parameters;
}
public synchronized void resetAuthToken() throws DBException {
public void resetAuthToken() throws DBException {
this.userContext.reset();
}
public synchronized boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
public boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
boolean contextChanged = super.updateSMSession(smAuthInfo);
if (contextChanged) {
refreshUserData();
@@ -919,8 +957,13 @@ public class WebSession extends BaseWebSession
*/
public void addSessionProject(@NotNull WebSessionProjectImpl project) {
getWorkspace().addProject(project);
if (navigatorModel != null) {
navigatorModel.getRoot().addProject(project, false);
navigatorModelLock.readLock().lock();
try {
if (navigatorModel != null) {
navigatorModel.getRoot().addProject(project, false);
}
} finally {
navigatorModelLock.readLock().unlock();
}
}
@@ -936,8 +979,13 @@ public class WebSession extends BaseWebSession
project.dispose();
getWorkspace().removeProject(project);
if (navigatorModel != null) {
navigatorModel.getRoot().removeProject(project);
navigatorModelLock.readLock().lock();
try {
if (navigatorModel != null) {
navigatorModel.getRoot().removeProject(project);
}
} finally {
navigatorModelLock.readLock().unlock();
}
}
@@ -1001,7 +1049,6 @@ public class WebSession extends BaseWebSession
}
}
public void handleTaskConfirmationWithParameters(@NotNull String taskId, @NotNull Map<String, Object> parameters) {
String attributeName = getTaskConfirmationAttributeName(taskId);
CompletableFuture<Map<String, Object>> confirmationFuture = getAttribute(attributeName);
@@ -1013,6 +1060,16 @@ public class WebSession extends BaseWebSession
}
}
public void handleActionCancelledEvent(@NotNull String actionId) {
Object attributeValue = getAttribute(actionId);
if (!(attributeValue instanceof CompletableFuture<?> future)) {
log.info("Action to be cancelled not found, skipping: " + actionId);
return;
}
future.completeExceptionally(new DBWebException("Action cancelled by user: " + actionId));
removeAttribute(actionId);
}
@NotNull
private String getTaskConfirmationAttributeName(@NotNull String taskId) {
return WebSQLConstants.TASK_CONFIRMATION_ATTR_PREFIX + taskId;
@@ -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;
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package io.cloudbeaver.model.session;
import io.cloudbeaver.WebSessionProjectImpl;
import io.cloudbeaver.utils.ServletAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
@@ -26,6 +27,7 @@ import org.jkiss.dbeaver.model.DBPImage;
import org.jkiss.dbeaver.model.app.DBPPlatform;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.fs.DBFFileSystemManager;
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
import org.jkiss.dbeaver.model.rm.RMUtils;
import org.jkiss.dbeaver.runtime.DBWorkbench;
@@ -45,6 +47,7 @@ public class WebSessionWorkspace implements DBPWorkspace {
private final SessionContextImpl workspaceAuthContext;
private final List<WebSessionProjectImpl> accessibleProjects = new ArrayList<>();
private WebSessionProjectImpl activeProject;
private DBFFileSystemManager fileSystemManager;
public WebSessionWorkspace(BaseWebSession session) {
this.session = session;
@@ -199,12 +202,28 @@ public class WebSessionWorkspace implements DBPWorkspace {
@Override
public boolean hasRealmPermission(@NotNull String permission) {
if (getWebSession() instanceof WebSession webSession) {
return webSession.getSessionPermissions().contains(permission);
}
return false;
}
@Override
public boolean supportsRealmFeature(@NotNull String feature) {
return false;
return ServletAppUtils.getServletApplication().getAppConfiguration().isFeatureEnabled(feature);
}
@NotNull
@Override
public synchronized DBFFileSystemManager getFileSystemManager() {
if (fileSystemManager == null) {
synchronized (this) {
if (fileSystemManager == null) {
fileSystemManager = new DBFFileSystemManager(session.getWorkspace());
}
}
}
return fileSystemManager;
}
}
@@ -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,9 @@ public class WebUserContext implements SMCredentialsProvider {
private final ServletApplication application;
private final DBPWorkspace workspace;
private WebUser user;
private Set<String> userPermissions;
private SMCredentials smCredentials = null;
private volatile WebUser user;
private volatile Set<String> userPermissions;
private volatile SMCredentials smCredentials = null;
private String smSessionId;
private String refreshToken;
@@ -87,7 +87,7 @@ public class WebUserContext implements SMCredentialsProvider {
* @return - true if context changed
* @throws DBException - if user already authorized and new token come from another user
*/
public synchronized boolean refresh(@NotNull SMAuthInfo smAuthInfo) throws DBException {
public boolean refresh(@NotNull SMAuthInfo smAuthInfo) throws DBException {
if (smAuthInfo.getAuthPermissions() == null && !isAuthorizedInSecurityManager()) {
throw new DBCException("Required information about session permissions is missing");
}
@@ -175,56 +175,56 @@ public class WebUserContext implements SMCredentialsProvider {
}
@NotNull
public synchronized SMController getSecurityController() {
public SMController getSecurityController() {
return securityController;
}
@Nullable
@Override
public synchronized SMCredentials getActiveUserCredentials() {
public SMCredentials getActiveUserCredentials() {
return smCredentials;
}
public synchronized boolean isAuthorizedInSecurityManager() {
public boolean isAuthorizedInSecurityManager() {
return smCredentials != null;
}
public synchronized boolean isNonAnonymousUserAuthorizedInSM() {
public boolean isNonAnonymousUserAuthorizedInSM() {
return isAuthorizedInSecurityManager() && getUser() != null;
}
public synchronized boolean isAnonymousUserAuthorizedInSM() {
public boolean isAnonymousUserAuthorizedInSM() {
return isAuthorizedInSecurityManager() && getUser() == null;
}
@Nullable
public synchronized WebUser getUser() {
public WebUser getUser() {
return user;
}
@Nullable
public synchronized String getUserId() {
public String getUserId() {
return user == null ? null : user.getUserId();
}
protected synchronized void setUser(@Nullable WebUser user) {
protected void setUser(@Nullable WebUser user) {
this.user = user;
}
@NotNull
public synchronized SMAdminController getAdminSecurityController() {
public SMAdminController getAdminSecurityController() {
return adminSecurityController;
}
@Nullable
public synchronized Set<String> getUserPermissions() {
public Set<String> getUserPermissions() {
return userPermissions;
}
/**
* reread the current user's permissions
*/
public synchronized void refreshPermissions() throws DBException {
public void refreshPermissions() throws DBException {
if (isAuthorizedInSecurityManager()) {
log.debug("refresh permissions " + getUserId() + " " + getSmSessionId());
setUserPermissions(securityController.getTokenPermissions().getPermissions());
@@ -245,8 +245,8 @@ public class WebUserContext implements SMCredentialsProvider {
return secretController;
}
@NotNull
public synchronized String getSmSessionId() {
@Nullable
public String getSmSessionId() {
return smSessionId;
}
@@ -114,4 +114,9 @@ public class WebUser {
public String getDisableReason() {
return user.getDisableReason();
}
@Nullable
public Instant getLastLoginTime() {
return user.getLastLoginTime();
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,8 @@ import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
import org.jkiss.dbeaver.model.security.SMAuthCredentialsProfile;
import org.jkiss.dbeaver.model.security.SMAuthProviderDescriptor;
import org.jkiss.dbeaver.model.security.SMSubjectType;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.dbeaver.utils.SystemVariablesResolver;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;
@@ -110,7 +112,9 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
}
public String getDescription() {
return cfg.getAttribute(WebRegistryConstant.ATTR_DESCRIPTION);
return GeneralUtils.replaceVariables(
cfg.getAttribute(WebRegistryConstant.ATTR_DESCRIPTION), SystemVariablesResolver.INSTANCE
);
}
public DBPImage getIcon() {
@@ -28,49 +28,34 @@ public class CBConstants {
public static final String RUNTIME_PRODUCT_CONFIG_FILE_NAME = ".product.runtime.conf";
public static final String AUTO_CONFIG_FILE_NAME = ".cloudbeaver.auto.conf";
public static final String PARAM_SERVER_CONFIGURATION = "server";
public static final String PARAM_SERVER_PORT = "serverPort";
public static final String PARAM_SERVER_HOST = "serverHost";
public static final String PARAM_SERVER_NAME = "serverName";
public static final String PARAM_FORCE_HTTPS = "forceHttps";
public static final String PARAM_BIND_SESSION_TO_IP = "bindSessionToIp";
public static final String PARAM_SUPPORTED_HOSTS = "supportedHosts";
public static final String PARAM_SSL_CONFIGURATION_PATH = "sslConfigurationPath";
public static final String PARAM_CONTENT_ROOT = "contentRoot";
public static final String PARAM_ROOT_URI = "rootURI";
public static final String PARAM_SERVICES_URI = "serviceURI";
public static final String PARAM_DRIVERS_LOCATION = "driversLocation";
public static final String PARAM_WORKSPACE_LOCATION = "workspaceLocation";
//legacy path to a product.conf file
@Deprecated
public static final String PARAM_PRODUCT_CONFIGURATION = "productConfiguration";
public static final String PARAM_PRODUCT_SETTINGS = "productSettings";
public static final String PARAM_EXTERNAL_PROPERTIES = "externalProperties";
public static final String PARAM_STATIC_CONTENT = "staticContent";
public static final String PARAM_RESOURCE_QUOTAS = "resourceQuotas";
public static final String PARAM_RESOURCE_MANAGER_ENABLED = "resourceManagerEnabled";
public static final String PARAM_SECRET_MANAGER_ENABLED = "secretManagerEnabled";
public static final String PARAM_SHOW_READ_ONLY_CONN_INFO = "showReadOnlyConnectionInfo";
public static final String PARAM_CONN_GRANT_ANON_ACCESS = "grantConnectionsAccessToAnonymousTeam";
public static final String PARAM_AUTH_PROVIDERS = "authConfiguration";
public static final String PARAM_DB_CONFIGURATION = "database";
public static final String PARAM_SESSION_EXPIRE_PERIOD = "expireSessionAfterPeriod";
public static final String PARAM_DEVEL_MODE = "develMode";
public static final String PARAM_SECURITY_MANAGER = "enableSecurityManager";
public static final String PARAM_SM_CONFIGURATION = "sm";
public static final String PARAM_PASSWORD_POLICY_CONFIGURATION = "passwordPolicy";
public static final int DEFAULT_SERVER_PORT = 8080;
//public static final String DEFAULT_SERVER_NAME = "CloudBeaver Web Server";
public static final String DEFAULT_CONTENT_ROOT = "/var/www/cloudbeaver";
public static final String DEFAULT_ROOT_URI = "/";
public static final String DEFAULT_SERVICES_URI = "/api/";
public static final String DEFAULT_DEPLOY_LOCATION = "/opt/cloudbeaver";
public static final String DEFAULT_DRIVERS_LOCATION = DEFAULT_DEPLOY_LOCATION + "/drivers";
public static final String DEFAULT_WORKSPACE_LOCATION = DEFAULT_DEPLOY_LOCATION + "/workspace";
public static final String DEFAULT_PRODUCT_CONFIGURATION = "conf/product.conf";
public static final String DEFAULT_DATASOURCE_PERMISSIONS_CONFIGURATION = "data-sources-permissions.json";
public static final String DEFAULT_ADMIN_NAME = "cbadmin";
@@ -83,14 +68,11 @@ public class CBConstants {
public static final String VAR_HOST_DOCKER_INTERNAL = "host.docker.internal";
public static final String VAR_AUTO_CB_SERVER_NAME = "CB_SERVER_NAME";
public static final String VAR_AUTO_CB_SERVER_URL = "CB_SERVER_URL";
public static final String VAR_AUTO_CB_ADMIN_NAME = "CB_ADMIN_NAME";
public static final String VAR_AUTO_CB_ADMIN_PASSWORD = "CB_ADMIN_PASSWORD";
public static final String CB_SESSION_COOKIE_NAME = "cb-session-id";
public static final String APPLICATION_JSON = "application/json";
public static final String ADMIN_AUTO_GRANT = "auto-grant";
public static final String HOST_LOCALHOST = "localhost";
public static final String HOST_127_0_0_1 = "127.0.0.1";
@@ -100,4 +82,6 @@ public class CBConstants {
public static final String DEFAULT_CLOUD_PROJECT_NAME = "GlobalConfiguration";
public static final String SECURED_VALUE = "******";
public static final String PREF_DATA_EDITOR_EXPORT_DISABLED_OLD = "plugin.data-viewer.export.disabled";
public static final String PREF_DATA_EDITOR_IMPORT_DISABLED_OLD = "plugin.data-import.disabled";
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,11 +20,14 @@ import io.cloudbeaver.WebProjectImpl;
import io.cloudbeaver.model.app.ServletApplication;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.app.DBPPlatform;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.auth.SMSession;
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.utils.CommonUtils;
import java.io.IOException;
@@ -51,12 +54,18 @@ public class ServerGlobalWorkspace extends BaseWorkspaceImpl {
@NotNull DBPPlatform platform,
@NotNull ServletApplication application
) {
super(platform, application.getWorkspaceDirectory());
super(platform, application.getWorkspacePath());
this.application = application;
}
@NotNull
@Override
public void initializeProjects() {
protected SMSession acquireWorkspaceSession(@NotNull DBRProgressMonitor monitor) throws DBException {
return new ServerGlobalWorkspaceSession(this);
}
@Override
public void initializeProjects() throws DBException {
initializeWorkspaceSession();
// Load global project
@@ -106,4 +115,9 @@ public class ServerGlobalWorkspace extends BaseWorkspaceImpl {
}
return null;
}
@Override
public boolean supportsRealmFeature(@NotNull String feature) {
return application.getAppConfiguration().isFeatureEnabled(feature);
}
}
@@ -0,0 +1,36 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.server;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.impl.app.LocalWorkspaceSession;
import org.jkiss.dbeaver.model.secret.DBSSecretController;
public class ServerGlobalWorkspaceSession extends LocalWorkspaceSession {
public ServerGlobalWorkspaceSession(@NotNull DBPWorkspace workspace) {
super(workspace);
}
@Nullable
@Override
public DBSSecretController getSecretController() {
return null;
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
*/
package io.cloudbeaver.service.sql;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.data.DBDAttributeConstraint;
import org.jkiss.dbeaver.model.gis.DBGeometry;
import org.jkiss.utils.CommonUtils;
@@ -27,6 +28,7 @@ import java.util.Map;
*/
public class WebSQLDataFilterConstraint {
private String attributeName;
private Integer attributePosition;
private Integer orderPosition;
private Boolean orderAsc;
@@ -38,8 +40,9 @@ public class WebSQLDataFilterConstraint {
private WebSQLDataFilterConstraint() {
}
public WebSQLDataFilterConstraint(Map<String, Object> map) {
this.attributePosition = CommonUtils.toInt(map.get("attributePosition"));
public WebSQLDataFilterConstraint(@NotNull Map<String, Object> map) {
this.attributeName = CommonUtils.toString(map.get("attributeName"));
this.attributePosition = CommonUtils.toInteger(map.get("attributePosition"), null);
this.orderPosition = map.containsKey("orderPosition") ?
CommonUtils.toInt(map.get("orderPosition")) + 1 : // Use position + 1 because 0 means no ordering (because of legacy compatibility)
null;
@@ -64,8 +67,10 @@ public class WebSQLDataFilterConstraint {
}
public static WebSQLDataFilterConstraint from(DBDAttributeConstraint constraint) {
@NotNull
public static WebSQLDataFilterConstraint from(@NotNull DBDAttributeConstraint constraint) {
var webConstraint = new WebSQLDataFilterConstraint();
webConstraint.attributeName = constraint.getAttributeName();
webConstraint.orderPosition = constraint.getOrderPosition();
webConstraint.orderAsc = !constraint.isOrderDescending();
webConstraint.criteria = constraint.getCriteria();
@@ -76,6 +81,10 @@ public class WebSQLDataFilterConstraint {
return webConstraint;
}
public String getAttributeName() {
return attributeName;
}
public Integer getAttributePosition() {
return attributePosition;
}
@@ -24,6 +24,7 @@ import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.utils.HttpConstants;
import java.net.CookieManager;
import java.net.URI;
@@ -127,7 +128,7 @@ public class WebGQLClient {
.uri(URI.create(apiUrl))
.POST(HttpRequest.BodyPublishers.ofString(makeGQLRequest(query, variables)))
.setHeader("TE-Client-Version", GeneralUtils.getMajorVersion())
.header("Content-Type", "application/json");
.header(HttpConstants.HEADER_CONTENT_TYPE, HttpConstants.CONTENT_TYPE_JSON);
if (!requestHeaders.isEmpty()) {
requestHeaders.forEach(requestBuilder::header);
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,6 +47,8 @@ public class ServletAppUtils {
private static final String HEADER_REFERER = "Referer";
private static final String HEADER_FORWARDED_SCHEME = "X-Forwarded-Scheme";
private static final String HEADER_FORWARDED_PROTO = "X-Forwarded-Proto";
private static final String HEADER_FORWARDED_PORT = "X-Forwarded-Port";
private static final String HEADER_FORWARDED_HOST = "X-Forwarded-Host";
private static final Set<Integer> DEFAULT_PORTS = Set.of(
80,
@@ -191,8 +193,12 @@ public class ServletAppUtils {
@NotNull String origin
) throws DBException {
String finalOrigin = webAuthApplication.modifyOrigin(origin);
StringBuilder authUriBuilder = new StringBuilder(removeSideSlashes(finalOrigin));
String serviceUriSegment = removeSideSlashes(webAuthApplication.getAuthServiceUriSegment());
String rootUri = removeSideSlashes(getServletApplication().getRootURI());
if (finalOrigin.endsWith("/" + rootUri) && serviceUriSegment.startsWith(rootUri + "/")) {
finalOrigin = finalOrigin.substring(0, finalOrigin.length() - rootUri.length());
}
StringBuilder authUriBuilder = new StringBuilder(removeSideSlashes(finalOrigin));
if (CommonUtils.isNotEmpty(serviceUriSegment)) {
authUriBuilder.append("/").append(serviceUriSegment);
}
@@ -294,19 +300,34 @@ public class ServletAppUtils {
@NotNull
public static String getOriginFromRequest(@NotNull HttpServletRequest request) {
String origin = request.getHeader(HEADER_ORIGIN);
if (log.isTraceEnabled()) {
log.trace("Origin header: " + origin);
}
if ("null".equals(origin)) {
//strange identity provider bug
origin = null;
}
if (CommonUtils.isEmpty(origin)) {
origin = request.getHeader(HEADER_X_ORIGIN);
}
if (CommonUtils.isEmpty(origin)) {
origin = request.getHeader(HEADER_REFERER);
if (log.isTraceEnabled()) {
log.trace("X-Origin header: " + origin);
}
}
String forwardedScheme = request.getHeader(HEADER_FORWARDED_SCHEME);
if (CommonUtils.isEmpty(forwardedScheme)) {
forwardedScheme = request.getHeader(HEADER_FORWARDED_PROTO);
}
String forwardedHost = request.getHeader(HEADER_FORWARDED_HOST);
if (CommonUtils.isNotEmpty(forwardedScheme) && CommonUtils.isNotEmpty(forwardedHost)) {
origin = forwardedScheme + "://" + forwardedHost;
}
if (CommonUtils.isEmpty(origin)) {
if (log.isTraceEnabled()) {
log.trace("forwarded origin: " + origin);
}
} else if (CommonUtils.isEmpty(origin)) {
URI requestUrl = URI.create(request.getRequestURL().toString());
if (log.isTraceEnabled()) {
log.trace("Request URL: " + requestUrl);
}
origin = getRootUrlFromUri(requestUrl) + "/";
}
origin = removeSideSlashes(origin);
@@ -318,13 +339,42 @@ public class ServletAppUtils {
origin = removeSideSlashes(origin);
URI uri = URI.create(origin);
if (DEFAULT_PORTS.contains(uri.getPort())) {
int port = uri.getPort();
if (CommonUtils.isNotEmpty(request.getHeader(HEADER_FORWARDED_PORT))) {
try {
port = Integer.parseInt(request.getHeader(HEADER_FORWARDED_PORT));
} catch (NumberFormatException e) {
log.error("Failed to parse port from header: " + request.getHeader(HEADER_FORWARDED_PORT), e);
}
}
String finalScheme = uri.getScheme();
String finalHost = uri.getHost();
int finalPort = port;
boolean changed = false;
if (CommonUtils.isNotEmpty(forwardedScheme) && !forwardedScheme.equals(finalScheme)) {
finalScheme = forwardedScheme;
changed = true;
}
if (CommonUtils.isNotEmpty(forwardedHost) && !forwardedHost.equals(finalHost)) {
finalHost = forwardedHost;
changed = true;
}
if (DEFAULT_PORTS.contains(port)) {
finalPort = -1;
changed = true;
}
if (log.isTraceEnabled()) {
log.trace("Origin URI: " + origin);
}
if (changed) {
try {
origin = new URI(
uri.getScheme(),
finalScheme,
uri.getUserInfo(),
uri.getHost(),
-1,
finalHost,
finalPort,
uri.getPath(),
uri.getQuery(),
uri.getFragment()
@@ -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;
@@ -162,18 +164,24 @@ public class WebDataSourceUtils {
}
public static boolean disconnectDataSource(@NotNull WebSession webSession, @NotNull DBPDataSourceContainer dataSource) {
public static boolean disconnectDataSource(
@NotNull WebSession webSession,
@NotNull DBPDataSourceContainer dataSource,
boolean sendDisconnectEvent
) {
if (dataSource.isConnected()) {
try {
dataSource.disconnect(webSession.getProgressMonitor());
webSession.addSessionEvent(
new WSDataSourceDisconnectEvent(
dataSource.getProject().getId(),
dataSource.getId(),
webSession.getSessionId(),
webSession.getUserId()
)
);
if (sendDisconnectEvent) {
webSession.addSessionEvent(
new WSDataSourceDisconnectEvent(
dataSource.getProject().getId(),
dataSource.getId(),
WebEventUtils.getSmSessionId(webSession),
webSession.getUserId()
)
);
}
return true;
} catch (DBException e) {
log.error("Error closing connection", e);
@@ -314,6 +322,13 @@ 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));
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,119 +17,21 @@
package io.cloudbeaver.utils;
import io.cloudbeaver.model.WebAsyncTaskInfo;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.websocket.event.task.WSSessionTaskInfoEvent;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.security.SMSubjectType;
import org.jkiss.dbeaver.model.websocket.WSConstants;
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDatasourceFolderEvent;
import org.jkiss.dbeaver.model.websocket.event.permissions.WSSubjectPermissionEvent;
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceProperty;
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceUpdatedEvent;
import java.util.List;
import java.util.Objects;
public class WebEventUtils {
/**
* adds events of updated datasource to event controller
*
* @param project project of updated database
* @param session web session
* @param datasourceId id of datasource
* @param eventAction type of event
*/
public static void addDataSourceUpdatedEvent(
DBPProject project,
WebSession session,
String datasourceId,
WSConstants.EventAction eventAction,
WSDataSourceProperty property
) {
if (project == null) {
return;
}
WSEvent event = null;
switch (eventAction) {
case CREATE:
event = WSDataSourceEvent.create(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(datasourceId),
property
);
break;
case DELETE:
event = WSDataSourceEvent.delete(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(datasourceId),
property
);
break;
case UPDATE:
event = WSDataSourceEvent.update(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(datasourceId),
property
);
break;
}
if (event == null) {
return;
}
ServletAppUtils.getServletApplication().getEventController().addEvent(event);
}
public static void addNavigatorNodeUpdatedEvent(
DBPProject project,
WebSession session,
String nodePath,
WSConstants.EventAction eventAction
) {
if (project == null) {
return;
}
WSEvent event = null;
switch (eventAction) {
case CREATE:
event = WSDatasourceFolderEvent.create(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(nodePath)
);
break;
case DELETE:
event = WSDatasourceFolderEvent.delete(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(nodePath)
);
break;
case UPDATE:
event = WSDatasourceFolderEvent.update(
session.getUserContext().getSmSessionId(),
session.getUserId(),
project.getId(),
List.of(nodePath)
);
break;
}
if (event == null) {
return;
}
ServletAppUtils.getServletApplication().getEventController().addEvent(event);
}
public static void addRmResourceUpdatedEvent(
String projectId,
@@ -153,7 +55,7 @@ public class WebEventUtils {
switch (eventAction) {
case CREATE:
event = WSResourceUpdatedEvent.create(
session.getUserContext().getSmSessionId(),
WebEventUtils.getSmSessionId(session),
session.getUserId(),
projectId,
resourcePath,
@@ -163,7 +65,7 @@ public class WebEventUtils {
break;
case DELETE:
event = WSResourceUpdatedEvent.delete(
session.getUserContext().getSmSessionId(),
WebEventUtils.getSmSessionId(session),
session.getUserId(),
projectId,
resourcePath,
@@ -173,7 +75,7 @@ public class WebEventUtils {
break;
case UPDATE:
event = WSResourceUpdatedEvent.update(
session.getUserContext().getSmSessionId(),
WebEventUtils.getSmSessionId(session),
session.getUserId(),
projectId,
resourcePath,
@@ -213,4 +115,12 @@ public class WebEventUtils {
ServletAppUtils.getServletApplication().getEventController().addEvent(event);
}
@Nullable
public static String getSmSessionId(@NotNull BaseWebSession webSession) {
return webSession.getUserContext().getSmSessionId();
}
public static boolean isSmSessionIdEquals(@NotNull BaseWebSession webSession, @Nullable String smSessionId) {
return Objects.equals(getSmSessionId(webSession), smSessionId);
}
}
@@ -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);
}
@@ -0,0 +1,40 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.websocket.event.client;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.websocket.WSConstants;
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
public class WSSessionCancelActionClientEvent extends WSClientEvent {
public static final String ID = "cb_client_cancel_action";
@NotNull
private final String actionId;
public WSSessionCancelActionClientEvent(
@NotNull String actionId
) {
super(ID, WSConstants.TOPIC_SESSION_ACTION);
this.actionId = actionId;
}
@NotNull
public String getActionId() {
return actionId;
}
}
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Community Product
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
Bundle-Version: 26.0.0.qualifier
Bundle-Release-Date: 20260302
Bundle-Version: 26.1.2.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.product.ce</artifactId>
<version>26.0.0-SNAPSHOT</version>
<version>26.1.2-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Cloudbeaver Base JDBC drivers
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
Bundle-Version: 1.0.141.qualifier
Bundle-Release-Date: 20260302
Bundle-Version: 1.0.149.qualifier
Bundle-Release-Date: 20260706
Bundle-Vendor: DBeaver Corp
Bundle-ActivationPolicy: lazy
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
@@ -14,7 +14,9 @@
<resource name="drivers/jaybird"/>
<resource name="drivers/h2"/>
<resource name="drivers/h2_v2"/>
<resource name="drivers/h2_v3"/>
<resource name="drivers/sqlite/xerial"/>
<resource name="drivers/libsql"/>
<resource name="drivers/mssql/new"/>
<resource name="drivers/trino"/>
<resource name="drivers/kyuubi"/>
@@ -35,7 +37,9 @@
<bundle id="drivers.firebird" label="Firebird drivers"/>
<bundle id="drivers.h2" label="H2 drivers"/>
<bundle id="drivers.h2_v2" label="H2 v2 drivers"/>
<bundle id="drivers.h2_v3" label="H2 v3 drivers"/>
<bundle id="drivers.sqlite.xerial" label="SQLite drivers"/>
<bundle id="drivers.libsql" label="LibSQL drivers"/>
<bundle id="drivers.mssql.new" label="SQL Server drivers"/>
<bundle id="drivers.trino" label="Trino drivers"/>
<bundle id="drivers.kyuubi" label="Apache Kyuubi drivers"/>
@@ -55,6 +59,7 @@
<driver id="clickhouse:com_clickhouse"/>
<driver id="h2:h2_embedded"/>
<driver id="h2:h2_embedded_v2"/>
<driver id="h2:h2_embedded_v3"/>
<driver id="sqlite:sqlite_jdbc"/>
<driver id="sqlite:libsql_jdbc"/>
<driver id="sqlserver:microsoft"/>
@@ -9,6 +9,6 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
<version>1.0.141-SNAPSHOT</version>
<version>1.0.149-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: CloudBeaver Community
Bundle-SymbolicName: io.cloudbeaver.server.ce;singleton:=true
Bundle-Version: 26.0.0.qualifier
Bundle-Release-Date: 20260302
Bundle-Version: 26.1.2.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.server.ce</artifactId>
<version>26.0.0-SNAPSHOT</version>
<version>26.1.2-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -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");
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
*/
package io.cloudbeaver.server;
import graphql.execution.instrumentation.Instrumentation;
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.auth.NoAuthCredentialsProvider;
import io.cloudbeaver.model.CBWebServerConfig;
@@ -29,6 +30,7 @@ import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.registry.WebDriverRegistry;
import io.cloudbeaver.registry.WebServiceRegistry;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
import io.cloudbeaver.server.jetty.CBJettyServer;
import io.cloudbeaver.service.DBWServiceInitializer;
import io.cloudbeaver.service.DBWServiceServerConfigurator;
@@ -45,14 +47,14 @@ import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBConstants;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.app.DBPPlatform;
import org.jkiss.dbeaver.model.auth.AuthInfo;
import org.jkiss.dbeaver.model.auth.SMAuthConfiguration;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.auth.SMObjectType;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
import org.jkiss.dbeaver.model.security.SMAdminController;
import org.jkiss.dbeaver.model.security.SMConstants;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.websocket.event.WSEventController;
import org.jkiss.dbeaver.model.websocket.event.WSServerConfigurationChangedEvent;
import org.jkiss.dbeaver.runtime.DBWorkbench;
@@ -226,11 +228,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();
@@ -441,7 +443,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);
@@ -532,7 +534,7 @@ public abstract class CBApplication<T extends CBServerConfig>
public synchronized void finishConfiguration(
@NotNull String adminName,
@Nullable String adminPassword,
@NotNull List<AuthInfo> authInfoList,
@NotNull List<SMAuthConfiguration> authInfoList,
@NotNull CBServerConfig serverConfig,
@NotNull CBAppConfig appConfig,
@Nullable SMCredentialsProvider credentialsProvider
@@ -589,7 +591,7 @@ public abstract class CBApplication<T extends CBServerConfig>
protected abstract void finishSecurityServiceConfiguration(
@NotNull String adminName,
@Nullable String adminPassword,
@NotNull List<AuthInfo> authInfoList
@NotNull List<SMAuthConfiguration> authInfoList
) throws DBException;
public synchronized void flushConfiguration(SMCredentialsProvider webSession) throws DBException {
@@ -685,6 +687,11 @@ public abstract class CBApplication<T extends CBServerConfig>
return new CBSessionManager(this);
}
@NotNull
public GraphQLEndpoint createGraphQLEndpoint(@NotNull Instrumentation instrumentation) {
return new GraphQLEndpoint(instrumentation);
}
@NotNull
public WebDriverRegistry getDriverRegistry() {
return WebDriverRegistry.getInstance();
@@ -736,7 +743,7 @@ public abstract class CBApplication<T extends CBServerConfig>
sendConfigChangedEvent(credentialsProvider);
}
protected void sendConfigChangedEvent(SMCredentialsProvider credentialsProvider) {
protected void sendConfigChangedEvent(@Nullable SMCredentialsProvider credentialsProvider) {
String sessionId = null;
if (credentialsProvider != null && credentialsProvider.getActiveUserCredentials() != null) {
sessionId = credentialsProvider.getActiveUserCredentials().getSmSessionId();
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBFileController;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.AuthInfo;
import org.jkiss.dbeaver.model.auth.SMAuthConfiguration;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.rm.RMController;
import org.jkiss.dbeaver.model.security.SMAdminController;
@@ -75,13 +75,16 @@ public class CBApplicationCE extends CBApplication<CBServerConfig> {
);
}
@NotNull
@Override
public RMController createResourceController(@NotNull SMCredentialsProvider credentialsProvider,
@NotNull DBPWorkspace workspace) throws DBException {
return LocalResourceController.builder(credentialsProvider, workspace, this::getSecurityController).build();
public RMController createResourceController(
@NotNull SMCredentialsProvider credentialsProvider,
@NotNull DBPWorkspace workspace
) throws DBException {
var lockManager = createLockManager();
return LocalResourceController
.builder(credentialsProvider, workspace, lockManager, this::getSecurityController)
.build();
}
@NotNull
@@ -98,7 +101,7 @@ public class CBApplicationCE extends CBApplication<CBServerConfig> {
protected void finishSecurityServiceConfiguration(
@NotNull String adminName,
@Nullable String adminPassword,
@NotNull List<AuthInfo> authInfoList
@NotNull List<SMAuthConfiguration> authInfoList
) throws DBException {
if (securityController instanceof CBEmbeddedSecurityController<?> embeddedSecurityController) {
embeddedSecurityController.finishConfiguration(adminName, adminPassword, authInfoList);
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import io.cloudbeaver.server.jobs.SessionStateJob;
import io.cloudbeaver.server.jobs.WebDataSourceMonitorJob;
import io.cloudbeaver.server.jobs.WebSessionMonitorJob;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.app.DBPRegistryListener;
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
@@ -51,7 +52,7 @@ public class CBPlatform extends BaseWebPlatform {
}
@Override
protected synchronized void initialize() {
protected synchronized void initialize() throws DBException {
long startTime = System.currentTimeMillis();
log.info("Initialize web platform " + getClass().getSimpleName());
this.preferenceStore = new WebServerPreferenceStore(WebPlatformActivator.getInstance().getPreferences());
@@ -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;
@@ -508,7 +505,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 +630,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
) {
}
@@ -24,6 +24,7 @@ import io.cloudbeaver.server.WebAppUtils;
import io.cloudbeaver.service.security.SMUtils;
import io.cloudbeaver.utils.ServletAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.security.SMAdminController;
@@ -33,7 +34,6 @@ import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.model.websocket.event.permissions.WSObjectPermissionEvent;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -43,10 +43,11 @@ import java.util.stream.Collectors;
public class WSObjectPermissionUpdatedEventHandler extends WSDefaultEventHandler<WSObjectPermissionEvent> {
private static final Log log = Log.getLog(WSObjectPermissionUpdatedEventHandler.class);
@Nullable
@Override
public void handleEvent(@NotNull WSObjectPermissionEvent event) {
protected Consumer<BaseWebSession> getEventConsumer(@NotNull WSObjectPermissionEvent event) {
String objectId = event.getObjectId();
Consumer<BaseWebSession> runnable = switch (event.getSmObjectType()) {
return switch (event.getSmObjectType()) {
case project:
yield getUpdateUserProjectsInfoConsumer(event, objectId);
case datasource:
@@ -61,23 +62,6 @@ public class WSObjectPermissionUpdatedEventHandler extends WSDefaultEventHandler
yield null;
}
};
if (runnable == null) {
return;
}
log.debug(event.getTopicId() + " event handled");
Collection<BaseWebSession> allSessions = CBApplication.getInstance().getSessionManager().getAllActiveSessions();
for (var activeUserSession : allSessions) {
if (!isAcceptableInSession(activeUserSession, event)) {
log.debug("Cannot handle %s event '%s' in session %s".formatted(
event.getTopicId(),
event.getId(),
activeUserSession.getSessionId()
));
continue;
}
log.debug("%s event '%s' handled".formatted(event.getTopicId(), event.getId()));
runnable.accept(activeUserSession);
}
}
@NotNull
@@ -24,6 +24,7 @@ import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.filters.ServerConfigurationTimeLimitFilter;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
import io.cloudbeaver.server.servlets.CBImageServlet;
import io.cloudbeaver.server.servlets.CBManifestServlet;
import io.cloudbeaver.server.servlets.CBStaticServlet;
import io.cloudbeaver.server.servlets.WebStatusServlet;
import io.cloudbeaver.server.websockets.CBEventsLongPollingServlet;
@@ -119,12 +120,15 @@ public class CBJettyServer {
ServletHolder imagesServletHolder = new ServletHolder("images", new CBImageServlet());
servletContextHandler.addServlet(imagesServletHolder, serverConfiguration.getServicesURI() + "images/*");
ServletHolder manifestServletHolder = new ServletHolder("manifest", new CBManifestServlet(contentRootPath));
servletContextHandler.addServlet(manifestServletHolder, "/manifest.webmanifest");
servletContextHandler.addServlet(new ServletHolder("status", new WebStatusServlet()), "/status");
ServletHolder eventsServletHolder = new ServletHolder("events", new CBEventsLongPollingServlet());
servletContextHandler.addServlet(eventsServletHolder, serverConfiguration.getServicesURI() + "events/*");
GraphQLEndpoint endpoint = new GraphQLEndpoint(new ServerConfigurationTimeLimitFilter(application));
GraphQLEndpoint endpoint = application.createGraphQLEndpoint(new ServerConfigurationTimeLimitFilter(application));
application.addApplicationContextValue(GraphQL.class.getName(), endpoint.getGraphQL());
String gqlServletPath = serverConfiguration.getServicesURI() + "gql/*";
servletContextHandler.addServlet(
@@ -257,4 +261,4 @@ public class CBJettyServer {
cbSessionHandler.setSecureCookies(application.getServerConfiguration().isForceHttps());
}
}
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class RequestHostFilter implements Filter {
private static final Log log = Log.getLog(RequestHostFilter.class);
@@ -74,9 +75,19 @@ public class RequestHostFilter implements Filter {
chain.doFilter(request, response);
return;
}
boolean isIpAddress = InetAddresses.isInetAddress(originUri.getHost());
if (CommonUtils.isNotEmpty(originUri.getHost())) {
requestAllowed = InetAddresses.isInetAddress(originUri.getHost());
} else {
log.debug(
"Request origin host is null, request URI - " + originUri +
", request path - " + httpRequest.getServletPath() +
", request url - " + httpRequest.getRequestURL()
);
}
String servletPath = httpRequest.getServletPath();
requestAllowed = isIpAddress;
if (!requestAllowed) {
if (CommonUtils.isNotEmpty(servletPath)) {
for (String excludedPath : excludedPaths) {
@@ -145,10 +156,10 @@ public class RequestHostFilter implements Filter {
if (!availableHosts.contains(requestHost)) {
for (String errorPath : errorPaths) {
if (httpRequest.getServletPath().contains(errorPath)) {
log.warn("Request host '" + requestHost + "' is not allowed. Available hosts: " + availableHosts);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write(
"Request host '" + requestHost + "' is not allowed. Available hosts: " + availableHosts
);
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().write("Request host is not allowed. Available hosts: " + availableHosts);
return false;
}
}
@@ -0,0 +1,119 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.server.servlets;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.server.CBApplication;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpHeader;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.utils.MimeTypes;
import org.jkiss.utils.IOUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@WebServlet(urlPatterns = "/manifest.webmanifest")
public class CBManifestServlet extends HttpServlet {
private static final Log log = Log.getLog(CBManifestServlet.class);
private static final String MANIFEST_FILE = "manifest.webmanifest";
// Fix: Create Gson instance with HTML escaping disabled to prevent \u003d escaping
private static final Gson GSON = new GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping() // This prevents escaping = to \u003d
.create();
@NotNull
private final Path contentRoot;
public CBManifestServlet(@NotNull Path contentRoot) {
this.contentRoot = contentRoot;
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Path manifestPath = contentRoot.resolve(MANIFEST_FILE).normalize();
if (!manifestPath.startsWith(contentRoot) || !Files.exists(manifestPath)) {
log.error("Manifest file not found or path is invalid: " + manifestPath);
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
try {
// Read the original manifest file
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (InputStream fis = Files.newInputStream(manifestPath)) {
IOUtils.copyStream(fis, baos);
}
String manifestContent = baos.toString(StandardCharsets.UTF_8);
// Parse the JSON and update dynamic fields
CBServerConfig serverConfig = CBApplication.getInstance().getServerConfiguration();
String rootURI = serverConfig.getRootURI();
// Ensure rootURI ends with / for proper scope and path handling
String normalizedRootURI = rootURI;
if (!normalizedRootURI.endsWith("/")) {
normalizedRootURI += "/";
}
// Use JsonParser to parse the existing manifest
JsonObject manifest = JsonParser.parseString(manifestContent).getAsJsonObject();
// Update scope to match the rootURI
manifest.addProperty("scope", normalizedRootURI);
// Update start_url and id to match the rootURI
String startUrl = normalizedRootURI + "?source=pwa";
manifest.addProperty("start_url", startUrl);
manifest.addProperty("id", startUrl);
// Convert back to JSON string using the Gson instance with HTML escaping disabled
String updatedManifest = GSON.toJson(manifest);
byte[] manifestBytes = updatedManifest.getBytes(StandardCharsets.UTF_8);
// Set appropriate headers
response.setHeader(HttpHeader.CONTENT_TYPE.toString(), "application/manifest+json");
response.setHeader(HttpHeader.CACHE_CONTROL.toString(), "no-cache, no-store, must-revalidate");
response.setHeader(HttpHeader.EXPIRES.toString(), "0");
response.setContentLength(manifestBytes.length);
// Write the response
response.getOutputStream().write(manifestBytes);
} catch (Exception e) {
log.error("Error processing manifest file", e);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,9 +24,9 @@ import io.cloudbeaver.registry.WebSessionHandlerDescriptor;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.WebAppSessionManager;
import io.cloudbeaver.server.events.WSWebUtils;
import io.cloudbeaver.service.DBWSessionHandler;
import io.cloudbeaver.utils.ServletAppUtils;
import io.cloudbeaver.utils.WebEventUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
@@ -51,7 +51,7 @@ public class CBSessionManager implements WebAppSessionManager {
private static final Log log = Log.getLog(CBSessionManager.class);
private final CBApplication<?> application;
private final Map<String, BaseWebSession> sessionMap = new HashMap<>();
protected final Map<String, BaseWebSession> sessionMap = new HashMap<>();
public CBSessionManager(CBApplication<?> application) {
this.application = application;
@@ -60,6 +60,7 @@ public class CBSessionManager implements WebAppSessionManager {
/**
* Closes Web Session, associated to HttpSession from {@code request}
*/
@Nullable
@Override
public BaseWebSession closeSession(@NotNull HttpServletRequest request) {
HttpSession session = request.getSession();
@@ -69,11 +70,13 @@ public class CBSessionManager implements WebAppSessionManager {
return null;
}
@Nullable
@Override
public BaseWebSession closeSession(@NotNull String sessionId) {
return closeSession(sessionId, true);
}
@Nullable
@Override
public BaseWebSession closeSession(@NotNull String sessionId, boolean sendSessionExpiredEvent) {
BaseWebSession webSession;
@@ -199,12 +202,49 @@ 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.
*
* @return WebSession object or null, if session expired or invalid
*/
@Nullable
@NotNull
public WebSession getOrRestoreWebSession(@NotNull WebHttpRequestInfo requestInfo) {
final var sessionId = requestInfo.getId();
if (sessionId == null) {
@@ -283,19 +323,19 @@ public class CBSessionManager implements WebAppSessionManager {
@Override
@Nullable
public WebSession findWebSession(HttpServletRequest request) {
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
@Override
public WebSession findWebSession(HttpServletRequest request, boolean errorOnNoFound) throws DBWebException {
public WebSession findWebSession(@NotNull HttpServletRequest request, boolean errorOnNoFound) throws DBWebException {
WebSession webSession = findWebSession(request);
if (webSession != null) {
return webSession;
@@ -323,6 +363,7 @@ public class CBSessionManager implements WebAppSessionManager {
}
}
@NotNull
@Override
public Collection<BaseWebSession> getAllActiveSessions() {
synchronized (sessionMap) {
@@ -379,6 +420,63 @@ public class CBSessionManager implements WebAppSessionManager {
}
}
@Nullable
public WebSession getWebSession(
@Nullable String smAccessToken,
@NotNull WebHttpRequestInfo requestInfo,
boolean create
) throws DBException {
if (CommonUtils.isEmpty(smAccessToken)) {
return null;
}
synchronized (sessionMap) {
var tempCredProvider = new SMTokenCredentialProvider(smAccessToken);
SMAuthPermissions authPermissions = application.createSecurityController(tempCredProvider).getTokenPermissions();
var sessionId = requestInfo.getId() != null ? requestInfo.getId()
: authPermissions.getSessionId();
var existSession = sessionMap.get(sessionId);
if (existSession instanceof WebSession webSession) {
var creds = webSession.getUserContext().getActiveUserCredentials();
if (creds == null || !smAccessToken.equals(creds.getSmAccessToken())) {
if (webSession.getUserContext().refresh(
smAccessToken,
null,
authPermissions
)) {
webSession.refreshUserData();
}
}
return webSession;
}
if (existSession != null) {
//session exist but it not web session
return null;
}
if (!create) {
return null;
}
if (requestInfo.getId() == null) {
requestInfo = new WebHttpRequestInfo(
sessionId,
requestInfo.getLocale(),
requestInfo.getLastRemoteAddress(),
requestInfo.getLastRemoteUserAgent()
);
}
var webSession = createWebSessionImpl(requestInfo);
webSession.getUserContext().refresh(
smAccessToken,
null,
authPermissions
);
webSession.refreshUserData();
sessionMap.put(sessionId, webSession);
return webSession;
}
}
/**
* Send session state with remaining alive time to all cached session
*/
@@ -444,7 +542,7 @@ public class CBSessionManager implements WebAppSessionManager {
for (Iterator<BaseWebSession> iterator = sessionMap.values().iterator(); iterator.hasNext(); ) {
var session = iterator.next();
iterator.remove();
session.close(false, !WSWebUtils.isSessionIdEquals(session, initiatorSessionId));
session.close(false, !WebEventUtils.isSmSessionIdEquals(session, initiatorSessionId));
}
}
}
@@ -470,4 +568,5 @@ public class CBSessionManager implements WebAppSessionManager {
log.debug("> Expire session '" + session.getSessionId() + "'");
session.close();
}
}
@@ -3,17 +3,13 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Server
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
Bundle-Version: 26.0.0.qualifier
Bundle-Release-Date: 20260302
Bundle-Version: 26.1.2.qualifier
Bundle-Release-Date: 20260706
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
org.apache.commons.jexl,
org.jkiss.utils;visibility:=reexport,
org.jkiss.dbeaver.model.sql;visibility:=reexport,
Require-Bundle: org.jkiss.dbeaver.model.sql;visibility:=reexport,
org.jkiss.dbeaver.data.gis,
org.jkiss.bundle.jetty.server;visibility:=reexport,
com.google.gson;visibility:=reexport,
org.jkiss.bundle.graphql;visibility:=reexport,
org.jkiss.bundle.apache.dbcp,
org.jkiss.dbeaver.net.ssh,
@@ -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.0.0-SNAPSHOT</version>
<version>26.1.2-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -565,9 +565,13 @@ type ConnectionInfo {
requiredAuth: String
defaultCatalogName: String @since(version: "25.0.5")
defaultSchemaName: String @since(version: "25.0.5")
"Default user settings for connection"
defaultUserPreferences: Object @since(version: "26.0.2")
"List of tools that can be used with this connection. Returns empty list if no tools are available"
tools: [String!]! @since(version: "24.1.3")
connectionType: ID! @since(version: "26.1.2")
}
type ConnectionFolderInfo {
@@ -631,6 +635,37 @@ 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!
}
####################################################
# Input types
####################################################
@@ -741,6 +776,10 @@ input ConnectionConfig {
defaultCatalogName: String @since(version: "25.0.5") @deprecated(reason: "25.2.1 use expertPropertyValues instead")
"Sets schema name for the connection"
defaultSchemaName: String @since(version: "25.0.5") @deprecated(reason: "25.2.1 use expertPropertyValues instead")
"Default user settings for connection"
defaultUserPreferences: Object @since(version: "26.0.2")
connectionType: ID @since(version: "26.1.2")
}
####################################################
@@ -783,6 +822,11 @@ extend type Query {
"Reads session log entries"
readSessionLog(maxEntries: Int, clearEntries: Boolean): [ LogEntry! ]!
"Returns connection driver properties based on configuration"
listConnectionDriverProperties( projectId: ID!, config: ConnectionConfig! ): [ObjectPropertyInfo!]!
listConnectionTypes: [ConnectionType!]! @since(version: "26.1.2")
}
extend type Mutation {
@@ -825,7 +869,7 @@ extend type Mutation {
testConnection( config: ConnectionConfig!, projectId: ID): ConnectionInfo!
"Test network handler connectivity"
testNetworkHandler( config: NetworkHandlerConfigInput! ): NetworkEndpointInfo!
testNetworkHandler(projectId: ID, connectionId: ID, config: NetworkHandlerConfigInput! ): NetworkEndpointInfo!
"Initiate database connection. User credentials may be (optionally) override default settings."
initConnection(
@@ -846,6 +890,7 @@ extend type Mutation {
"Sets to default navigator settings for connection. Resets all user navigator settings for this connection."
clearConnectionNavigatorSettings(projectId: ID!, id: ID!): ConnectionInfo! @since(version: "25.3.2")
setObjectSettingsForDatasource(id: ID!, projectId:ID, settings: Object!): Object! @since(version: "25.3.5")
#### Generic async functions
"Cancel async task by ID"
@@ -0,0 +1,62 @@
####################################################
extend enum CBServerEventId {
"Datasource created"
cb_datasource_created,
"Datasource updated"
cb_datasource_updated,
"Datasource deleted"
cb_datasource_deleted,
"Datasource folder created"
cb_datasource_folder_created,
"Datasource folder updated"
cb_datasource_folder_updated,
"Datasource folder deleted"
cb_datasource_folder_deleted,
"Datasource disconnected"
cb_datasource_disconnected,
"Datasource connected"
cb_datasource_connected
}
extend enum CBEventTopic {
cb_datasource,
cb_datasource_folder,
cb_datasource_connection
}
"Datasource folder event"
type CBDatasourceFolderEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
nodePaths: [String!]! # list of folders node ids
projectId: String!
}
"Datasource event"
type CBDatasourceEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
dataSourceIds: [String!]! # list of datasources ids
projectId: String!
}
"Datasource disconnect event"
type WSDataSourceDisconnectEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
connectionId: String!
projectId: String!
timestamp: Int!
}
"Datasource connect event"
type WSDataSourceConnectEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
connectionId: String!
projectId: String!
timestamp: Int!
}
@@ -17,52 +17,11 @@ enum CBServerEventId {
"Session expired"
cb_session_expired,
"Datasource created"
cb_datasource_created,
"Datasource updated"
cb_datasource_updated,
"Datasource deleted"
cb_datasource_deleted,
"Datasource folder created"
cb_datasource_folder_created,
"Datasource folder updated"
cb_datasource_folder_updated,
"Datasource folder deleted"
cb_datasource_folder_deleted,
"Datasource disconnected"
cb_datasource_disconnected,
"Datasource connected"
cb_datasource_connected,
"Resource manager resource created"
cb_rm_resource_created,
"Resource manager resource updated"
cb_rm_resource_updated,
"Resource manager resource deleted"
cb_rm_resource_deleted,
"Resource manager project added"
cb_rm_project_added,
"Resource manager project added"
cb_rm_project_updated,
"Resource manager project removed"
cb_rm_project_removed,
"Object permissions updated (e.g., datasource)"
cb_object_permissions_updated,
"Subject permissions updated (e.g., user, team)"
cb_subject_permissions_updated,
"Database output log updated (e.g., DBMS in Oracle)"
cb_database_output_log_updated,
cb_ai_chat_message_chunk @since(version: "25.1.1")
cb_ai_chat_message_error @since(version: "25.1.1")
"Transaction count updated"
cb_transaction_count @since(version: "24.3.3"),
"Session async task info updated"
cb_session_task_info_updated @since(version: "24.3.1"),
"Workspace configuration updated"
@@ -70,11 +29,6 @@ enum CBServerEventId {
"Server notification"
cb_server_notification@since(version: "25.2.1")
"Session task confirmations"
cb_session_task_confirmation_request @since(version: "25.2.2")
cb_session_task_query_confirmation_request @since(version: "25.2.2")
cb_session_task_query_params_confirmation_request @since(version: "25.2.5")
"Open URL request in client"
cb_open_url @since(version: "25.3.1")
}
@@ -85,8 +39,7 @@ enum CBClientEventId {
cb_client_topic_unsubscribe,
cb_client_projects_active,
cb_client_session_ping,
cb_client_session_task_confirmation @since(version: "25.2.2")
cb_client_session_task_with_parameters_confirmation @since(version: "25.2.5")
cb_client_cancel_action @since(version: "26.0.2")
}
"Client subscribes on topic to receive only related events"
@@ -94,22 +47,14 @@ enum CBEventTopic {
cb_config,
cb_session_log,
cb_session,
cb_datasource,
cb_datasource_folder,
cb_scripts,
cb_projects,
cb_object_permissions,
cb_subject_permissions,
cb_database_output_log,
cb_session_task, @since(version: "24.3.1")
cb_datasource_connection,
cb_delete_temp_folder,
cb_transaction @since(version: "24.3.3"),
cb_workspace_configuration @since(version: "25.1.1")
cb_ai @since(version: "25.1.1")
cb_notification @since(version: "25.2.1")
cb_session_action @since(version: "25.3.1")
}
@@ -126,30 +71,6 @@ interface CBClientEvent {
topicId: CBEventTopic
}
"Datasource folder event"
type CBDatasourceFolderEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
nodePaths: [String!]! # list of folders node ids
projectId: String!
}
"Datasource event"
type CBDatasourceEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
dataSourceIds: [String!]! # list of datasources ids
projectId: String!
}
"Resource manager event"
type CBRMEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
resourcePath: String!
projectId: String!
}
"Server Config event"
type CBConfigEvent implements CBServerEvent {
id: CBServerEventId!
@@ -187,12 +108,6 @@ type WSSessionExpiredEvent implements CBServerEvent {
topicId: CBEventTopic
}
"RM project update event"
type CBProjectUpdateEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
projectId: String!
}
"Subject permission update event"
type CBSubjectPermissionUpdateEvent implements CBServerEvent {
@@ -222,20 +137,11 @@ type CBProjectsActiveEvent implements CBClientEvent {
projectIds: [String!]!
}
"Database output log event"
type CBDatabaseOutputLogEvent implements CBServerEvent {
id: CBServerEventId!
"Action cancelled by user"
type CBActionCancelledEvent implements CBClientEvent @since(version: "26.0.2") {
id: CBClientEventId!
topicId: CBEventTopic
contextId: String!
messages: [WSOutputLogInfo!]!
eventTimestamp: Int!
}
"Define the type for WSOutputLogInfo"
type WSOutputLogInfo {
severity: String
message: String
# Add more fields if needed
actionId: String!
}
"Async task info status event"
@@ -246,50 +152,6 @@ type WSAsyncTaskInfo @since(version: "24.3.1") {
running: Boolean!
}
"Datasource disconnect event"
type WSDataSourceDisconnectEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
connectionId: String!
projectId: String!
timestamp: Int!
}
"Datasource connect event"
type WSDataSourceConnectEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
connectionId: String!
projectId: String!
timestamp: Int!
}
"Datasource count event in transactional mode"
type WSTransactionalCountEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
contextId: String!
projectId: String!
connectionId: String!
transactionalCount: Int!
}
type WSAIChatMessageChunkEvent implements CBServerEvent @since(version: "25.1.1") {
id: CBServerEventId!
topicId: CBEventTopic
conversationId: ID!
messageId: ID!
chunk: String
completed: Boolean!
}
type WSAIChatMessageErrorEvent implements CBServerEvent @since(version: "25.1.1") {
id: CBServerEventId!
topicId: CBEventTopic
conversationId: ID!
messageId: ID!
errorMessage: String
}
enum WSServerNotificationEventType {
INFO
ERROR
@@ -297,12 +159,6 @@ enum WSServerNotificationEventType {
CUSTOM
}
"Query parameter information"
type WSQueryParameterInfo {
name: String!
value: String
}
"General event for showing user notification"
type WSServerNotificationEvent implements CBServerEvent @since(version: "25.2.1") {
id: CBServerEventId!
@@ -312,54 +168,14 @@ type WSServerNotificationEvent implements CBServerEvent @since(version: "25.2.1"
notificationType: WSServerNotificationEventType!
}
#TODO: move out
"Event for opening URL in client application"
type WSOpenUrlEvent implements CBServerEvent @since(version: "25.3.1") {
id: CBServerEventId!
topicId: CBEventTopic!
timestamp: Int!
url: String!
}
type WSSessionTaskConfirmationRequestEvent implements CBServerEvent @since(version: "25.2.2") {
id: CBServerEventId!
topicId: CBEventTopic!
taskId: ID!
title: String!
message: String!
}
type WSSessionTaskQueryConfirmationRequestEvent implements CBServerEvent @since(version: "25.2.2") {
id: CBServerEventId!
topicId: CBEventTopic!
taskId: ID!
title: String!
message: String!
query: String!
}
type WSSessionTaskConfirmationEvent implements CBClientEvent @since(version: "25.2.2") {
id: CBClientEventId!
topicId: CBEventTopic
taskId: ID!
confirmed: Boolean!
skipConfirmations: Boolean!
}
type WSSessionTaskQueryParamsConfirmationEvent implements CBServerEvent @since(version: "26.0.0") {
id: CBServerEventId!
topicId: CBEventTopic!
taskId: ID!
title: String!
message: String!
query: String!
parameters: [WSQueryParameterInfo]!
}
type WSSessionTaskWithParametersConfirmationEvent implements CBClientEvent @since(version: "25.2.5") {
id: CBClientEventId!
topicId: CBEventTopic
taskId: ID!
parameters: Object!
actionId: String!
}
extend type Query {
@@ -0,0 +1,97 @@
extend enum CBServerEventId {
"Database output log updated (e.g., DBMS in Oracle)"
cb_database_output_log_updated,
"Transaction count updated"
cb_transaction_count @since(version: "24.3.3"),
"Session task confirmations"
cb_session_task_confirmation_request @since(version: "25.2.2")
cb_session_task_query_confirmation_request @since(version: "25.2.2")
cb_session_task_query_params_confirmation_request @since(version: "25.2.5")
}
extend enum CBClientEventId {
cb_client_session_task_confirmation @since(version: "25.2.2")
cb_client_session_task_with_parameters_confirmation @since(version: "25.2.5")
}
extend enum CBEventTopic {
cb_database_output_log,
cb_transaction @since(version: "24.3.3")
}
"Database output log event"
type CBDatabaseOutputLogEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
contextId: String!
messages: [WSOutputLogInfo!]!
eventTimestamp: Int!
}
"Define the type for WSOutputLogInfo"
type WSOutputLogInfo {
severity: String
message: String
# Add more fields if needed
}
"Datasource count event in transactional mode"
type WSTransactionalCountEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
contextId: String!
projectId: String!
connectionId: String!
transactionalCount: Int!
}
"Query parameter information"
type WSQueryParameterInfo {
name: String!
value: String
}
type WSSessionTaskConfirmationRequestEvent implements CBServerEvent @since(version: "25.2.2") {
id: CBServerEventId!
topicId: CBEventTopic!
taskId: ID!
title: String!
message: String!
}
type WSSessionTaskQueryConfirmationRequestEvent implements CBServerEvent @since(version: "25.2.2") {
id: CBServerEventId!
topicId: CBEventTopic!
taskId: ID!
title: String!
message: String!
query: String!
}
type WSSessionTaskConfirmationEvent implements CBClientEvent @since(version: "25.2.2") {
id: CBClientEventId!
topicId: CBEventTopic
taskId: ID!
confirmed: Boolean!
skipConfirmations: Boolean!
}
type WSSessionTaskQueryParamsConfirmationEvent implements CBServerEvent @since(version: "26.0.0") {
id: CBServerEventId!
topicId: CBEventTopic!
taskId: ID!
title: String!
message: String!
query: String!
parameters: [WSQueryParameterInfo]!
}
type WSSessionTaskWithParametersConfirmationEvent implements CBClientEvent @since(version: "25.2.5") {
id: CBClientEventId!
topicId: CBEventTopic
taskId: ID!
parameters: Object!
}

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