Compare commits

...
Author SHA1 Message Date
Daria Marutkina 6b8425ecf9 Merge branch 'devel' into dbeaver/pro#6324-cursor-sync 2025-08-06 13:41:07 +02:00
sergeyteleshevandDaria Marutkina 00b214b8bc [CB] Rename the Enable HTTPs mode on server config (#3631)
* dbeaver/pro#6469 corrects enable https mode translation and switch position in server config form

* adds link for proxy configuration article

* fixes punctuation for ru locale

* updates description translations

* removes extra dot in ru locale

* adds advanced documentation link

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2025-08-06 16:12:04 +08:00
Sychev AndreyandEvgenia ea6c960353 dbeaver/pro#6444 refactor: fix Radio component (#3632)
removes RadioGroupContext(radio group/radio use their own provider)
removes all the logic of state changing from Radio component since it's creating a wrong understanding of how these components should be used together with RadioGroup. Radio still can be controllable, but it should be used in most scenarios only with RadioGroup which is controlling the state.
simplify styles by removing extra class

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2025-08-06 16:11:13 +08:00
Viktor Kirst 44c0cfe856 Dbeaver/pro#2951 Show message about disabling user (#3624)
* dbeaver/pro#2951 Emit WSUserDisabledEvent after disabling a user

* dbeaver/pro#2951 Close user sessions on WSUserDisabledEvent

* dbeaver/pro#2951 Unify session termination logic for deleted and disabled users
2025-08-05 18:32:27 +02:00
naumov a4c1c6e174 dbeaver/pro#6324 sync view with selection 2025-08-05 18:24:21 +02:00
Ainur 470d64718b dbeaver/pro#6214 invert feature and enable by default (#3615) 2025-08-05 13:43:10 +00:00
Evgenia 0c0669f0c7 dbeaver/pro#6457 Update README file (#3630) 2025-08-04 19:43:21 +02:00
2a012d600c dbeaver/pro#6382 feat: add bind session to ip combobox in server config (#3626)
* dbeaver/pro#6382 feat: add bind session to ip combobox in server config

* dbeaver/pro#6382 use select for bindSessionToIP setting instead of combobox

* dbeaver/pro#6382 bind session ip to dbeaver's hosts

* dbeaver/pro#6382 feat: add placeholder for bindToIp setting

* dbeaver/pro#6382 fix: defaultServerConfig

* dbeaver/pro#6382 refactor: remove l10n from CE

* dbeaver/pro#6382 debug log

* dbeaver/pro#6382 remove debug logs

---------

Co-authored-by: Sychev Andrey <nukemoore@gmail.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2025-08-04 16:37:56 +02:00
Alexander Skoblikovandkseniaguzeeva 0b2370d414 dbeaver/pro#6467 ignore default ports (#3622)
* dbeaver/pro#6467 ignore default ports

* dbeaver/pro#6467 check port

* dbeaver/pro#6467 fix ip host parsing

* dbeaver/pro#6467 debug log

* dbeaver/pro#6467 remove debug logs

---------

Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2025-08-04 13:47:18 +02:00
sergeyteleshev a0def9f3ae [CB] write unit tests (#3621)
* dbeaver/pro#6436 initializes unit tests for common-react

* installs common-react in the scripts

* adds @dbeaver/react-tests

* setups @dbeaver/react-test into tests-runner

* implements react-testing library into @dbeaver/react-tests

* removes unused deps

* adds test for frontend build github action
2025-08-04 18:45:22 +08:00
alex 13cf2911e0 dbeaver/pro#6470 remove pre commit hook dep from deps list (#3623) 2025-07-30 21:55:41 +08:00
Serge Rider c0c3594633 dbeaver/pro#6457 25.1.5 version bump 2025-07-29 21:41:44 +03:00
180ec65dd9 dbeaver/pro#5552 new gql api (#3579)
* dbeaver/pro#5552 new gql api

* dbeaver/pro#5552 fixes

* adds available hosts fields

* adds warnings for the https mode

* adds default host for domain manager + fixes some translations

* reverts server URL for domain manager

* fixes description for available hosts

* separates available hosts by new line in textarea

* removes dots

* removes http, https from examples for available hosts field

* dbeaver/pro#5552 validate request's host

* dbeaver/pro#5552 review fixes

* dbeaver/pro#5552 update default configs

* refactors availableHosts -> supportedHosts

* review fix

* dbeaver/pro#5552 fix build

* refactor: supportedHosts - array -> string

* cleanup

* reverts serverConfigurationInfoForm interfaces

* refactors secureCookies -> forceHttps (frontend)

* dbeaver/pro#5552 rename secureCookies param to forceHttps

* dbeaver/pro#5552 rename secureCookies param to forceHttps

* dbeaver/pro#5552 rename secureCookies param to forceHttps

* dbeaver/pro#5552 enable https for old deployments

* fixes translations, readonly state & hosts examples for TE

* dbeaver/pro#5552 fixes

* dbeaver/pro#5552 redirect from http to https

* dbeaver/pro#5552 redirect to https

* dbeaver/pro#5886 use scheme from origin

* dbeaver/pro#5552 exclude special servlets

* dbeaver/pro#5552 saml test

* dbeaver/pro#5552 remove slashes from hostname

---------

Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2025-07-29 19:04:14 +02:00
79d5702744 dbeaver/pro#5957 save lock state in sm controller (#3576)
* dbeaver/pro#5957 save lock state in sm controller

* dbeaver/pro#5957 fix read config

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2025-07-29 18:10:32 +02:00
603b94c551 dbeaver/pro#5241 [CB] Screen reader does not read some fields in the application (#3585)
* dbeaver/pro#5241 fix: input doesn't create label wrapper if label is not provided

* dbeaver/pro#5241 fix: link label to input inside InputFieldBase

* dbeaver/pro#5241 fix: make required field asterisk invisible for assistive technologies

* dbeaver/pro#5241 fix: link FieldLabel to textarea with generated id and add aria-label for accessibility

even with linked label, voiceOver doesn't pronounce label when focusing textarea, that's why aria-label is used there

* dbeaver/pro#5241 refactor: force aria-label for input field

* dbeaver/pro#5241 refactor: change role for snackbar

aria-live polite works with aria regions which initially empty and then dynamically populated with some data. If we want assistive technologies read initial content, we need to use alert role.

also hide icon from assistive technologies (no additional information from that)

* dbeaver/pro#5241 refactor: remove aria-label from input

use native htmlFor

* dbeaver/pro#5241 refactor: remove aria-label from Textarea component

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2025-07-29 23:09:05 +08:00
Alexey PotsetsuevandEvgenia ad6973d069 dbeaver/pro#6337 fix: sql editor dnd insertion (#3620)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2025-07-29 19:53:03 +08:00
sergeyteleshevandmr-anton-t 231da5b0b8 6056-cb-impelement-ui-kit-select-in-combobox (#3557)
* dbeaver/pro#6056 adds boilerplate for new combobox ui kit

* removes unused code

* refactors layout

* fixes theme styles

* fixes styles

* fixes search value

* implements ui kit store instead of menu

* reverts search value changes

* fixes search

* cleanup

* adds ellipsis on select items

* selectField supports required for form validations

* adds types for functions

* adds disabled styles

* removes hover border-color for disabled select

* removes popoverProps

* moves some styles from kit select to combobox + fixes some styles

* fixes for QA

* adds custom paddings as it was before refactoring

* fixes item styles, placeholder, title errors

* fixes focus styles for input

* fixes expanded state focus

* splits to Select and Combobox

* removes unused code for select

* pr fixes

* replaces arrow icon + opens select on label click

* fixes click on label

* Revert "fixes click on label"

This reverts commit 772c988b28.

* reverts select label

* removes 1/3 width for preferences

* removes border radius from top of the navigation tab

* fixes selection element

* removes double select border

* qa fixes

* fixes border-radius and outline-offset for select

* fixes outline

* fixes colors

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2025-07-29 19:52:34 +08:00
Alexander Skoblikovandkseniaguzeeva 57e77b7cd1 dbeaver/pro#6303 do not remove default user team (#3609)
* dbeaver/pro#6303 do not remove default user team

* dbeaver/pro#6303 throw error

---------

Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2025-07-29 13:17:09 +02:00
Alexey Potsetsuev 58c4744aa7 dbeaver/pro#6417 chore: update deps (#3616) 2025-07-28 22:49:30 +08:00
Sychev AndreyandEvgenia 39c7346e0a dbeaver/pro#5720 feat: add Script button for tables w/o identifier (#3608)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2025-07-28 22:46:51 +08:00
131b1e13f4 dbeaver/pro#6109 add void type (#3603)
Co-authored-by: Ainur <ainur.iagudin@dbeaver.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2025-07-27 15:17:41 +02:00
Sychev AndreyandDaria Marutkina 3914461f55 dbeaver/pro#5161 fix: show confirmation message if there is unsaved changes in teams form (#3605)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2025-07-25 15:30:37 +02:00
Sychev Andrey 5dc6e5ca38 dbeaver/cloudbeaver#3569 fix: don't show new connection button (#3604) 2025-07-25 15:27:52 +02:00
alex c747c30063 dbeaver/pro#6399 do not check executable feature (#3610) 2025-07-25 14:54:31 +02:00
ad225404f4 dbeaver/pro#6124 Add audit log cleanup job (#3591)
* dbeaver/pro#6124 Add audit log cleanup job

* dbeaver/pro#6124 fixes

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
2025-07-24 17:47:12 +02:00
sergeyteleshev d9fe873bb5 dbeaver/pro#6383 reverts stylus stub (#3606) 2025-07-24 12:08:09 +02:00
sergeyteleshev 0193e67f72 dbeaver/pro#6372 adds stub for stylus lib (#3601) 2025-07-23 15:44:17 +02:00
8ebf7eba9c dbeaver/pro#5886 core api for permission management (#3519)
* dbeaver/pro#5886 new permissions api

* dbeaver/pro#5886 new permissions api

* dbeaver/pro#5886 test + forgotten commit

* dbeaver/pro#5886 test + forgotten commit, review fixes

* dbeaver/#5886 test + forgotten commits

* dbeaver/pro#5886 clean up CE repos

* dbeaver/pro#5886 gql api for global perms

* dbeaver/pro#5886 new perms validation

* dbeaver/pro#5886 remove unused class

* dbeaver/pro#5886 new api

* chore: update permission key

* dbeaver/pro#5087 feat: permissions management

* feat: check permission for query execution

* fix: override only restrictive permissions

* fix: permissions layer

* fix: status gutter duplication

* fix: script execution error message display

* fix: hide not available script execution actions

* dbeaver/pro#5886 anon perms fix, default value for global perms

* fix: hide settings groups only for specific settings

* feat: restore permissions

* refactor: after review

* fix: merge conflicts

* fix: type import

* fix: merge conflicts

* fix: linting

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: Wroud <wrouds@gmail.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
2025-07-23 03:56:43 +02:00
AnastasiyaandDaria Marutkina 848b95113a dbeaver/cloudbeaver#3590 update DuckDB driver till version 1.3.2 (#3592)
Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
2025-07-22 11:32:41 +02:00
sergeyteleshevandmr-anton-t d4c304f725 [CB] write unit tests (#3593)
* dbeaver/pro#6312 adds unit tests for helpers

* updates yarn locks

* lint fix

* restores mock

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2025-07-22 09:41:08 +02:00
sergeyteleshev 444ff0d672 [CB] fix unit tests issue with console mocking (#3589)
* dbeaver/pro#6288 adds init for console mocks

* test fix 2

* test fix 3

* test fix 4

* resetets deprecated settings
2025-07-22 09:33:33 +02:00
Daria Marutkina bf49a94642 dbeaver/pro#6275 Update README file (#3596) 2025-07-21 11:11:39 +02:00
JeremyandEvgenia 3a2e41fa69 feat: upgrade databend-jdbc to v0.3.9 (#3540)
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2025-07-21 08:59:53 +02:00
Alexey PotsetsuevandEvgenia 36e8d1fac9 dbeaver/pro#6201 feat: ai chat query insertion (#3578)
* dbeaver/pro#6201 feat: ai chat query insertion

* chore: remove confusing comment

* chore: query execution and replacement

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
2025-07-18 23:44:17 +08:00
sergeyteleshev dcc13811e8 [CB] fix frontend linter to pass the github action (#3588)
* dbeaver/pro#6272 initial commit to see errors

* removes func-call-spacing from rules

* removes func-call-spacing from rules 2

* fixes flat file for stylistic

* try to update stylistic

* Revert "try to update stylistic"

This reverts commit 459bf77714.

* reverts rule

* tries function-call-spacing

* test linter rule

* Revert "test linter rule"

This reverts commit 0b0fb66b69.
2025-07-18 19:51:16 +08:00
Serge Rider 2ba910386f dbeaver/pro#6275 25.1.4 version bump 2025-07-15 20:21:32 +03:00
Serge Rider a7c5d53f0a dbeaver/pro#6275 25.1.4 version bump 2025-07-15 20:20:39 +03:00
295 changed files with 4762 additions and 2426 deletions
+2 -2
View File
@@ -85,8 +85,8 @@ jobs:
- run: yarn test
working-directory: ./cloudbeaver/common-typescript
# - run: yarn test
# working-directory: ./cloudbeaver/common-react
- run: yarn test
working-directory: ./cloudbeaver/common-react
- run: yarn test
+29
View File
@@ -19,6 +19,26 @@
"group": "test",
"args": ["test"]
},
{
"label": "Run Tests Common Typescript",
"type": "shell",
"command": "yarn",
"group": "test",
"args": ["test"],
"options": {
"cwd": "${workspaceFolder}/common-typescript"
}
},
{
"label": "Run Tests Common React",
"type": "shell",
"command": "yarn",
"group": "test",
"args": ["test"],
"options": {
"cwd": "${workspaceFolder}/common-react"
}
},
{
"label": "Run DevServer CE",
"group": "build",
@@ -107,6 +127,15 @@
"options": {
"cwd": "${workspaceFolder}/webapp/packages"
}
},
{
"label": "Run Ui Kit Preview",
"type": "shell",
"command": "yarn",
"options": {
"cwd": "${workspaceFolder}/common-react/@dbeaver/ui-kit"
},
"args": ["docs"]
}
],
"inputs": [
+12
View File
@@ -25,6 +25,18 @@ You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
## Changelog
### 25.1.4 2025-08-04
- Added an ability to review the script before execution when users edit tables without primary keys in the Data Editor;
- DuckDB: driver has been updated to version 1.3.2;
- Databend: driver has been updated to version 0.3.9.
### 25.1.3 2025-07-21
- A password confirmation field has been added for administrators in the Easy Config section to help prevent accidental misconfigurations;
- Added column descriptions in the Data Editor to provide more metadata context. You can disable this in Preferences > Data Viewer;
- Added the option to display tabs across multiple rows, allowing you to see all tabs without scrolling. You can enable this in Preferences > Interface;
- Changed the default engine used for autocompletion in the SQL Editor. This Semantic engine offers improved suggestions for database objects, keywords, and functions. You can switch back to the Legacy engine in Preferences -> SQL Editor;
- Clickhouse: fixed the presentation of tuples and map data types in the data editor.
### 25.1.2 2025-07-07
- The CloudBeaver default theme matches the device theme by default now. You can change this behavior in user preferences under the Theme section;
- Added a button “Clear” to the Output panel;
+2 -1
View File
@@ -28,7 +28,8 @@
"@dbeaver/tsconfig": "workspace:^",
"@types/react": "^19",
"rimraf": "^6",
"typescript": "^5"
"typescript": "^5",
"vitest": "^3"
},
"dependencies": {
"react": "^19"
@@ -0,0 +1,54 @@
import { describe, it, expect } from 'vitest';
import React from 'react';
import { DND_STORE_PREFIX, DnDStore, getStoreProvider } from './store.js';
function createMockDragEvent(id?: string): React.DragEvent<HTMLElement> {
const types = id ? [DND_STORE_PREFIX + id] : [];
return {
dataTransfer: { types },
} as unknown as React.DragEvent<HTMLElement>;
}
describe('DnDStore', () => {
it('sets and gets data correctly', () => {
const store = new DnDStore();
store.setData('id1', 'key1', 'value1');
expect(store.getData('id1', 'key1')).toBe('value1');
});
it('removes data correctly', () => {
const store = new DnDStore();
store.setData('id2', 'key2', 'value2');
store.removeData('id2');
expect(store.getData('id2', 'key2')).toBeUndefined();
});
it('getProvider returns a working provider', () => {
const store = new DnDStore();
const provider = store.getProvider('id3');
provider.setData('key3', 'value3');
expect(provider.getData('key3')).toBe('value3');
provider.removeData();
expect(provider.getData('key3')).toBeUndefined();
});
});
describe('getStoreProvider', () => {
it('returns null if no DND_STORE_PREFIX type is present', () => {
const event = createMockDragEvent();
expect(getStoreProvider(event)).toBeNull();
});
it('returns a provider if DND_STORE_PREFIX type is present', () => {
const id = 'testid';
const event = createMockDragEvent(id);
const provider = getStoreProvider(event);
expect(provider).not.toBeNull();
provider!.setData('foo', 'bar');
expect(provider!.getData('foo')).toBe('bar');
provider!.removeData();
expect(provider!.getData('foo')).toBeUndefined();
});
});
+1 -1
View File
@@ -6,7 +6,7 @@ export interface DnDStoreProvider {
removeData: () => void;
}
class DnDStore {
export class DnDStore {
private data: Map<string, Record<string, any>> = new Map();
setData(id: string, key: string, value: any) {
if (!this.data.has(id)) {
@@ -0,0 +1 @@
# @dbeaver/react-tests
@@ -0,0 +1,38 @@
{
"name": "@dbeaver/react-tests",
"type": "module",
"sideEffects": [],
"exports": {
".": "./lib/index.js",
"./*": "./lib/*.js"
},
"scripts": {
"clear": "rimraf lib"
},
"files": [
"package.json",
"LICENSE",
"README.md",
"CHANGELOG.md",
"lib",
"!lib/**/*.d.ts.map",
"!lib/**/*.test.js",
"!lib/**/*.test.d.ts",
"!lib/**/*.test.d.ts.map",
"!lib/**/*.test.js.map",
"!lib/tests",
"!.tsbuildinfo"
],
"packageManager": "yarn@4.9.2",
"devDependencies": {
"@dbeaver/cli": "workspace:^",
"@dbeaver/tsconfig": "workspace:^",
"@testing-library/dom": "^10",
"@testing-library/jest-dom": "^6",
"@testing-library/react": "^16",
"@types/node": "^22",
"rimraf": "^6",
"typescript": "^5",
"vitest": "^3"
}
}
@@ -0,0 +1 @@
export { default as ReactVitestConfig } from './vitest.config.js';
@@ -0,0 +1,44 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { mergeConfig, defineConfig } from 'vitest/config';
import { DefaultVitestConfig } from '@dbeaver/cli';
import { posix } from 'node:path';
export default mergeConfig(
DefaultVitestConfig,
defineConfig({
test: {
environment: 'happy-dom',
setupFiles: [posix.resolve('./vitest.setup.js')],
alias: [
{
find: /.*.(css|scss|less)$/,
replacement: import.meta.resolve('./__mocks__/styleMock.js'),
},
],
css: false,
},
plugins: [
{
name: 'vitest-setup',
resolveId(source) {
if (source === posix.resolve('./vitest.setup.js')) {
return '\0internal:vitest-setup';
}
return null;
},
load(id) {
if (id === '\0internal:vitest-setup') {
return "import '@dbeaver/react-tests/vitest.setup';";
}
return null;
},
},
],
}),
);
@@ -5,6 +5,7 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import '@testing-library/jest-dom/vitest';
// different machine has its own timezones and some tests can fail because of it
@@ -0,0 +1,21 @@
{
"extends": "@dbeaver/tsconfig/tsconfig.json",
"compilerOptions": {
"tsBuildInfoFile": "./lib/.tsbuildinfo",
"rootDir": "src",
"rootDirs": [
"src"
],
"outDir": "lib",
"incremental": true,
"composite": true
},
"include": [
"src"
],
"references": [
{
"path": "../../../common-typescript/@dbeaver/cli"
}
]
}
@@ -6,23 +6,49 @@
* you may not use this file except in compliance with the License.
*/
import { type ComponentPropsWithRef } from 'react';
import { type ComponentPropsWithRef, type ReactElement, useId } from 'react';
import './Input.css';
import clsx from 'clsx';
import { componentProviderWrapper } from '../componentProviderWrapper.js';
export interface InputProps extends Omit<ComponentPropsWithRef<'input'>, 'size'> {
size?: 'small' | 'medium' | 'large' | 'xlarge';
label?: React.ReactNode;
}
export type InputProps = Omit<ComponentPropsWithRef<'input'>, 'size'> & {
label?: ReactElement | string;
size?: 'small' | 'medium' | 'large' | 'xlarge';
};
export const InputBase: React.FC<InputProps> = function Input({ size, className, ...props }) {
export const InputBase: React.FC<InputProps> = function Input({
size,
className,
label,
'aria-labelledby': ariaLabelledBy,
'aria-label': ariaLabel,
...props
}) {
const labelId = useId();
const classNameToApply = clsx(`dbv-kit-input`, `dbv-kit-input--${size ?? 'medium'}`, className);
if (label) {
return (
<label className="dbv-kit-input-wrapper">
<div id={labelId} className={clsx('dbv-kit-input__title', props.required && 'dbv-kit-input__title--required')}>
{label}
</div>
<input
className={classNameToApply}
aria-labelledby={labelId}
{...props}
/>
</label>
);
}
return (
<label className="dbv-kit-input-wrapper">
{props.label && <div className={clsx('dbv-kit-input__title', props.required && 'dbv-kit-input__title--required')}>{props.label}</div>}
<input className={classNameToApply} {...props} />
</label>
<input
className={classNameToApply}
aria-labelledby={ariaLabelledBy}
aria-label={ariaLabel}
{...props}
/>
);
};
@@ -20,19 +20,20 @@
font-weight: var(--dbv-kit-select-font-weight);
font-size: var(--dbv-kit-select-font-size);
outline-width: var(--dbv-kit-control-outline-width);
outline-offset: var(--dbv-kit-control-outline-offset);
outline-color: var(--dbv-kit-control-outline-color);
&:hover {
border-color: var(--dbv-kit-color-control-hover);
&:hover:not(:disabled) {
border-color: var(--dbv-kit-select-hover-border-color);
}
&[aria-disabled='true'] {
opacity: var(--dbv-kit-control-disabled-opacity);
color: var(--dbv-kit-select-disabled-foreground);
background-color: var(--dbv-kit-select-disabled-background);
}
&[data-focus-visible],
&:focus-visible {
outline-style: solid;
outline-offset: var(--dbv-kit-control-outline-offset);
}
}
@@ -74,6 +75,8 @@
.dbv-kit-select__item {
display: block;
width: 100%;
font-weight: var(--dbv-kit-select-font-weight);
font-size: var(--dbv-kit-select-font-size);
padding-inline: var(--dbv-kit-select-item-padding-inline);
padding-block: var(--dbv-kit-select-item-padding-block);
text-align: left;
@@ -8,7 +8,7 @@
import { useState } from 'react';
import clsx from 'clsx';
import { SelectProvider, Select, SelectPopover, SelectItem, SelectLabel } from './Select.js';
import { SelectProvider, Select, SelectPopover, SelectItem, SelectLabel, type SelectProviderProps } from './Select.js';
import './SelectField.css';
export interface SelectItem<T> {
@@ -57,6 +57,8 @@ export interface SelectFieldProps<T, ItemType = SelectItem<T>> {
className?: string;
noItemsPlaceholder?: React.ReactNode;
/**
* Custom renderer for the selected value, overrides itemRenderer for the selected state
* Only needed for special formatting of the selected value different from list items
@@ -67,6 +69,16 @@ export interface SelectFieldProps<T, ItemType = SelectItem<T>> {
* Custom arrow icon React Node that will be rendered instead default one
*/
arrowIcon?: React.ReactNode;
store?: SelectProviderProps['store'];
autoFocusItemsOnShow?: boolean;
'aria-labelledby'?: string,
'aria-label'?: string
id?: string;
}
// Utility function to get value by it's key or using getter function
@@ -82,13 +94,17 @@ export function SelectField<T, ItemType extends {} = SelectItem<T>>({
itemRender,
itemDisabled,
label,
noItemsPlaceholder = 'No items',
description,
disabled,
required,
className,
selectedRender,
arrowIcon,
store,
autoFocusItemsOnShow,
name,
id,
}: SelectFieldProps<T, ItemType>) {
const getItemValue = (item: ItemType): T =>
getValueByPath<ItemType, T>(item, itemValue, i => ('value' in i ? (i as unknown as SelectItem<T>).value : (i as unknown as T)));
@@ -115,29 +131,28 @@ export function SelectField<T, ItemType extends {} = SelectItem<T>>({
const selectedItem = items.find(item => getItemValue(item) === currentValue);
const displayValue = selectedItem ? (selectedRender ? selectedRender(currentValue, selectedItem) : renderItem(selectedItem)) : '';
const displayValue = selectedRender ? selectedRender(currentValue, selectedItem) : selectedItem ? renderItem(selectedItem) : '';
return (
<div className={clsx('dbv-kit-select-field', className)}>
<SelectProvider value={currentValue as any} setValue={val => handleChange(val as T)}>
<SelectProvider value={currentValue as any} setValue={val => handleChange(val as T)} store={store}>
{label && <SelectLabel className={clsx(required && 'dbv-kit-select__label--required')}>{label}</SelectLabel>}
<Select name={name} disabled={disabled}>
<Select id={id} name={name} disabled={disabled} required={required}>
{displayValue}
{arrowIcon ?? <Select.Arrow />}
{arrowIcon ?? <Select.Arrow className='dbv-kit-select__arrow-icon' />}
</Select>
{description && <span className="dbv-kit-select__description">{description}</span>}
<SelectPopover gutter={4} unmountOnHide>
<SelectPopover autoFocusOnShow={autoFocusItemsOnShow} gutter={4} unmountOnHide>
{items.length === 0 ? (
<div className="dbv-kit-select__empty">No items</div>
<div className="dbv-kit-select__empty">{noItemsPlaceholder}</div>
) : (
items.map(item => (
<SelectItem
key={String(getItemValue(item))}
value={getItemValue(item) as any}
disabled={isItemDisabled(item)}
className="dbv-kit-select__item"
>
{renderItem(item)}
</SelectItem>
@@ -5,11 +5,14 @@
--dbv-kit-select-foreground: var(--dbv-kit-color-foreground);
--dbv-kit-select-background: var(--dbv-kit-color-background);
--dbv-kit-select-disabled-foreground: var(--dbv-kit-control-disabled-foreground);
--dbv-kit-select-disabled-background: var(--dbv-kit-control-disabled-background);
--dbv-kit-select-border-width: var(--dbv-kit-control-border-width);
--dbv-kit-select-border-color: var(--dbv-kit-color-control-inactive);
--dbv-kit-select-border-style: solid;
--dbv-kit-select-border-radius: var(--dbv-kit-control-border-radius);
--dbv-kit-select-hover-border-color: var(--dbv-kit-color-control-hover);
--dbv-kit-select-font-weight: var(--tw-font-weight-normal);
--dbv-kit-select-font-size: calc(var(--dbv-kit-font-size-base) * 0.875);
+8 -2
View File
@@ -1,5 +1,6 @@
{
"name": "common-react",
"type": "module",
"packageManager": "yarn@4.9.2",
"private": true,
"workspaces": [
@@ -7,12 +8,17 @@
"../common-typescript"
],
"scripts": {
"clear": "rimraf -g '@dbeaver/*/{dist,lib}'"
"clear": "rimraf -g '@dbeaver/*/{dist,lib}'",
"test": "dbeaver-test"
},
"devDependencies": {
"@dbeaver/cli": "workspace:^",
"@dbeaver/prettier-config": "workspace:^",
"@dbeaver/react-tests": "workspace:^",
"@wroud/ts-template": "^0",
"happy-dom": "^18",
"prettier": "^3.6.2",
"rimraf": "^6"
"rimraf": "^6",
"vitest": "^3"
}
}
+11
View File
@@ -0,0 +1,11 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { ReactVitestConfig } from '@dbeaver/react-tests';
export default ReactVitestConfig;
+583 -343
View File
File diff suppressed because it is too large Load Diff
@@ -27,6 +27,7 @@
"devDependencies": {
"@dbeaver/tsconfig": "workspace:^",
"rimraf": "^6",
"typescript": "^5"
"typescript": "^5",
"vitest": "^3"
}
}
@@ -0,0 +1,417 @@
import { describe, it, expect } from 'vitest';
import { parseJdbcUri } from './parseJdbcUri.js';
describe('parseJdbcUri', () => {
describe('invalid inputs', () => {
it('should handle null input', () => {
const result = parseJdbcUri(null);
expect(result).toEqual({
raw: '',
subprotocol: '',
subname: '',
queryParams: {},
});
});
it('should handle undefined input', () => {
const result = parseJdbcUri(undefined);
expect(result).toEqual({
raw: '',
subprotocol: '',
subname: '',
queryParams: {},
});
});
it('should handle empty string', () => {
const result = parseJdbcUri('');
expect(result).toEqual({
raw: '',
subprotocol: '',
subname: '',
queryParams: {},
});
});
it('should handle non-string input', () => {
const result = parseJdbcUri(123 as any);
expect(result).toEqual({
raw: '123',
subprotocol: '',
subname: '',
queryParams: {},
});
});
});
describe('non-jdbc URLs', () => {
it('should handle non-jdbc URLs', () => {
const result = parseJdbcUri('https://example.com');
expect(result).toEqual({
raw: 'https://example.com',
subprotocol: '',
subname: 'https://example.com',
queryParams: {},
});
});
it('should handle plain text', () => {
const result = parseJdbcUri('just some text');
expect(result).toEqual({
raw: 'just some text',
subprotocol: '',
subname: 'just some text',
queryParams: {},
});
});
});
describe('malformed jdbc URLs', () => {
it('should handle jdbc: without subprotocol', () => {
const result = parseJdbcUri('jdbc:');
expect(result).toEqual({
raw: 'jdbc:',
subprotocol: '',
subname: '',
queryParams: {},
});
});
it('should handle jdbc: with only subprotocol', () => {
const result = parseJdbcUri('jdbc:mysql');
expect(result).toEqual({
raw: 'jdbc:mysql',
subprotocol: '',
subname: 'mysql',
queryParams: {},
});
});
});
describe('SQLite URLs', () => {
it('should parse SQLite file path', () => {
const result = parseJdbcUri('jdbc:sqlite:/path/to/file.db');
expect(result).toEqual({
raw: 'jdbc:sqlite:/path/to/file.db',
subprotocol: 'sqlite',
subname: '/path/to/file.db',
database: '/path/to/file.db',
queryParams: {},
});
});
it('should parse SQLite relative path', () => {
const result = parseJdbcUri('jdbc:sqlite:file.db');
expect(result).toEqual({
raw: 'jdbc:sqlite:file.db',
subprotocol: 'sqlite',
subname: 'file.db',
database: 'file.db',
queryParams: {},
});
});
it('should parse SQLite in-memory database', () => {
const result = parseJdbcUri('jdbc:sqlite::memory:');
expect(result).toEqual({
raw: 'jdbc:sqlite::memory:',
subprotocol: 'sqlite',
subname: ':memory:',
database: ':memory:',
queryParams: {},
});
});
});
describe('Oracle Thin URLs', () => {
it('should parse Oracle Thin with host, port, and SID', () => {
const result = parseJdbcUri('jdbc:oracle:thin:@localhost:1521:XE');
expect(result).toEqual({
raw: 'jdbc:oracle:thin:@localhost:1521:XE',
subprotocol: 'oracle',
subname: 'thin:@localhost:1521:XE',
host: 'localhost',
port: '1521',
database: 'XE',
queryParams: {},
});
});
it('should handle malformed Oracle Thin URL', () => {
const result = parseJdbcUri('jdbc:oracle:thin:@localhost');
expect(result).toEqual({
raw: 'jdbc:oracle:thin:@localhost',
subprotocol: 'oracle',
subname: 'thin:@localhost',
queryParams: {},
});
});
it('should handle Oracle Thin with different format', () => {
const result = parseJdbcUri('jdbc:oracle:thin:@localhost:1521');
expect(result).toEqual({
raw: 'jdbc:oracle:thin:@localhost:1521',
subprotocol: 'oracle',
subname: 'thin:@localhost:1521',
queryParams: {},
});
});
});
describe('standard JDBC URLs with //', () => {
it('should parse MySQL URL with host and port', () => {
const result = parseJdbcUri('jdbc:mysql://localhost:3306/testdb');
expect(result).toEqual({
raw: 'jdbc:mysql://localhost:3306/testdb',
subprotocol: 'mysql',
subname: '//localhost:3306/testdb',
host: 'localhost',
port: '3306',
database: 'testdb',
queryParams: {},
});
});
it('should parse PostgreSQL URL without port', () => {
const result = parseJdbcUri('jdbc:postgresql://localhost/mydb');
expect(result).toEqual({
raw: 'jdbc:postgresql://localhost/mydb',
subprotocol: 'postgresql',
subname: '//localhost/mydb',
host: 'localhost',
port: undefined,
database: 'mydb',
queryParams: {},
});
});
it('should parse URL with credentials', () => {
const result = parseJdbcUri('jdbc:mysql://user:pass@localhost:3306/testdb');
expect(result).toEqual({
raw: 'jdbc:mysql://user:pass@localhost:3306/testdb',
subprotocol: 'mysql',
subname: '//user:pass@localhost:3306/testdb',
host: 'localhost',
port: '3306',
database: 'testdb',
username: 'user',
password: 'pass',
queryParams: {},
});
});
it('should parse URL with query parameters', () => {
const result = parseJdbcUri('jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC');
expect(result).toEqual({
raw: 'jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC',
subprotocol: 'mysql',
subname: '//localhost:3306/testdb?useSSL=false&serverTimezone=UTC',
host: 'localhost',
port: '3306',
database: 'testdb',
queryParams: {
useSSL: 'false',
serverTimezone: 'UTC',
},
});
});
it('should parse URL with empty query parameter', () => {
const result = parseJdbcUri('jdbc:mysql://localhost:3306/testdb?param1=&param2=value');
expect(result).toEqual({
raw: 'jdbc:mysql://localhost:3306/testdb?param1=&param2=value',
subprotocol: 'mysql',
subname: '//localhost:3306/testdb?param1=&param2=value',
host: 'localhost',
port: '3306',
database: 'testdb',
queryParams: {
param1: '',
param2: 'value',
},
});
});
it('should handle URL-encoded query parameters', () => {
const result = parseJdbcUri('jdbc:mysql://localhost:3306/testdb?param1=value%20with%20spaces&param2=value%3Dtest');
expect(result).toEqual({
raw: 'jdbc:mysql://localhost:3306/testdb?param1=value%20with%20spaces&param2=value%3Dtest',
subprotocol: 'mysql',
subname: '//localhost:3306/testdb?param1=value%20with%20spaces&param2=value%3Dtest',
host: 'localhost',
port: '3306',
database: 'testdb',
queryParams: {
param1: 'value with spaces',
param2: 'value=test',
},
});
});
it('should handle malformed URL gracefully', () => {
const result = parseJdbcUri('jdbc:mysql://:3306/testdb');
expect(result).toEqual({
raw: 'jdbc:mysql://:3306/testdb',
subprotocol: 'mysql',
subname: '//:3306/testdb',
host: undefined,
port: '3306',
database: 'testdb',
queryParams: {},
});
});
});
describe('template placeholders', () => {
it('should handle host placeholder', () => {
const result = parseJdbcUri('jdbc:mysql://{host}:3306/testdb');
expect(result).toEqual({
raw: 'jdbc:mysql://{host}:3306/testdb',
subprotocol: 'mysql',
subname: '//{host}:3306/testdb',
host: undefined,
port: '3306',
database: '/{host}:3306/testdb',
queryParams: {},
});
});
it('should handle port placeholder', () => {
const result = parseJdbcUri('jdbc:mysql://localhost:{port}/testdb');
expect(result).toEqual({
raw: 'jdbc:mysql://localhost:{port}/testdb',
subprotocol: 'mysql',
subname: '//localhost:{port}/testdb',
host: 'localhost',
port: undefined,
database: '/localhost:{port}/testdb',
queryParams: {},
});
});
it('should handle database placeholder', () => {
const result = parseJdbcUri('jdbc:mysql://localhost:3306/{database}');
expect(result).toEqual({
raw: 'jdbc:mysql://localhost:3306/{database}',
subprotocol: 'mysql',
subname: '//localhost:3306/{database}',
host: 'localhost',
port: '3306',
database: '/localhost:3306/{database}',
queryParams: {},
});
});
it('should handle multiple placeholders', () => {
const result = parseJdbcUri('jdbc:mysql://{host}:{port}/{database}');
expect(result).toEqual({
raw: 'jdbc:mysql://{host}:{port}/{database}',
subprotocol: 'mysql',
subname: '//{host}:{port}/{database}',
host: undefined,
port: undefined,
database: '/{host}:{port}/{database}',
queryParams: {},
});
});
it('should handle placeholders with query parameters', () => {
const result = parseJdbcUri('jdbc:mysql://{host}:{port}/{database}?useSSL=false');
expect(result).toEqual({
raw: 'jdbc:mysql://{host}:{port}/{database}?useSSL=false',
subprotocol: 'mysql',
subname: '//{host}:{port}/{database}?useSSL=false',
host: undefined,
port: undefined,
database: '/{host}:{port}/{database}',
queryParams: {
useSSL: 'false',
},
});
});
});
describe('other JDBC formats', () => {
it('should handle simple subname as database', () => {
const result = parseJdbcUri('jdbc:hsqldb:mem:testdb');
expect(result).toEqual({
raw: 'jdbc:hsqldb:mem:testdb',
subprotocol: 'hsqldb',
subname: 'mem:testdb',
database: 'mem:testdb',
queryParams: {},
});
});
it('should handle Derby embedded', () => {
const result = parseJdbcUri('jdbc:derby:testdb;create=true');
expect(result).toEqual({
raw: 'jdbc:derby:testdb;create=true',
subprotocol: 'derby',
subname: 'testdb;create=true',
database: 'testdb;create=true',
queryParams: {},
});
});
});
describe('edge cases', () => {
it('should handle URL with only host', () => {
const result = parseJdbcUri('jdbc:mysql://localhost');
expect(result).toEqual({
raw: 'jdbc:mysql://localhost',
subprotocol: 'mysql',
subname: '//localhost',
host: 'localhost',
port: undefined,
database: undefined,
queryParams: {},
});
});
it('should handle URL with host and port but no database', () => {
const result = parseJdbcUri('jdbc:mysql://localhost:3306');
expect(result).toEqual({
raw: 'jdbc:mysql://localhost:3306',
subprotocol: 'mysql',
subname: '//localhost:3306',
host: 'localhost',
port: '3306',
database: undefined,
queryParams: {},
});
});
it('should handle URL with empty database', () => {
const result = parseJdbcUri('jdbc:mysql://localhost:3306/');
expect(result).toEqual({
raw: 'jdbc:mysql://localhost:3306/',
subprotocol: 'mysql',
subname: '//localhost:3306/',
host: 'localhost',
port: '3306',
database: undefined,
username: undefined,
password: undefined,
queryParams: {},
});
});
it('should handle URL with only query parameters', () => {
const result = parseJdbcUri('jdbc:mysql://localhost:3306/?param1=value1');
expect(result).toEqual({
raw: 'jdbc:mysql://localhost:3306/?param1=value1',
subprotocol: 'mysql',
subname: '//localhost:3306/?param1=value1',
host: 'localhost',
port: '3306',
database: undefined,
username: undefined,
password: undefined,
queryParams: {
param1: 'value1',
},
});
});
});
});
@@ -30,5 +30,8 @@
"rimraf": "^6",
"typescript": "^5",
"vitest": "^3"
},
"dependencies": {
"async-mutex": "^0"
}
}
@@ -1,2 +1,3 @@
export * from './isDefined.js';
export * from './isNotNullDefined.js';
export * from './mutex.js';
@@ -0,0 +1 @@
export * as mutex from 'async-mutex';
+238 -224
View File
@@ -95,12 +95,12 @@ __metadata:
linkType: hard
"@babel/types@npm:^7.26.7, @babel/types@npm:^7.27.1, @babel/types@npm:^7.28.0":
version: 7.28.0
resolution: "@babel/types@npm:7.28.0"
version: 7.28.2
resolution: "@babel/types@npm:7.28.2"
dependencies:
"@babel/helper-string-parser": "npm:^7.27.1"
"@babel/helper-validator-identifier": "npm:^7.27.1"
checksum: 10c0/7ca8521bf5e2d2ed4db31176efaaf94463a6b7a4d16dcc60e34e963b3596c2ecadb85457bebed13a9ee9a5829ef5f515d05b55a991b6a8f3b835451843482e39
checksum: 10c0/24b11c9368e7e2c291fe3c1bcd1ed66f6593a3975f479cbb9dd7b8c8d8eab8a962b0d2fca616c043396ce82500ac7d23d594fbbbd013828182c01596370a0b10
languageName: node
linkType: hard
@@ -135,6 +135,7 @@ __metadata:
"@dbeaver/tsconfig": "workspace:^"
rimraf: "npm:^6"
typescript: "npm:^5"
vitest: "npm:^3"
languageName: unknown
linkType: soft
@@ -144,6 +145,7 @@ __metadata:
dependencies:
"@dbeaver/cli": "workspace:^"
"@dbeaver/tsconfig": "workspace:^"
async-mutex: "npm:^0"
rimraf: "npm:^6"
typescript: "npm:^5"
vitest: "npm:^3"
@@ -182,184 +184,184 @@ __metadata:
languageName: unknown
linkType: soft
"@esbuild/aix-ppc64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/aix-ppc64@npm:0.25.6"
"@esbuild/aix-ppc64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/aix-ppc64@npm:0.25.8"
conditions: os=aix & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/android-arm64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/android-arm64@npm:0.25.6"
"@esbuild/android-arm64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/android-arm64@npm:0.25.8"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@esbuild/android-arm@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/android-arm@npm:0.25.6"
"@esbuild/android-arm@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/android-arm@npm:0.25.8"
conditions: os=android & cpu=arm
languageName: node
linkType: hard
"@esbuild/android-x64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/android-x64@npm:0.25.6"
"@esbuild/android-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/android-x64@npm:0.25.8"
conditions: os=android & cpu=x64
languageName: node
linkType: hard
"@esbuild/darwin-arm64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/darwin-arm64@npm:0.25.6"
"@esbuild/darwin-arm64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/darwin-arm64@npm:0.25.8"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@esbuild/darwin-x64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/darwin-x64@npm:0.25.6"
"@esbuild/darwin-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/darwin-x64@npm:0.25.8"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@esbuild/freebsd-arm64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/freebsd-arm64@npm:0.25.6"
"@esbuild/freebsd-arm64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/freebsd-arm64@npm:0.25.8"
conditions: os=freebsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/freebsd-x64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/freebsd-x64@npm:0.25.6"
"@esbuild/freebsd-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/freebsd-x64@npm:0.25.8"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/linux-arm64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/linux-arm64@npm:0.25.6"
"@esbuild/linux-arm64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-arm64@npm:0.25.8"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@esbuild/linux-arm@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/linux-arm@npm:0.25.6"
"@esbuild/linux-arm@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-arm@npm:0.25.8"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@esbuild/linux-ia32@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/linux-ia32@npm:0.25.6"
"@esbuild/linux-ia32@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-ia32@npm:0.25.8"
conditions: os=linux & cpu=ia32
languageName: node
linkType: hard
"@esbuild/linux-loong64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/linux-loong64@npm:0.25.6"
"@esbuild/linux-loong64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-loong64@npm:0.25.8"
conditions: os=linux & cpu=loong64
languageName: node
linkType: hard
"@esbuild/linux-mips64el@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/linux-mips64el@npm:0.25.6"
"@esbuild/linux-mips64el@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-mips64el@npm:0.25.8"
conditions: os=linux & cpu=mips64el
languageName: node
linkType: hard
"@esbuild/linux-ppc64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/linux-ppc64@npm:0.25.6"
"@esbuild/linux-ppc64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-ppc64@npm:0.25.8"
conditions: os=linux & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/linux-riscv64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/linux-riscv64@npm:0.25.6"
"@esbuild/linux-riscv64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-riscv64@npm:0.25.8"
conditions: os=linux & cpu=riscv64
languageName: node
linkType: hard
"@esbuild/linux-s390x@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/linux-s390x@npm:0.25.6"
"@esbuild/linux-s390x@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-s390x@npm:0.25.8"
conditions: os=linux & cpu=s390x
languageName: node
linkType: hard
"@esbuild/linux-x64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/linux-x64@npm:0.25.6"
"@esbuild/linux-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-x64@npm:0.25.8"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@esbuild/netbsd-arm64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/netbsd-arm64@npm:0.25.6"
"@esbuild/netbsd-arm64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/netbsd-arm64@npm:0.25.8"
conditions: os=netbsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/netbsd-x64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/netbsd-x64@npm:0.25.6"
"@esbuild/netbsd-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/netbsd-x64@npm:0.25.8"
conditions: os=netbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/openbsd-arm64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/openbsd-arm64@npm:0.25.6"
"@esbuild/openbsd-arm64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/openbsd-arm64@npm:0.25.8"
conditions: os=openbsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/openbsd-x64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/openbsd-x64@npm:0.25.6"
"@esbuild/openbsd-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/openbsd-x64@npm:0.25.8"
conditions: os=openbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/openharmony-arm64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/openharmony-arm64@npm:0.25.6"
"@esbuild/openharmony-arm64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/openharmony-arm64@npm:0.25.8"
conditions: os=openharmony & cpu=arm64
languageName: node
linkType: hard
"@esbuild/sunos-x64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/sunos-x64@npm:0.25.6"
"@esbuild/sunos-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/sunos-x64@npm:0.25.8"
conditions: os=sunos & cpu=x64
languageName: node
linkType: hard
"@esbuild/win32-arm64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/win32-arm64@npm:0.25.6"
"@esbuild/win32-arm64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/win32-arm64@npm:0.25.8"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@esbuild/win32-ia32@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/win32-ia32@npm:0.25.6"
"@esbuild/win32-ia32@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/win32-ia32@npm:0.25.8"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@esbuild/win32-x64@npm:0.25.6":
version: 0.25.6
resolution: "@esbuild/win32-x64@npm:0.25.6"
"@esbuild/win32-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/win32-x64@npm:0.25.8"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
@@ -610,142 +612,142 @@ __metadata:
languageName: node
linkType: hard
"@rollup/rollup-android-arm-eabi@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-android-arm-eabi@npm:4.44.2"
"@rollup/rollup-android-arm-eabi@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-android-arm-eabi@npm:4.46.1"
conditions: os=android & cpu=arm
languageName: node
linkType: hard
"@rollup/rollup-android-arm64@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-android-arm64@npm:4.44.2"
"@rollup/rollup-android-arm64@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-android-arm64@npm:4.46.1"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@rollup/rollup-darwin-arm64@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-darwin-arm64@npm:4.44.2"
"@rollup/rollup-darwin-arm64@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-darwin-arm64@npm:4.46.1"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@rollup/rollup-darwin-x64@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-darwin-x64@npm:4.44.2"
"@rollup/rollup-darwin-x64@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-darwin-x64@npm:4.46.1"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@rollup/rollup-freebsd-arm64@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-freebsd-arm64@npm:4.44.2"
"@rollup/rollup-freebsd-arm64@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-freebsd-arm64@npm:4.46.1"
conditions: os=freebsd & cpu=arm64
languageName: node
linkType: hard
"@rollup/rollup-freebsd-x64@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-freebsd-x64@npm:4.44.2"
"@rollup/rollup-freebsd-x64@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-freebsd-x64@npm:4.46.1"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@rollup/rollup-linux-arm-gnueabihf@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.44.2"
"@rollup/rollup-linux-arm-gnueabihf@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.46.1"
conditions: os=linux & cpu=arm & libc=glibc
languageName: node
linkType: hard
"@rollup/rollup-linux-arm-musleabihf@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.44.2"
"@rollup/rollup-linux-arm-musleabihf@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.46.1"
conditions: os=linux & cpu=arm & libc=musl
languageName: node
linkType: hard
"@rollup/rollup-linux-arm64-gnu@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.44.2"
"@rollup/rollup-linux-arm64-gnu@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.46.1"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
"@rollup/rollup-linux-arm64-musl@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-linux-arm64-musl@npm:4.44.2"
"@rollup/rollup-linux-arm64-musl@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-linux-arm64-musl@npm:4.46.1"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
"@rollup/rollup-linux-loongarch64-gnu@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.44.2"
"@rollup/rollup-linux-loongarch64-gnu@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.46.1"
conditions: os=linux & cpu=loong64 & libc=glibc
languageName: node
linkType: hard
"@rollup/rollup-linux-powerpc64le-gnu@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.44.2"
"@rollup/rollup-linux-ppc64-gnu@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.46.1"
conditions: os=linux & cpu=ppc64 & libc=glibc
languageName: node
linkType: hard
"@rollup/rollup-linux-riscv64-gnu@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.44.2"
"@rollup/rollup-linux-riscv64-gnu@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.46.1"
conditions: os=linux & cpu=riscv64 & libc=glibc
languageName: node
linkType: hard
"@rollup/rollup-linux-riscv64-musl@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.44.2"
"@rollup/rollup-linux-riscv64-musl@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.46.1"
conditions: os=linux & cpu=riscv64 & libc=musl
languageName: node
linkType: hard
"@rollup/rollup-linux-s390x-gnu@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.44.2"
"@rollup/rollup-linux-s390x-gnu@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.46.1"
conditions: os=linux & cpu=s390x & libc=glibc
languageName: node
linkType: hard
"@rollup/rollup-linux-x64-gnu@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-linux-x64-gnu@npm:4.44.2"
"@rollup/rollup-linux-x64-gnu@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-linux-x64-gnu@npm:4.46.1"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
"@rollup/rollup-linux-x64-musl@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-linux-x64-musl@npm:4.44.2"
"@rollup/rollup-linux-x64-musl@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-linux-x64-musl@npm:4.46.1"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
"@rollup/rollup-win32-arm64-msvc@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.44.2"
"@rollup/rollup-win32-arm64-msvc@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.46.1"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@rollup/rollup-win32-ia32-msvc@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.44.2"
"@rollup/rollup-win32-ia32-msvc@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.46.1"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@rollup/rollup-win32-x64-msvc@npm:4.44.2":
version: 4.44.2
resolution: "@rollup/rollup-win32-x64-msvc@npm:4.44.2"
"@rollup/rollup-win32-x64-msvc@npm:4.46.1":
version: 4.46.1
resolution: "@rollup/rollup-win32-x64-msvc@npm:4.46.1"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
@@ -814,11 +816,11 @@ __metadata:
linkType: hard
"@types/node@npm:^22":
version: 22.16.0
resolution: "@types/node@npm:22.16.0"
version: 22.16.5
resolution: "@types/node@npm:22.16.5"
dependencies:
undici-types: "npm:~6.21.0"
checksum: 10c0/6219b521062f6c38d4d85ebd25807bd7f2bc703a5acba24e2c6716938d9d6cefd6fafd7b5156f61580eb58a0d82e8921751b778655675389631d813e5f261c03
checksum: 10c0/93a245e96a01f4a6aa38475000070292a9299e17b95bc1fc8180c652fb3a62f9b3b9ec0b78f9fc6a78b9274d3dfce456e80c7bb68b5357c8f7dbf581df4408c1
languageName: node
linkType: hard
@@ -989,6 +991,15 @@ __metadata:
languageName: node
linkType: hard
"async-mutex@npm:^0":
version: 0.5.0
resolution: "async-mutex@npm:0.5.0"
dependencies:
tslib: "npm:^2.4.0"
checksum: 10c0/9096e6ad6b674c894d8ddd5aa4c512b09bb05931b8746ebd634952b05685608b2b0820ed5c406e6569919ff5fe237ab3c491e6f2887d6da6b6ba906db3ee9c32
languageName: node
linkType: hard
"balanced-match@npm:^1.0.0":
version: 1.0.2
resolution: "balanced-match@npm:1.0.2"
@@ -1052,15 +1063,15 @@ __metadata:
linkType: hard
"chai@npm:^5.2.0":
version: 5.2.0
resolution: "chai@npm:5.2.0"
version: 5.2.1
resolution: "chai@npm:5.2.1"
dependencies:
assertion-error: "npm:^2.0.1"
check-error: "npm:^2.1.1"
deep-eql: "npm:^5.0.1"
loupe: "npm:^3.1.0"
pathval: "npm:^2.0.0"
checksum: 10c0/dfd1cb719c7cebb051b727672d382a35338af1470065cb12adb01f4ee451bbf528e0e0f9ab2016af5fc1eea4df6e7f4504dc8443f8f00bd8fb87ad32dc516f7d
checksum: 10c0/58209c03ae9b2fd97cfa1cb0fbe372b1906e6091311b9ba1b0468cc4923b0766a50a1050a164df3ccefb9464944c9216b632f1477c9e429068013bdbb57220f6
languageName: node
linkType: hard
@@ -1260,35 +1271,35 @@ __metadata:
linkType: hard
"esbuild@npm:^0.25.0":
version: 0.25.6
resolution: "esbuild@npm:0.25.6"
version: 0.25.8
resolution: "esbuild@npm:0.25.8"
dependencies:
"@esbuild/aix-ppc64": "npm:0.25.6"
"@esbuild/android-arm": "npm:0.25.6"
"@esbuild/android-arm64": "npm:0.25.6"
"@esbuild/android-x64": "npm:0.25.6"
"@esbuild/darwin-arm64": "npm:0.25.6"
"@esbuild/darwin-x64": "npm:0.25.6"
"@esbuild/freebsd-arm64": "npm:0.25.6"
"@esbuild/freebsd-x64": "npm:0.25.6"
"@esbuild/linux-arm": "npm:0.25.6"
"@esbuild/linux-arm64": "npm:0.25.6"
"@esbuild/linux-ia32": "npm:0.25.6"
"@esbuild/linux-loong64": "npm:0.25.6"
"@esbuild/linux-mips64el": "npm:0.25.6"
"@esbuild/linux-ppc64": "npm:0.25.6"
"@esbuild/linux-riscv64": "npm:0.25.6"
"@esbuild/linux-s390x": "npm:0.25.6"
"@esbuild/linux-x64": "npm:0.25.6"
"@esbuild/netbsd-arm64": "npm:0.25.6"
"@esbuild/netbsd-x64": "npm:0.25.6"
"@esbuild/openbsd-arm64": "npm:0.25.6"
"@esbuild/openbsd-x64": "npm:0.25.6"
"@esbuild/openharmony-arm64": "npm:0.25.6"
"@esbuild/sunos-x64": "npm:0.25.6"
"@esbuild/win32-arm64": "npm:0.25.6"
"@esbuild/win32-ia32": "npm:0.25.6"
"@esbuild/win32-x64": "npm:0.25.6"
"@esbuild/aix-ppc64": "npm:0.25.8"
"@esbuild/android-arm": "npm:0.25.8"
"@esbuild/android-arm64": "npm:0.25.8"
"@esbuild/android-x64": "npm:0.25.8"
"@esbuild/darwin-arm64": "npm:0.25.8"
"@esbuild/darwin-x64": "npm:0.25.8"
"@esbuild/freebsd-arm64": "npm:0.25.8"
"@esbuild/freebsd-x64": "npm:0.25.8"
"@esbuild/linux-arm": "npm:0.25.8"
"@esbuild/linux-arm64": "npm:0.25.8"
"@esbuild/linux-ia32": "npm:0.25.8"
"@esbuild/linux-loong64": "npm:0.25.8"
"@esbuild/linux-mips64el": "npm:0.25.8"
"@esbuild/linux-ppc64": "npm:0.25.8"
"@esbuild/linux-riscv64": "npm:0.25.8"
"@esbuild/linux-s390x": "npm:0.25.8"
"@esbuild/linux-x64": "npm:0.25.8"
"@esbuild/netbsd-arm64": "npm:0.25.8"
"@esbuild/netbsd-x64": "npm:0.25.8"
"@esbuild/openbsd-arm64": "npm:0.25.8"
"@esbuild/openbsd-x64": "npm:0.25.8"
"@esbuild/openharmony-arm64": "npm:0.25.8"
"@esbuild/sunos-x64": "npm:0.25.8"
"@esbuild/win32-arm64": "npm:0.25.8"
"@esbuild/win32-ia32": "npm:0.25.8"
"@esbuild/win32-x64": "npm:0.25.8"
dependenciesMeta:
"@esbuild/aix-ppc64":
optional: true
@@ -1344,7 +1355,7 @@ __metadata:
optional: true
bin:
esbuild: bin/esbuild
checksum: 10c0/6c2ddc66d8789d75bfa940fddf51a6a98b0fcb474f090669b47091f587e8c3e8e7da57d769b770fd8133268dd5bfc7055318aea0bca6f7c725220d7550437b42
checksum: 10c0/43747a25e120d5dd9ce75c82f57306580d715647c8db4f4a0a84e73b04cf16c27572d3937d3cfb95d5ac3266a4d1bbd3913e3d76ae719693516289fc86f8a5fd
languageName: node
linkType: hard
@@ -1786,8 +1797,8 @@ __metadata:
linkType: hard
"less@npm:^4.2.0":
version: 4.3.0
resolution: "less@npm:4.3.0"
version: 4.4.0
resolution: "less@npm:4.4.0"
dependencies:
copy-anything: "npm:^2.0.1"
errno: "npm:^0.1.1"
@@ -1816,7 +1827,7 @@ __metadata:
optional: true
bin:
lessc: bin/lessc
checksum: 10c0/69a9260d4613387fd1f2da3b0904005423272c59f304b27475c145a62f6890be5d38729fd78ef2a65cba3d1d9b02760f309074cad0be4764252934ad04efb2ae
checksum: 10c0/843b3f194fcceba2c244db3d51b1d73599410c4919d4e96b572d91a28547e2f09d60cf88e6a28c2df944eb694af1daee09c56e0f0dc4a506b8461777e3334a92
languageName: node
linkType: hard
@@ -1842,9 +1853,9 @@ __metadata:
linkType: hard
"loupe@npm:^3.1.0, loupe@npm:^3.1.4":
version: 3.1.4
resolution: "loupe@npm:3.1.4"
checksum: 10c0/5c2e6aefaad25f812d361c750b8cf4ff91d68de289f141d7c85c2ce9bb79eeefa06a93c85f7b87cba940531ed8f15e492f32681d47eed23842ad1963eb3a154d
version: 3.2.0
resolution: "loupe@npm:3.2.0"
checksum: 10c0/f572fd9e38db8d36ae9eede305480686e310d69bc40394b6842838ebc6c3860a0e35ab30182f33606ab2d8a685d9ff6436649269f8218a1c3385ca329973cb2c
languageName: node
linkType: hard
@@ -2229,10 +2240,10 @@ __metadata:
languageName: node
linkType: hard
"picomatch@npm:^4.0.2":
version: 4.0.2
resolution: "picomatch@npm:4.0.2"
checksum: 10c0/7c51f3ad2bb42c776f49ebf964c644958158be30d0a510efd5a395e8d49cb5acfed5b82c0c5b365523ce18e6ab85013c9ebe574f60305892ec3fa8eee8304ccc
"picomatch@npm:^4.0.2, picomatch@npm:^4.0.3":
version: 4.0.3
resolution: "picomatch@npm:4.0.3"
checksum: 10c0/9582c951e95eebee5434f59e426cddd228a7b97a0161a375aed4be244bd3fe8e3a31b846808ea14ef2c8a2527a6eeab7b3946a67d5979e81694654f939473ae2
languageName: node
linkType: hard
@@ -2405,29 +2416,29 @@ __metadata:
linkType: hard
"rollup@npm:^4.40.0":
version: 4.44.2
resolution: "rollup@npm:4.44.2"
version: 4.46.1
resolution: "rollup@npm:4.46.1"
dependencies:
"@rollup/rollup-android-arm-eabi": "npm:4.44.2"
"@rollup/rollup-android-arm64": "npm:4.44.2"
"@rollup/rollup-darwin-arm64": "npm:4.44.2"
"@rollup/rollup-darwin-x64": "npm:4.44.2"
"@rollup/rollup-freebsd-arm64": "npm:4.44.2"
"@rollup/rollup-freebsd-x64": "npm:4.44.2"
"@rollup/rollup-linux-arm-gnueabihf": "npm:4.44.2"
"@rollup/rollup-linux-arm-musleabihf": "npm:4.44.2"
"@rollup/rollup-linux-arm64-gnu": "npm:4.44.2"
"@rollup/rollup-linux-arm64-musl": "npm:4.44.2"
"@rollup/rollup-linux-loongarch64-gnu": "npm:4.44.2"
"@rollup/rollup-linux-powerpc64le-gnu": "npm:4.44.2"
"@rollup/rollup-linux-riscv64-gnu": "npm:4.44.2"
"@rollup/rollup-linux-riscv64-musl": "npm:4.44.2"
"@rollup/rollup-linux-s390x-gnu": "npm:4.44.2"
"@rollup/rollup-linux-x64-gnu": "npm:4.44.2"
"@rollup/rollup-linux-x64-musl": "npm:4.44.2"
"@rollup/rollup-win32-arm64-msvc": "npm:4.44.2"
"@rollup/rollup-win32-ia32-msvc": "npm:4.44.2"
"@rollup/rollup-win32-x64-msvc": "npm:4.44.2"
"@rollup/rollup-android-arm-eabi": "npm:4.46.1"
"@rollup/rollup-android-arm64": "npm:4.46.1"
"@rollup/rollup-darwin-arm64": "npm:4.46.1"
"@rollup/rollup-darwin-x64": "npm:4.46.1"
"@rollup/rollup-freebsd-arm64": "npm:4.46.1"
"@rollup/rollup-freebsd-x64": "npm:4.46.1"
"@rollup/rollup-linux-arm-gnueabihf": "npm:4.46.1"
"@rollup/rollup-linux-arm-musleabihf": "npm:4.46.1"
"@rollup/rollup-linux-arm64-gnu": "npm:4.46.1"
"@rollup/rollup-linux-arm64-musl": "npm:4.46.1"
"@rollup/rollup-linux-loongarch64-gnu": "npm:4.46.1"
"@rollup/rollup-linux-ppc64-gnu": "npm:4.46.1"
"@rollup/rollup-linux-riscv64-gnu": "npm:4.46.1"
"@rollup/rollup-linux-riscv64-musl": "npm:4.46.1"
"@rollup/rollup-linux-s390x-gnu": "npm:4.46.1"
"@rollup/rollup-linux-x64-gnu": "npm:4.46.1"
"@rollup/rollup-linux-x64-musl": "npm:4.46.1"
"@rollup/rollup-win32-arm64-msvc": "npm:4.46.1"
"@rollup/rollup-win32-ia32-msvc": "npm:4.46.1"
"@rollup/rollup-win32-x64-msvc": "npm:4.46.1"
"@types/estree": "npm:1.0.8"
fsevents: "npm:~2.3.2"
dependenciesMeta:
@@ -2453,7 +2464,7 @@ __metadata:
optional: true
"@rollup/rollup-linux-loongarch64-gnu":
optional: true
"@rollup/rollup-linux-powerpc64le-gnu":
"@rollup/rollup-linux-ppc64-gnu":
optional: true
"@rollup/rollup-linux-riscv64-gnu":
optional: true
@@ -2475,7 +2486,7 @@ __metadata:
optional: true
bin:
rollup: dist/bin/rollup
checksum: 10c0/5ada4fd03e8077888a065bb03f2425501b8402e7cc26f0ffbb454feb61e3a825c8260252a5f768c25481866e798c5ff910f5953c4638ae238d1a14befced02b8
checksum: 10c0/84297d63a97bf8fc131039d0f600787ada8baaa38b744820f7d29da601bce9f70a14aa12da80843b2b49eb8660718576f441ee9952954efa16009d7ccebf0000
languageName: node
linkType: hard
@@ -2584,12 +2595,12 @@ __metadata:
linkType: hard
"socks@npm:^2.8.3":
version: 2.8.5
resolution: "socks@npm:2.8.5"
version: 2.8.6
resolution: "socks@npm:2.8.6"
dependencies:
ip-address: "npm:^9.0.5"
smart-buffer: "npm:^4.2.0"
checksum: 10c0/e427d0eb0451cfd04e20b9156ea8c0e9b5e38a8d70f21e55c30fbe4214eda37cfc25d782c63f9adc5fbdad6d062a0f127ef2cefc9a44b6fee2b9ea5d1ed10827
checksum: 10c0/15b95db4caa359c80bfa880ff3e58f3191b9ffa4313570e501a60ee7575f51e4be664a296f4ee5c2c40544da179db6140be53433ce41ec745f9d51f342557514
languageName: node
linkType: hard
@@ -2601,9 +2612,9 @@ __metadata:
linkType: hard
"source-map@npm:^0.7.3":
version: 0.7.4
resolution: "source-map@npm:0.7.4"
checksum: 10c0/dc0cf3768fe23c345ea8760487f8c97ef6fca8a73c83cd7c9bf2fde8bc2c34adb9c0824d6feb14bc4f9e37fb522e18af621543f1289038a66ac7586da29aa7dc
version: 0.7.6
resolution: "source-map@npm:0.7.6"
checksum: 10c0/59f6f05538539b274ba771d2e9e32f6c65451982510564438e048bc1352f019c6efcdc6dd07909b1968144941c14015c2c7d4369fb7c4d7d53ae769716dcc16c
languageName: node
linkType: hard
@@ -2801,7 +2812,7 @@ __metadata:
languageName: node
linkType: hard
"tslib@npm:^2, tslib@npm:^2.3.0":
"tslib@npm:^2, tslib@npm:^2.3.0, tslib@npm:^2.4.0":
version: 2.8.1
resolution: "tslib@npm:2.8.1"
checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62
@@ -2809,8 +2820,8 @@ __metadata:
linkType: hard
"typescript-plugin-css-modules@npm:^5":
version: 5.1.0
resolution: "typescript-plugin-css-modules@npm:5.1.0"
version: 5.2.0
resolution: "typescript-plugin-css-modules@npm:5.2.0"
dependencies:
"@types/postcss-modules-local-by-default": "npm:^4.0.2"
"@types/postcss-modules-scope": "npm:^3.0.4"
@@ -2830,7 +2841,10 @@ __metadata:
tsconfig-paths: "npm:^4.2.0"
peerDependencies:
typescript: ">=4.0.0"
checksum: 10c0/a578e9055d015e932e1d42c5329d80d44d7dcaca5990c1d32aed62ea3d7ba49424f18c60cc654e96d1865e509f929359a9e571330ea2f5a7be7c1dd79e25ead3
dependenciesMeta:
stylus:
optional: true
checksum: 10c0/7cd024f7145c0a29d9b78f2fb49c42cdf1747b50a43391f9993132ba42a727266f9b544fd868d905d5352e0a8676a19ae7a9aa56d516cc819c3ab39d66aa25e4
languageName: node
linkType: hard
@@ -2909,13 +2923,13 @@ __metadata:
linkType: hard
"vite@npm:^5.0.0 || ^6.0.0 || ^7.0.0-0, vite@npm:^7":
version: 7.0.3
resolution: "vite@npm:7.0.3"
version: 7.0.6
resolution: "vite@npm:7.0.6"
dependencies:
esbuild: "npm:^0.25.0"
fdir: "npm:^6.4.6"
fsevents: "npm:~2.3.3"
picomatch: "npm:^4.0.2"
picomatch: "npm:^4.0.3"
postcss: "npm:^8.5.6"
rollup: "npm:^4.40.0"
tinyglobby: "npm:^0.2.14"
@@ -2959,7 +2973,7 @@ __metadata:
optional: true
bin:
vite: bin/vite.js
checksum: 10c0/9173a1bf9fee82606f39c7b3e785638c1ac7f3473879fc88593f3a2093ed766140a08b845050fd790adb8a36e2cbc81b71401407da05d5dd747ae4f3adb89794
checksum: 10c0/3b14dfa661281b4843789884199ba2a9cca940a7666970036fe3fb1abff52b88e63e8be5ab419dd04d9f96c0415ee0f1e3ec8ebe357041648af7ccd8e348b6ad
languageName: node
linkType: hard
+3 -2
View File
@@ -1,7 +1,7 @@
{
server: {
serverPort: "${CLOUDBEAVER_WEB_SERVER_PORT:8978}",
secureCookies: "${CLOUDBEAVER_SECURE_COOKIES:true}",
forceHttps: "${CLOUDBEAVER_FORCE_HTTPS:false}",
contentRoot: "web",
driversLocation: "drivers",
@@ -10,6 +10,7 @@
rootURI: "${CLOUDBEAVER_ROOT_URI:/}",
serviceURI: "/api/",
supportedHosts: [],
productSettings: {
# Global properties
@@ -27,7 +28,7 @@
},
expireSessionAfterPeriod: "${CLOUDBEAVER_EXPIRE_SESSION_AFTER_PERIOD:1800000}",
bindSessionToIp: "${CLOUDBEAVER_BIND_SESSION_TO_IP:true}",
bindSessionToIp: "${CLOUDBEAVER_BIND_SESSION_TO_IP:disable}",
develMode: "${CLOUDBEAVER_DEVEL_MODE:false}",
enableSecurityManager: false,
+4
View File
@@ -7,6 +7,8 @@ mkdir -p ./cloudbeaver/web
cd ../../cloudbeaver/common-typescript
yarn install --immutable
cd ../common-react
yarn install --immutable
cd ../webapp
yarn install --immutable
@@ -19,6 +21,8 @@ fi
cd ../../../common-typescript
yarn test
cd ../common-react
yarn test
cd ../webapp
yarn test
+1 -1
View File
@@ -1,6 +1,6 @@
lefthook: |
cd webapp
yarn lefthook
yarn dlx --quiet lefthook
output: false
pre-commit:
commands:
@@ -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.81.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 1.0.83.qualifier
Bundle-Release-Date: 20250818
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.model</artifactId>
<version>1.0.81-SNAPSHOT</version>
<version>1.0.83-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -30,6 +30,7 @@ public interface DBWConstants {
String PERMISSION_PRIVATE_PROJECT_ACCESS = "private-project-access";
String PERMISSION_SECRET_MANAGER = "secret-manager";
String GLOBAL_PERMISSION_SCRIPT_EXECUTE = "permission.sql.script.execution";
String PERMISSION_EDIT_STRUCTURE = "edit-meta";
String PERMISSION_EDIT_DATA = "edit-data";
@@ -36,4 +36,6 @@ public interface DBWFeatureSet {
boolean isEnabled();
boolean isEnabledByDefault();
}
@@ -36,6 +36,7 @@ public abstract class BaseWebAppConfiguration implements ServletAppConfiguration
protected boolean secretManagerEnabled;
protected boolean showReadOnlyConnectionInfo;
protected String[] enabledFeatures;
protected String[] disabledFeatures;
protected String[] disabledBetaFeatures;
@@ -43,6 +44,7 @@ public abstract class BaseWebAppConfiguration implements ServletAppConfiguration
this.plugins = new LinkedHashMap<>();
this.resourceManagerEnabled = true;
this.enabledFeatures = null;
this.disabledFeatures = new String[0];
this.disabledBetaFeatures = new String[0];
this.showReadOnlyConnectionInfo = false;
this.secretManagerEnabled = false;
@@ -53,6 +55,7 @@ public abstract class BaseWebAppConfiguration implements ServletAppConfiguration
this.defaultUserTeam = src.defaultUserTeam;
this.resourceManagerEnabled = src.resourceManagerEnabled;
this.enabledFeatures = src.enabledFeatures;
this.disabledFeatures = src.disabledFeatures;
this.disabledBetaFeatures = src.disabledBetaFeatures;
this.showReadOnlyConnectionInfo = src.showReadOnlyConnectionInfo;
this.secretManagerEnabled = src.secretManagerEnabled;
@@ -122,6 +125,16 @@ public abstract class BaseWebAppConfiguration implements ServletAppConfiguration
this.enabledFeatures = enabledFeatures;
}
@NotNull
@Override
public String[] getDisabledFeatures() {
return disabledFeatures;
}
public void setDisabledFeatures(@NotNull String[] disabledFeatures) {
this.disabledFeatures = disabledFeatures;
}
public boolean isShowReadOnlyConnectionInfo() {
return showReadOnlyConnectionInfo;
}
@@ -51,6 +51,16 @@ public interface ServletAppConfiguration {
return new String[0];
}
/**
* Returns disabled features.
*
* @return array of disabled feature IDs
*/
@NotNull
default String[] getDisabledFeatures() {
return new String[0];
}
default boolean isSupportsCustomConnections() {
return true;
}
@@ -57,7 +57,6 @@ public class CBAppConfig extends BaseWebAppConfiguration implements ServletAuthC
private boolean anonymousAccessEnabled;
private boolean grantConnectionsAccessToAnonymousTeam;
private boolean systemVariablesResolvingEnabled;
private boolean apiCallLoggingEnabled;
@Deprecated
private String anonymousUserRole;
private String anonymousUserTeam;
@@ -91,7 +90,6 @@ public class CBAppConfig extends BaseWebAppConfiguration implements ServletAuthC
this.linkExternalCredentialsWithUser = true;
this.grantConnectionsAccessToAnonymousTeam = false;
this.systemVariablesResolvingEnabled = false;
this.apiCallLoggingEnabled = false;
}
public CBAppConfig(CBAppConfig src) {
@@ -116,7 +114,6 @@ public class CBAppConfig extends BaseWebAppConfiguration implements ServletAuthC
this.linkExternalCredentialsWithUser = src.linkExternalCredentialsWithUser;
this.grantConnectionsAccessToAnonymousTeam = src.grantConnectionsAccessToAnonymousTeam;
this.systemVariablesResolvingEnabled = src.systemVariablesResolvingEnabled;
this.apiCallLoggingEnabled = src.apiCallLoggingEnabled;
}
@Override
@@ -357,8 +354,4 @@ public class CBAppConfig extends BaseWebAppConfiguration implements ServletAuthC
authConfiguration.clear();
}
}
public boolean isApiCallLoggingEnabled() {
return apiCallLoggingEnabled;
}
}
@@ -21,10 +21,12 @@ package io.cloudbeaver.model.config;
*/
public class ConnectionBruteForceConfiguration {
private static final int DEFAULT_ERROR_ATTEMPTS_CHECK_MINUTES = 24 * 60;
private static final int BLOCK_TIME_IN_MINUTES = 60;
private static final int DEFAULT_MAX_CONNECT_ATTEMPTS = 5;
private boolean enabled = false;
private int maxFailedConnectAttempts = DEFAULT_MAX_CONNECT_ATTEMPTS;
private int errorAttemptsPeriodInMinutes = DEFAULT_ERROR_ATTEMPTS_CHECK_MINUTES;
private int blockTimeInMinutes = BLOCK_TIME_IN_MINUTES;
public boolean isEnabled() {
return enabled;
@@ -49,4 +51,12 @@ public class ConnectionBruteForceConfiguration {
public void setErrorAttemptsPeriodInMinutes(int errorAttemptsPeriodInMinutes) {
this.errorAttemptsPeriodInMinutes = errorAttemptsPeriodInMinutes;
}
public int getBlockTimeInMinutes() {
return blockTimeInMinutes;
}
public void setBlockTimeInMinutes(int blockTimeInMinutes) {
this.blockTimeInMinutes = blockTimeInMinutes;
}
}
@@ -1022,6 +1022,9 @@ public class WebSession extends BaseWebSession
}
}
public boolean hasGlobalPermission(String permissionId) {
return true;
}
private record PersistentAttribute(Object value) {
}
@@ -23,6 +23,7 @@ import org.eclipse.core.runtime.IConfigurationElement;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.DBPImage;
import org.jkiss.dbeaver.model.impl.AbstractContextDescriptor;
import org.jkiss.utils.CommonUtils;
/**
* WebFeatureDescriptor
@@ -35,6 +36,7 @@ public class WebFeatureDescriptor extends AbstractContextDescriptor implements D
private final String label;
private final String description;
private final DBPImage icon;
private final boolean enabledByDefault;
public WebFeatureDescriptor(IConfigurationElement config)
{
@@ -43,6 +45,7 @@ public class WebFeatureDescriptor extends AbstractContextDescriptor implements D
this.label = config.getAttribute("label");
this.description = config.getAttribute("description");
this.icon = iconToImage(config.getAttribute("icon"));
this.enabledByDefault = CommonUtils.getBoolean(config.getAttribute("enabledByDefault"), false);
}
@NotNull
@@ -69,4 +72,9 @@ public class WebFeatureDescriptor extends AbstractContextDescriptor implements D
return ServletAppUtils.getServletApplication().getAppConfiguration().isFeatureEnabled(this.id);
}
@Override
public boolean isEnabledByDefault() {
return enabledByDefault;
}
}
@@ -68,4 +68,9 @@ public class WebServerFeatureDescriptor extends AbstractContextDescriptor implem
return true;
}
@Override
public boolean isEnabledByDefault() {
return false;
}
}
@@ -32,7 +32,9 @@ public class CBConstants {
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_SECURE_COOKIES = "secureCookies";
public static final String PARAM_FORCE_HTTPS = "forceHttps";
public static final String PARAM_BIND_SESSION_TO_IP = "bindSessionToIp";
public static final String PARAM_SUPPORTED_HOSTS = "supportedHosts";
public static final String PARAM_SSL_CONFIGURATION_PATH = "sslConfigurationPath";
public static final String PARAM_CONTENT_ROOT = "contentRoot";
public static final String PARAM_SERVER_URL = "serverURL";
@@ -93,4 +95,7 @@ public class CBConstants {
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";
public static final String BIND_SESSION_ENABLE = "enable";
public static final String BIND_SESSION_DISABLE = "disable";
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,11 @@
package io.cloudbeaver.service;
import io.cloudbeaver.model.app.ServletApplication;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import java.util.List;
/**
* Servlet service
*/
@@ -28,4 +31,9 @@ public interface DBWServiceBindingServlet<APPLICATION extends ServletApplication
}
void addServlets(APPLICATION application, DBWServletContext servletContext) throws DBException;
@NotNull
default List<String> getExcludedServletPaths(@NotNull APPLICATION application) {
return List.of();
}
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@ import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.utils.CommonUtils;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.*;
@@ -47,6 +48,11 @@ public class ServletAppUtils {
private static final String HEADER_FORWARDED_SCHEME = "X-Forwarded-Scheme";
private static final String HEADER_FORWARDED_HOST = "X-Forwarded-Host";
private static final Set<Integer> DEFAULT_PORTS = Set.of(
80,
443
);
private static final Log log = Log.getLog(ServletAppUtils.class);
@@ -309,7 +315,26 @@ public class ServletAppUtils {
if (!origin.endsWith(rootUri)) {
origin = origin + "/" + rootUri + "/";
}
return removeSideSlashes(origin);
origin = removeSideSlashes(origin);
URI uri = URI.create(origin);
if (DEFAULT_PORTS.contains(uri.getPort())) {
try {
origin = new URI(
uri.getScheme(),
uri.getUserInfo(),
uri.getHost(),
-1,
uri.getPath(),
uri.getQuery(),
uri.getFragment()
).toString();
} catch (URISyntaxException e) {
log.error("Failed to create URI without port", e);
}
}
return origin;
}
public static String getRootUrlFromUri(@NotNull URI uri) {
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Community Product
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
Bundle-Version: 25.1.3.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 25.1.5.qualifier
Bundle-Release-Date: 20250818
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>25.1.3-SNAPSHOT</version>
<version>25.1.5-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.126.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 1.0.128.qualifier
Bundle-Release-Date: 20250818
Bundle-Vendor: DBeaver Corp
Bundle-ActivationPolicy: lazy
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
@@ -9,6 +9,6 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
<version>1.0.126-SNAPSHOT</version>
<version>1.0.128-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver CE Server
Bundle-SymbolicName: io.cloudbeaver.server.ce;singleton:=true
Bundle-Version: 25.1.3.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 25.1.5.qualifier
Bundle-Release-Date: 20250818
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>25.1.3-SNAPSHOT</version>
<version>25.1.5-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -1,5 +1,5 @@
extend type ServerConfig {
serverURL: String!
serverURL: String! @deprecated
rootURI: String!
hostName: String! @deprecated(reason: "use container id instead")
containerId: String!
@@ -12,5 +12,11 @@ extend type ServerConfig {
redirectOnFederatedAuth: Boolean!
enabledAuthProviders: [ID!]!
passwordPolicyConfiguration: PasswordPolicyConfig! @since(version: "23.3.3")
"Mark all cookies as secure, recommend for HTTPS deployment configurations"
forceHttps: Boolean! @since(version: "25.1.3")
"List of allowed hosts to use the application - if empty, all hosts are allowed"
supportedHosts: [String!]! @since(version: "25.1.3")
"Indicates whether the session should be linked to the user's IP address"
bindSessionToIp: String! @since(version: "25.1.4")
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,8 @@ import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.utils.CommonUtils;
import java.util.List;
public class CBWebServerConfig extends WebServerConfig {
private final CBApplication<?> cbApp;
@@ -86,4 +88,21 @@ public class CBWebServerConfig extends WebServerConfig {
public PasswordPolicyConfiguration getPasswordPolicyConfiguration() {
return cbApp.getServerConfiguration().getSecurityManagerConfiguration().getPasswordPolicyConfiguration();
}
@Property
public boolean isForceHttps() {
return cbApp.getServerConfiguration().isForceHttps();
}
@NotNull
@Property
public List<String> getSupportedHosts() {
return cbApp.getServerConfiguration().getSupportedHosts();
}
@NotNull
@Property
public String getBindSessionToIp() {
return cbApp.getServerConfiguration().getBindSessionToIp();
}
}
@@ -20,18 +20,21 @@ import com.google.gson.annotations.SerializedName;
import io.cloudbeaver.auth.CBAuthConstants;
import io.cloudbeaver.model.app.WebServerConfiguration;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.utils.ServletAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import java.util.HashMap;
import java.util.Map;
import java.net.URI;
import java.util.*;
public class CBServerConfig implements WebServerConfiguration {
private static final Log log = Log.getLog(CBServerConfig.class);
protected String serverURL;
protected boolean secureCookies;
@NotNull
protected List<String> supportedHosts = new ArrayList<>();
protected boolean forceHttps;
protected int serverPort = CBConstants.DEFAULT_SERVER_PORT;
private String serverHost = null;
private String serverName = null;
@@ -52,7 +55,8 @@ public class CBServerConfig implements WebServerConfiguration {
@SerializedName("database")
private WebDatabaseConfig databaseConfiguration = new WebDatabaseConfig();
private String staticContent = "";
private boolean bindSessionToIp = true;
@NotNull
private String bindSessionToIp = CBConstants.BIND_SESSION_DISABLE;
public CBServerConfig() {
this.securityManagerConfiguration = createSecurityManagerConfiguration();
@@ -184,11 +188,48 @@ public class CBServerConfig implements WebServerConfiguration {
return new SMControllerConfiguration();
}
public boolean isSecureCookies() {
return secureCookies;
public boolean isForceHttps() {
return forceHttps;
}
public boolean isBindSessionToIp() {
public void setForceHttps(boolean forceHttps) {
this.forceHttps = forceHttps;
}
@NotNull
public List<String> getSupportedHosts() {
return new ArrayList<>(supportedHosts);
}
public void setSupportedHosts(@NotNull Collection<String> availableHosts) {
LinkedHashSet<String> uniqueHosts = new LinkedHashSet<>();
for (String host : availableHosts) {
try {
if (!host.startsWith("http://") && !host.startsWith("https://")) {
host = "http://" + host; // Default to HTTP if no scheme is provided to avoid uri parse exception
}
URI uri = URI.create(host);
StringBuilder hostNameBuilder = new StringBuilder();
hostNameBuilder.append(ServletAppUtils.removeSideSlashes(uri.getHost() != null ? uri.getHost() : host));
if (uri.getPort() > 0) {
hostNameBuilder.append(':')
.append(uri.getPort());
}
uniqueHosts.add(hostNameBuilder.toString());
} catch (Exception e) {
log.error("Invalid host URI: " + host, e);
}
}
this.supportedHosts.clear();
this.supportedHosts.addAll(uniqueHosts);
}
@NotNull
public String getBindSessionToIp() {
return bindSessionToIp;
}
public void setBindSessionToIp(@NotNull String bindSessionToIp) {
this.bindSessionToIp = bindSessionToIp;
}
}
@@ -27,6 +27,7 @@ import io.cloudbeaver.model.app.ServletSystemInformationCollector;
import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.registry.WebDriverRegistry;
import io.cloudbeaver.registry.WebFeatureRegistry;
import io.cloudbeaver.registry.WebServiceRegistry;
import io.cloudbeaver.server.jetty.CBJettyServer;
import io.cloudbeaver.service.ConnectionController;
@@ -214,7 +215,12 @@ public abstract class CBApplication<T extends CBServerConfig>
configurationMode = CommonUtils.isEmpty(getServerConfiguration().getServerName());
refreshDisabledDriversConfig();
try {
refreshServerConfiguration();
} catch (DBException e) {
log.error("Error refreshing server configuration", e);
return;
}
eventController.setForceSkipEvents(isConfigurationMode()); // do not send events if configuration mode is on
@@ -331,6 +337,25 @@ public abstract class CBApplication<T extends CBServerConfig>
log.debug("Shutdown");
}
private void refreshServerConfiguration() throws DBException {
refreshDisabledDriversConfig();
refreshEnabledFeatures();
if (!isConfigurationMode()) {
flushConfiguration();
}
}
private void refreshEnabledFeatures() {
Set<String> enabledFeatures = new LinkedHashSet<>(Arrays.asList(getAppConfiguration().getEnabledFeatures()));
Set<String> disabledFeatures = new LinkedHashSet<>(Arrays.asList(getAppConfiguration().getDisabledFeatures()));
WebFeatureRegistry.getInstance().getWebFeatures().stream()
.filter(f -> f.isEnabledByDefault() && !disabledFeatures.contains(f.getId()))
.forEach(f -> enabledFeatures.add(f.getId()));
getAppConfiguration().setEnabledFeatures(enabledFeatures.toArray(new String[0]));
}
protected ServletSystemInformationCollector<?> createSystemInformationCollector() {
return new ServletSystemInformationCollector<>(this);
}
@@ -93,6 +93,7 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
log.debug("Runtime configuration [" + runtimeConfigPath.toAbsolutePath() + "]");
loadConfiguration(runtimeConfigPath);
}
// Set default preferences
PrefUtils.setDefaultPreferenceValue(DBWorkbench.getPlatform().getPreferenceStore(),
ModelPreferences.UI_DRIVERS_HOME,
@@ -107,7 +108,8 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
return CommonUtils.nullIfEmpty(envValue);
}
public void loadConfiguration(Path configPath) throws DBException {
@NotNull
public Map<String, Object> loadConfiguration(Path configPath) throws DBException {
CBAppConfig prevConfig = new CBAppConfig(appConfiguration);
Map<String, Object> configProps = readConfiguration(configPath);
try {
@@ -123,6 +125,7 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
mergeOldConfiguration(prevConfig);
patchConfigurationWithProperties(getServerConfiguration().getProductSettings());
return configProps;
}
protected void parseConfiguration(Map<String, Object> configProps) throws DBException {
@@ -531,8 +534,20 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
copyConfigValue(
originServerConfig,
serverConfigProperties,
CBConstants.PARAM_SECURE_COOKIES,
serverConfig.isSecureCookies()
CBConstants.PARAM_FORCE_HTTPS,
serverConfig.isForceHttps()
);
copyConfigValue(
originServerConfig,
serverConfigProperties,
CBConstants.PARAM_SUPPORTED_HOSTS,
serverConfig.getSupportedHosts()
);
copyConfigValue(
originServerConfig,
serverConfigProperties,
CBConstants.PARAM_BIND_SESSION_TO_IP,
serverConfig.getBindSessionToIp()
);
var productConfigProperties = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
Map<String, Object> oldProductRuntimeConfig = JSONUtils.getObject(originServerConfig,
@@ -22,11 +22,26 @@ import org.jkiss.dbeaver.model.websocket.WSEventHandler;
import org.jkiss.dbeaver.model.websocket.event.WSAbstractEvent;
import org.jkiss.dbeaver.model.websocket.event.WSUserCloseSessionsEvent;
import org.jkiss.dbeaver.model.websocket.event.WSUserDeletedEvent;
import org.jkiss.dbeaver.model.websocket.event.WSUserDisabledEvent;
public class WSUserEventHandler<EVENT extends WSAbstractEvent> implements WSEventHandler<EVENT> {
@Override
public void handleEvent(@NotNull EVENT event) {
var sessionManager = CBApplication.getInstance().getSessionManager();
switch (event) {
case WSUserCloseSessionsEvent closeSessionsEvent -> {
if (closeSessionsEvent.getSessionIds().isEmpty()) {
sessionManager.closeAllSessions(closeSessionsEvent.getSessionId());
} else {
sessionManager.closeSessions(closeSessionsEvent.getSessionIds());
}
}
case WSUserDeletedEvent e -> sessionManager.closeUserSession(e);
case WSUserDisabledEvent e -> sessionManager.closeUserSession(e);
default -> { }
}
switch (event.getId()) {
case WSUserCloseSessionsEvent.ID:
if (event instanceof WSUserCloseSessionsEvent closeSessionsEvent) {
@@ -42,6 +57,11 @@ public class WSUserEventHandler<EVENT extends WSAbstractEvent> implements WSEven
sessionManager.closeUserSession(userDeletedEvent);
}
break;
case WSUserDisabledEvent.ID:
if (event instanceof WSUserDisabledEvent userDisabledEvent) {
sessionManager.closeUserSession(userDisabledEvent);
}
break;
default:
break;
}
@@ -20,8 +20,8 @@ import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.registry.WebServiceRegistry;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
import io.cloudbeaver.server.filters.ServerConfigurationTimeLimitFilter;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
import io.cloudbeaver.server.servlets.CBImageServlet;
import io.cloudbeaver.server.servlets.CBStaticServlet;
import io.cloudbeaver.server.servlets.WebStatusServlet;
@@ -30,10 +30,7 @@ import io.cloudbeaver.server.websockets.CBWebSocketServerConfigurator;
import io.cloudbeaver.service.DBWServiceBindingServlet;
import io.cloudbeaver.service.DBWServiceBindingWebSocket;
import jakarta.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.ServletMapping;
import org.eclipse.jetty.ee10.servlet.*;
import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.util.resource.ResourceFactory;
@@ -48,6 +45,8 @@ import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class CBJettyServer {
@@ -92,6 +91,7 @@ public class CBJettyServer {
}
{
// Handler configuration
Path contentRootPath = Path.of(serverConfiguration.getContentRoot());
ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
@@ -99,6 +99,7 @@ public class CBJettyServer {
String rootURI = serverConfiguration.getRootURI();
servletContextHandler.setContextPath(rootURI);
ServletHolder staticServletHolder = new ServletHolder(
"static", new CBStaticServlet(Path.of(serverConfiguration.getContentRoot()))
);
@@ -126,7 +127,7 @@ public class CBJettyServer {
servletContextHandler.addEventListener(new CBServerContextListener(application));
// Add extensions from services
Set<String> excludedFilterPaths = new HashSet<>();
CBJettyServletContext servletContext = new CBJettyServletContext(servletContextHandler);
for (DBWServiceBindingServlet wsd : WebServiceRegistry.getInstance()
.getWebServices(DBWServiceBindingServlet.class)
@@ -134,6 +135,7 @@ public class CBJettyServer {
if (wsd.isApplicable(this.application)) {
try {
wsd.addServlets(this.application, servletContext);
excludedFilterPaths.addAll(wsd.getExcludedServletPaths(this.application));
} catch (DBException e) {
log.error(e.getMessage(), e);
}
@@ -152,6 +154,9 @@ public class CBJettyServer {
}
}
}
FilterHolder hostsFilter = new FilterHolder(new RequestHostFilter(application, excludedFilterPaths));
servletContextHandler.addFilter(hostsFilter, "/*", null);
JakartaWebSocketServletContainerInitializer.configure(servletContextHandler, (context, container) -> {
// Add echo endpoint to server container
@@ -231,7 +236,7 @@ public class CBJettyServer {
&& servletContextHandler.getSessionHandler() instanceof CBSessionHandler cbSessionHandler
) {
cbSessionHandler.setMaxCookieAge((int) (application.getMaxSessionIdleTime() / 1000));
cbSessionHandler.setSecureCookies(application.getServerConfiguration().isSecureCookies());
cbSessionHandler.setSecureCookies(application.getServerConfiguration().isForceHttps());
}
}
}
@@ -0,0 +1,146 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.server.jetty;
import io.cloudbeaver.model.config.CBServerConfig;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.utils.ServletAppUtils;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.utils.CommonUtils;
import java.io.IOException;
import java.net.URI;
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);
@NotNull
private final CBApplication<?> application;
private final Set<String> excludedPaths = new HashSet<>();
public RequestHostFilter(@NotNull CBApplication<?> application, @NotNull Set<String> excludedPaths) {
this.application = application;
this.excludedPaths.addAll(
excludedPaths.stream()
.map(path -> ServletAppUtils.removeSideSlashes(path.replace("*", "")))
.toList()
);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest httpRequest) {
CBServerConfig serverConfig = application.getServerConfiguration();
URI originUri;
try {
String origin = ServletAppUtils.getOriginFromRequestOrThrow(httpRequest);
originUri = URI.create(origin);
} catch (Exception e) {
log.error("Failed to get origin from request", e);
chain.doFilter(request, response);
return;
}
String servletPath = httpRequest.getServletPath();
if (CommonUtils.isNotEmpty(servletPath)) {
for (String excludedPath : excludedPaths) {
if (servletPath.contains(excludedPath)) {
chain.doFilter(request, response);
return;
}
}
}
validateHosts(serverConfig, httpRequest, response, originUri);
validateSchema(serverConfig, httpRequest, response, originUri);
}
chain.doFilter(request, response);
}
private void validateSchema(CBServerConfig serverConfig, HttpServletRequest httpRequest, ServletResponse response, URI originUri) {
boolean httpsExpected = serverConfig.isForceHttps();
try {
if ("http".equals(originUri.getScheme()) && httpsExpected) {
log.warn("Request schema is 'http' but 'forceHttps' is enabled. Redirecting to 'https'.");
StringBuilder redirectUrlBuilder = new StringBuilder("https://")
.append(originUri.getHost());
if (originUri.getPort() > -1) {
redirectUrlBuilder.append(':').append(originUri.getPort());
}
redirectUrlBuilder.append(httpRequest.getRequestURI());
if (httpRequest.getQueryString() != null) {
redirectUrlBuilder.append("?")
.append(httpRequest.getQueryString());
}
((HttpServletResponse) response).sendRedirect(redirectUrlBuilder.toString());
}
} catch (Exception e) {
log.error("Failed to redirect to HTTPS", e);
}
}
private void validateHosts(
@NotNull CBServerConfig serverConfig,
@NotNull HttpServletRequest httpRequest,
@NotNull ServletResponse response,
URI originUri
) throws IOException {
List<String> availableHosts = serverConfig.getSupportedHosts();
if (CommonUtils.isEmpty(availableHosts)) {
return;
}
try {
var requestHostBuilder = new StringBuilder(originUri.getHost());
if (originUri.getPort() > -1) {
requestHostBuilder.append(':').append(originUri.getPort());
}
String requestHost = requestHostBuilder.toString();
if (!availableHosts.contains(requestHost)) {
log.warn("Request host '" + requestHost + "' is not allowed. Redirect to default: " + availableHosts);
redirectToDefaultHost((HttpServletResponse) response, httpRequest, availableHosts);
}
} catch (Throwable e) {
log.error(e.getMessage(), e);
redirectToDefaultHost((HttpServletResponse) response, httpRequest, availableHosts);
}
}
private void redirectToDefaultHost(
@NotNull HttpServletResponse response,
@NotNull HttpServletRequest httpRequest,
@NotNull List<String> availableHosts
) throws IOException {
boolean https = application.getServerConfiguration().isForceHttps();
String redirectUrl = (https ? "https://" : "http://") + getDefaultHost(availableHosts) + httpRequest.getRequestURI();
if (httpRequest.getQueryString() != null) {
redirectUrl += "?" + httpRequest.getQueryString();
}
response.sendRedirect(redirectUrl);
}
@NotNull
private String getDefaultHost(@NotNull List<String> availableHosts) {
return availableHosts.getFirst();
}
}
@@ -22,6 +22,7 @@ import io.cloudbeaver.model.session.*;
import io.cloudbeaver.registry.WebHandlerRegistry;
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;
@@ -34,7 +35,9 @@ import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.auth.SMAuthInfo;
import org.jkiss.dbeaver.model.security.user.SMAuthPermissions;
import org.jkiss.dbeaver.model.websocket.event.WSAbstractEvent;
import org.jkiss.dbeaver.model.websocket.event.WSUserDeletedEvent;
import org.jkiss.dbeaver.model.websocket.event.WSUserDisabledEvent;
import org.jkiss.dbeaver.model.websocket.event.session.WSSessionStateEvent;
import org.jkiss.utils.CommonUtils;
@@ -156,8 +159,15 @@ public class CBSessionManager implements WebAppSessionManager {
}
}
validateSessionIp(request, webSession);
return webSession;
}
private void validateSessionIp(@NotNull HttpServletRequest request, WebSession webSession) {
boolean bindingEnabled = isSessionBindingEnabled(request);
String currentRemote = request.getRemoteAddr();
if (application.getServerConfiguration().isBindSessionToIp()
if (bindingEnabled
&& (CommonUtils.isEmpty(currentRemote) || !currentRemote.equals(webSession.getLastRemoteAddr()))
) {
var error = new DBWebException(
@@ -169,8 +179,11 @@ public class CBSessionManager implements WebAppSessionManager {
webSession.addSessionError(error);
closeSession(webSession.getSessionId());
}
}
return webSession;
protected boolean isSessionBindingEnabled(@NotNull HttpServletRequest request) {
String bindingState = application.getServerConfiguration().getBindSessionToIp();
return CBConstants.BIND_SESSION_ENABLE.equalsIgnoreCase(bindingState) || Boolean.parseBoolean(bindingState);
}
@NotNull
@@ -391,14 +404,14 @@ public class CBSessionManager implements WebAppSessionManager {
}
}
public void closeUserSession(@NotNull WSUserDeletedEvent userDeletedEvent) {
public void closeUserSession(@NotNull WSAbstractEvent event) {
synchronized (sessionMap) {
for (Iterator<BaseWebSession> iterator = sessionMap.values().iterator(); iterator.hasNext(); ) {
var session = iterator.next();
if (CommonUtils.equalObjects(session.getUserContext().getUserId(),
userDeletedEvent.getDeletedUserId())) {
event.getUserId())) {
if (session instanceof WebHeadlessSession headlessSession) {
headlessSession.addSessionEvent(userDeletedEvent);
headlessSession.addSessionEvent(event);
}
iterator.remove();
session.close();
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Server
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
Bundle-Version: 25.1.3.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 25.1.5.qualifier
Bundle-Release-Date: 20250818
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
@@ -1,2 +1,2 @@
log.api.graphql.debug = Enable detailed logging
log.api.graphql.debug = Log GraphQL requests to console
log.api.graphql.debug.description = Enable detailed logging of GraphQL queries in the server log, including all provided variables
@@ -1,2 +1,2 @@
log.api.graphql.debug = Включить подробное логирование
log.api.graphql.debug =Логировать GraphQL-запросы в консоль
log.api.graphql.debug.description = Включите подробное логирование запросов GraphQL в журнале сервера, включая все предоставленные переменные
@@ -64,9 +64,9 @@
<extension point="org.jkiss.dbeaver.settings">
<propertyGroup id="log" label="Logger">
<property id="log.api.graphql.debug" label="Enable detailed logging"
<property id="log.api.graphql.debug" label="%log.api.graphql.debug"
type="boolean" scopes="global"
description="Enable detailed logging of GraphQL queries in the server log, including all provided variables"/>
description="%log.api.graphql.debug.description"/>
</propertyGroup>
</extension>
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.server</artifactId>
<version>25.1.3-SNAPSHOT</version>
<version>25.1.5-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-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,4 +35,6 @@ public @interface WebAction {
boolean initializationRequired() default true;
String[] requireGlobalPermissions() default {};
}
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,22 +18,23 @@ package io.cloudbeaver.model;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.impl.PropertyDescriptor;
import org.jkiss.dbeaver.model.impl.PropertyGroupDescriptor;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.registry.settings.ProductSettingDescriptor;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class WebProductSettings {
public class WebGroupPropertiesInfo<T extends PropertyDescriptor> {
@NotNull
private final WebSession webSession;
@NotNull
private final List<PropertyGroupDescriptor<ProductSettingDescriptor>> groups;
private final Collection<PropertyGroupDescriptor<T>> groups;
public WebProductSettings(
public WebGroupPropertiesInfo(
@NotNull WebSession webSession,
@NotNull List<PropertyGroupDescriptor<ProductSettingDescriptor>> groups
@NotNull Collection<PropertyGroupDescriptor<T>> groups
) {
this.webSession = webSession;
this.groups = groups;
@@ -41,7 +42,7 @@ public class WebProductSettings {
@NotNull
@Property
public List<WebSettingsGroupInfo> getGroups() {
public List<WebSettingsGroupInfo<T>> getGroups() {
return groups.stream()
.map(WebSettingsGroupInfo::new)
.collect(Collectors.toList());
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,15 +17,15 @@
package io.cloudbeaver.model;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.impl.PropertyDescriptor;
import org.jkiss.dbeaver.model.impl.PropertyGroupDescriptor;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.registry.settings.ProductSettingDescriptor;
public class WebSettingsGroupInfo {
public class WebSettingsGroupInfo<T extends PropertyDescriptor> {
@NotNull
private final PropertyGroupDescriptor<ProductSettingDescriptor> groupDescriptor;
private final PropertyGroupDescriptor<T> groupDescriptor;
public WebSettingsGroupInfo(@NotNull PropertyGroupDescriptor<ProductSettingDescriptor> groupDescriptor) {
public WebSettingsGroupInfo(@NotNull PropertyGroupDescriptor<T> groupDescriptor) {
this.groupDescriptor = groupDescriptor;
}
@@ -33,7 +33,7 @@ public class WebObjectFeatureRegistry {
private final List<WebObjectFeatureProviderDescriptor> providers = new ArrayList<>();
public static WebObjectFeatureRegistry getInstance() {
public static synchronized WebObjectFeatureRegistry getInstance() {
if (INSTANCE == null) {
INSTANCE = new WebObjectFeatureRegistry();
INSTANCE.loadExtensions(Platform.getExtensionRegistry());
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -265,7 +265,8 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
throw new DBWebExceptionServerNotInitialized(message);
}
String[] reqPermissions = webAction.requirePermissions();
if (reqPermissions.length == 0 && !webAction.authRequired()) {
String[] reqGlobalPermissions = webAction.requireGlobalPermissions();
if (reqPermissions.length == 0 && reqGlobalPermissions.length == 0 && !webAction.authRequired()) {
return;
}
WebSession session = findWebSession(env);
@@ -297,6 +298,13 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
throw new DBWebExceptionAccessDenied("Access denied");
}
}
// Check permissions
for (String gp : reqGlobalPermissions) {
if (!session.hasGlobalPermission(gp)) {
log.debug("Access to " + method.getName() + " denied for " + session.getUser());
throw new DBWebExceptionAccessDenied("Access denied");
}
}
}
}
@@ -29,6 +29,7 @@ import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
import org.jkiss.dbeaver.model.rm.RMConstants;
import org.jkiss.dbeaver.registry.settings.ProductSettingDescriptor;
import java.util.List;
import java.util.Map;
@@ -48,7 +49,7 @@ public interface DBWServiceCore extends DBWService {
WebPropertyInfo[] getSystemInformationProperties(@NotNull WebSession webSession);
@WebAction
WebProductSettings getProductSettings(@NotNull WebSession webSession);
WebGroupPropertiesInfo<ProductSettingDescriptor> getProductSettings(@NotNull WebSession webSession);
@WebAction
List<WebDatabaseDriverInfo> getDriverList(@NotNull WebSession webSession, String driverId) throws DBWebException;
@@ -54,6 +54,7 @@ import org.jkiss.dbeaver.registry.DataSourceDescriptor;
import org.jkiss.dbeaver.registry.DataSourceProviderRegistry;
import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor;
import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
import org.jkiss.dbeaver.registry.settings.ProductSettingDescriptor;
import org.jkiss.dbeaver.registry.settings.ProductSettingsRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.utils.CommonUtils;
@@ -644,8 +645,8 @@ public class WebServiceCore implements DBWServiceCore {
}
@Override
public WebProductSettings getProductSettings(@NotNull WebSession webSession) {
return new WebProductSettings(webSession, ProductSettingsRegistry.getInstance().getSettings());
public WebGroupPropertiesInfo<ProductSettingDescriptor> getProductSettings(@NotNull WebSession webSession) {
return new WebGroupPropertiesInfo<>(webSession, ProductSettingsRegistry.getInstance().getSettings());
}
private WebSessionProjectImpl getProjectById(WebSession webSession, String projectId) throws DBWebException {
@@ -16,6 +16,7 @@
*/
package io.cloudbeaver.service.sql;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebAction;
import io.cloudbeaver.model.WebAsyncTaskInfo;
@@ -90,7 +91,7 @@ public interface DBWServiceSQL extends DBWService {
@WebAction
void setContextDefaults(@NotNull WebSQLContextInfo sqlContext, String catalogName, String schemaName) throws DBWebException;
@WebAction
@WebAction(requireGlobalPermissions = DBWConstants.GLOBAL_PERMISSION_SCRIPT_EXECUTE)
WebAsyncTaskInfo asyncExecuteQuery(
@NotNull WebSQLContextInfo contextInfo,
@NotNull String sql,
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Administration
Bundle-SymbolicName: io.cloudbeaver.service.admin;singleton:=true
Bundle-Version: 1.0.125.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 1.0.127.qualifier
Bundle-Release-Date: 20250818
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.admin</artifactId>
<version>1.0.125-SNAPSHOT</version>
<version>1.0.127-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -108,7 +108,7 @@ type WebFeatureSet {
input ServerConfigInput {
serverName: String
serverURL: String
serverURL: String @deprecated
adminName: String
adminPassword: String
@@ -126,6 +126,9 @@ input ServerConfigInput {
disabledDrivers: [ID!]
sessionExpireTime: Int
forceHttps: Boolean @since(version: "25.1.3")
supportedHosts: [String!] @since(version: "25.1.3")
bindSessionToIp: String @since(version: "25.1.4")
}
input AdminUserFilterInput {
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,9 @@ package io.cloudbeaver.service.admin;
import io.cloudbeaver.model.config.CBAppConfig;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBConstants;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import java.util.Arrays;
@@ -28,6 +31,7 @@ import java.util.Map;
* Server configuration for admin API
*/
public class AdminServerConfig {
private static final Log log = Log.getLog(AdminServerConfig.class);
private String serverName;
private String serverURL;
@@ -45,8 +49,14 @@ public class AdminServerConfig {
private final List<String> enabledAuthProviders;
private final String[] enabledDrivers;
private final String[] disabledDrivers;
@Nullable
private final Boolean forceHttps;
@Nullable
private final List<String> supportedHosts;
private long sessionExpireTime;
@Nullable
private String bindSessionToIp;
public AdminServerConfig(Map<String, Object> params) {
this.serverName = JSONUtils.getString(params, "serverName");
@@ -57,8 +67,16 @@ public class AdminServerConfig {
CBAppConfig appConfig = CBApplication.getInstance().getAppConfiguration();
this.anonymousAccessEnabled = JSONUtils.getBoolean(params, "anonymousAccessEnabled", appConfig.isAnonymousAccessEnabled());
this.customConnectionsEnabled = JSONUtils.getBoolean(params, "customConnectionsEnabled", appConfig.isSupportsCustomConnections());
this.publicCredentialsSaveEnabled = JSONUtils.getBoolean(params, "publicCredentialsSaveEnabled", appConfig.isPublicCredentialsSaveEnabled());
this.adminCredentialsSaveEnabled = JSONUtils.getBoolean(params, "adminCredentialsSaveEnabled", appConfig.isAdminCredentialsSaveEnabled());
this.publicCredentialsSaveEnabled = JSONUtils.getBoolean(
params,
"publicCredentialsSaveEnabled",
appConfig.isPublicCredentialsSaveEnabled()
);
this.adminCredentialsSaveEnabled = JSONUtils.getBoolean(
params,
"adminCredentialsSaveEnabled",
appConfig.isAdminCredentialsSaveEnabled()
);
this.resourceManagerEnabled = JSONUtils.getBoolean(params, "resourceManagerEnabled", appConfig.isResourceManagerEnabled());
this.secretManagerEnabled = JSONUtils.getBoolean(params, "secretManagerEnabled", appConfig.isSecretManagerEnabled());
@@ -87,6 +105,24 @@ public class AdminServerConfig {
} else {
this.disabledDrivers = appConfig.getDisabledDrivers();
}
if (params.containsKey(CBConstants.PARAM_FORCE_HTTPS)) {
this.forceHttps = JSONUtils.getBoolean(params, CBConstants.PARAM_FORCE_HTTPS);
} else {
this.forceHttps = null;
}
if (params.containsKey(CBConstants.PARAM_SUPPORTED_HOSTS)) {
this.supportedHosts = JSONUtils.getStringList(params, CBConstants.PARAM_SUPPORTED_HOSTS);
} else {
this.supportedHosts = null;
}
if (params.containsKey("bindSessionToIp")) {
this.bindSessionToIp = JSONUtils.getString(params, "bindSessionToIp");
} else {
this.bindSessionToIp = CBConstants.BIND_SESSION_DISABLE;
}
}
public String getServerName() {
@@ -168,4 +204,19 @@ public class AdminServerConfig {
public boolean isSecretManagerEnabled() {
return secretManagerEnabled;
}
@Nullable
public List<String> getSupportedHosts() {
return supportedHosts;
}
@Nullable
public Boolean getForceHttps() {
return forceHttps;
}
@Nullable
public String getBindSessionToIp() {
return bindSessionToIp;
}
}
@@ -584,7 +584,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
appConfig.setSupportsCustomConnections(config.isCustomConnectionsEnabled());
appConfig.setPublicCredentialsSaveEnabled(config.isPublicCredentialsSaveEnabled());
appConfig.setAdminCredentialsSaveEnabled(config.isAdminCredentialsSaveEnabled());
appConfig.setEnabledFeatures(config.getEnabledFeatures().toArray(new String[0]));
updateDisabledFeaturesConfig(appConfig, config.getEnabledFeatures());
// custom logic for enabling embedded drivers
updateDisabledDriversConfig(appConfig, config.getDisabledDrivers());
appConfig.setResourceManagerEnabled(config.isResourceManagerEnabled());
@@ -605,6 +605,15 @@ public class WebServiceAdmin implements DBWServiceAdmin {
serverConfig.setServerName(config.getServerName());
serverConfig.setServerURL(config.getServerURL());
serverConfig.setMaxSessionIdleTime(config.getSessionExpireTime());
if (config.getForceHttps() != null) {
serverConfig.setForceHttps(config.getForceHttps());
}
if (config.getSupportedHosts() != null) {
serverConfig.setSupportedHosts(config.getSupportedHosts());
}
if (config.getBindSessionToIp() != null) {
serverConfig.setBindSessionToIp(config.getBindSessionToIp());
}
}
if (CommonUtils.isEmpty(adminName)) {
@@ -665,6 +674,15 @@ public class WebServiceAdmin implements DBWServiceAdmin {
return true;
}
private void updateDisabledFeaturesConfig(CBAppConfig appConfig, List<String> enabledFeatures) {
Set<String> enabledIds = new LinkedHashSet<>(enabledFeatures);
appConfig.setEnabledFeatures(enabledFeatures.toArray(new String[0]));
String[] disabledFeatures = WebFeatureRegistry.getInstance().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(CBAppConfig appConfig, String[] disabledDriversConfig) {
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Authentication
Bundle-SymbolicName: io.cloudbeaver.service.auth;singleton:=true
Bundle-Version: 1.0.125.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 1.0.127.qualifier
Bundle-Release-Date: 20250818
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.auth</artifactId>
<version>1.0.125-SNAPSHOT</version>
<version>1.0.127-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Data Transfer
Bundle-SymbolicName: io.cloudbeaver.service.data.transfer;singleton:=true
Bundle-Version: 1.0.126.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 1.0.128.qualifier
Bundle-Release-Date: 20250818
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.data.transfer</artifactId>
<version>1.0.126-SNAPSHOT</version>
<version>1.0.128-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - File System
Bundle-SymbolicName: io.cloudbeaver.service.fs;singleton:=true
Bundle-Version: 1.0.43.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 1.0.45.qualifier
Bundle-Release-Date: 20250818
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.fs</artifactId>
<version>1.0.43-SNAPSHOT</version>
<version>1.0.45-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Name: Cloudbeaver Web Service - LDAP
Bundle-Vendor: DBeaver Corp
Bundle-SymbolicName: io.cloudbeaver.service.ldap.auth;singleton:=true
Bundle-Version: 1.0.8.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 1.0.10.qualifier
Bundle-Release-Date: 20250818
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.model;visibility:=reexport,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.ldap.auth</artifactId>
<version>1.0.8-SNAPSHOT</version>
<version>1.0.10-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Metadata
Bundle-SymbolicName: io.cloudbeaver.service.metadata;singleton:=true
Bundle-Version: 1.0.129.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 1.0.131.qualifier
Bundle-Release-Date: 20250818
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.metadata</artifactId>
<version>1.0.129-SNAPSHOT</version>
<version>1.0.131-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Resource manager NIO implementation
Bundle-SymbolicName: io.cloudbeaver.service.rm.nio;singleton:=true
Bundle-Version: 1.0.43.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 1.0.45.qualifier
Bundle-Release-Date: 20250818
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.model,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.rm.nio</artifactId>
<version>1.0.43-SNAPSHOT</version>
<version>1.0.45-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Resource manager
Bundle-SymbolicName: io.cloudbeaver.service.rm;singleton:=true
Bundle-Version: 1.0.78.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 1.0.80.qualifier
Bundle-Release-Date: 20250818
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.rm</artifactId>
<version>1.0.78-SNAPSHOT</version>
<version>1.0.80-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Name: Cloudbeaver Web Service - Security
Bundle-Vendor: DBeaver Corp
Bundle-SymbolicName: io.cloudbeaver.service.security;singleton:=true
Bundle-Version: 1.0.81.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 1.0.83.qualifier
Bundle-Release-Date: 20250818
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.model;visibility:=reexport,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.security</artifactId>
<version>1.0.81-SNAPSHOT</version>
<version>1.0.83-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -54,6 +54,7 @@ import org.jkiss.dbeaver.model.security.user.*;
import org.jkiss.dbeaver.model.sql.SQLUtils;
import org.jkiss.dbeaver.model.websocket.event.WSUserCloseSessionsEvent;
import org.jkiss.dbeaver.model.websocket.event.WSUserDeletedEvent;
import org.jkiss.dbeaver.model.websocket.event.WSUserDisabledEvent;
import org.jkiss.dbeaver.model.websocket.event.permissions.WSObjectPermissionEvent;
import org.jkiss.dbeaver.model.websocket.event.permissions.WSSubjectPermissionEvent;
import org.jkiss.dbeaver.model.websocket.event.session.WSAuthEvent;
@@ -411,7 +412,14 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
@NotNull Connection dbCon,
@NotNull String userId,
@NotNull String[] teamIds
) throws SQLException {
) throws SQLException, DBCException {
String defaultTeam = getDefaultUserTeam();
if (ArrayUtils.contains(teamIds, defaultTeam)) {
throw new SMException("Cannot delete default user team: " + defaultTeam);
}
if (ArrayUtils.isEmpty(teamIds)) {
return;
}
String deleteUserTeamsSql = "DELETE FROM {table_prefix}CB_USER_TEAM WHERE USER_ID=? " +
"AND TEAM_ID IN (" + SQLUtils.generateParamList(teamIds.length) + ")";
@@ -819,6 +827,10 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
dbStat.setString(5, userId);
dbStat.executeUpdate();
}
if (!enabled) {
var event = new WSUserDisabledEvent(userId);
application.getEventController().addEvent(event);
}
log.info(String.format("User updated: [userId=%s, isActive=%s, reason=%s]", userId, enabled, disableReason));
}
@@ -1433,9 +1445,14 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
addSubjectPermissionsUpdateEvent(subjectId, null);
}
public void initialize() throws DBException {
}
private void insertPermissions(Connection dbCon, String subjectId, String[] permissionIds, String grantorId) throws SQLException {
if (!ArrayUtils.isEmpty(permissionIds)) {
try (PreparedStatement dbStat = dbCon.prepareStatement(
try (
PreparedStatement dbStat = dbCon.prepareStatement(
"INSERT INTO {table_prefix}CB_AUTH_PERMISSIONS" +
"(SUBJECT_ID,PERMISSION_ID,GRANT_TIME,GRANTED_BY) VALUES(?,?,?,?)")
) {
@@ -1503,6 +1520,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
}
}
protected Set<String> getUserPermissions(String userId, String authRole) throws DBException {
return getUserPermissions(userId);
}
@@ -1584,7 +1602,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
}
}
private Set<String> getAnonymousUserPermissions() throws DBException {
protected Set<String> getAnonymousUserPermissions() throws DBException {
var anonymousUserTeam = application.getAppConfiguration().getAnonymousUserTeam();
return getSubjectPermissions(anonymousUserTeam);
}
@@ -2994,7 +3012,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
}
private void addSubjectPermissionsUpdateEvent(@NotNull String subjectId, @Nullable SMSubjectType subjectType) {
protected void addSubjectPermissionsUpdateEvent(@NotNull String subjectId, @Nullable SMSubjectType subjectType) {
if (subjectType == null) {
subjectType = getSubjectType(subjectId);
}
@@ -3408,7 +3426,7 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
}
}
private SMSubjectType getSubjectType(@NotNull String subjectId) {
protected SMSubjectType getSubjectType(@NotNull String subjectId) {
try (Connection dbCon = database.openConnection()) {
String sqlBuilder = "SELECT SUBJECT_TYPE FROM {table_prefix}CB_AUTH_SUBJECT U WHERE SUBJECT_ID = ?";
try (var dbStat = dbCon.prepareStatement(sqlBuilder)) {
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,7 +46,9 @@ public class EmbeddedSecurityControllerFactory<T extends ServletAuthApplication>
SMCredentialsProvider credentialsProvider,
SMControllerConfiguration smConfig
) throws DBException {
boolean initialization = false;
if (DB_INSTANCE == null) {
initialization = true;
synchronized (EmbeddedSecurityControllerFactory.class) {
if (DB_INSTANCE == null) {
DB_INSTANCE = createAndInitDatabaseInstance(
@@ -64,9 +66,13 @@ public class EmbeddedSecurityControllerFactory<T extends ServletAuthApplication>
)).schedule();
}
}
return createEmbeddedSecurityController(
var controller = createEmbeddedSecurityController(
application, DB_INSTANCE, credentialsProvider, smConfig
);
if (initialization) {
controller.initialize();
}
return controller;
}
protected @NotNull CBDatabase createAndInitDatabaseInstance(
@@ -24,7 +24,6 @@ import io.cloudbeaver.model.app.ServletApplication;
import io.cloudbeaver.model.config.WebDatabaseConfig;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import org.jkiss.dbeaver.model.sql.db.InternalProxyConnection;
import io.cloudbeaver.utils.ServletAppUtils;
import org.apache.commons.dbcp2.*;
import org.apache.commons.pool2.impl.GenericObjectPool;
@@ -44,6 +43,7 @@ import org.jkiss.dbeaver.model.security.SMAdminController;
import org.jkiss.dbeaver.model.security.user.SMTeam;
import org.jkiss.dbeaver.model.security.user.SMUser;
import org.jkiss.dbeaver.model.sql.db.InternalDB;
import org.jkiss.dbeaver.model.sql.db.InternalProxyConnection;
import org.jkiss.dbeaver.model.sql.schema.SQLSchemaConfig;
import org.jkiss.dbeaver.registry.DataSourceProviderRegistry;
import org.jkiss.dbeaver.registry.storage.H2Migrator;
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: CloudBeaver SLF4j Binding
Bundle-SymbolicName: io.cloudbeaver.slf4j;singleton:=true
Bundle-Version: 1.0.41.qualifier
Bundle-Release-Date: 20250721
Bundle-Version: 1.0.43.qualifier
Bundle-Release-Date: 20250818
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Fragment-Host: slf4j.api
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.slf4j</artifactId>
<version>1.0.41-SNAPSHOT</version>
<version>1.0.43-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>

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