mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
54
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f5ac673cf | ||
|
|
25247321b8 | ||
|
|
72975abef0 | ||
|
|
1fadf5009d | ||
|
|
23090e286a | ||
|
|
1cacf6a5cc | ||
|
|
d0e86745f1 | ||
|
|
97d179a103 | ||
|
|
37b8716aee | ||
|
|
d1c2d6547f | ||
|
|
4f53ece164 | ||
|
|
0fe432f45c | ||
|
|
58b8eafc7b | ||
|
|
cdb4c7e1fc | ||
|
|
3a2d4f807d | ||
|
|
39ea1e2b7f | ||
|
|
da7d4eef19 | ||
|
|
d23ab441ff | ||
|
|
220c59ed01 | ||
|
|
eb0f3db330 | ||
|
|
3babafaecd | ||
|
|
4765629537 | ||
|
|
534526e5e4 | ||
|
|
2d6ebd91f1 | ||
|
|
e6e92f0bb3 | ||
|
|
5a394e648d | ||
|
|
e466462269 | ||
|
|
1c57e670c0 | ||
|
|
3808af50c7 | ||
|
|
390452d8a6 | ||
|
|
b733f395d3 | ||
|
|
e31ae9a439 | ||
|
|
07f60c0034 | ||
|
|
ba32c12b9c | ||
|
|
ca3ce9b79d | ||
|
|
96d6dba83a | ||
|
|
6ce5ef235c | ||
|
|
a1404e3495 | ||
|
|
d4e2f2365a | ||
|
|
ab813e21ab | ||
|
|
c5190b2567 | ||
|
|
e896789cb0 | ||
|
|
60827d769a | ||
|
|
65876e9b4e | ||
|
|
048206266d | ||
|
|
f41b9d24fe | ||
|
|
03dabc0e1b | ||
|
|
56946742c2 | ||
|
|
e1e4ae0ba6 | ||
|
|
80f258b561 | ||
|
|
ac48e13039 | ||
|
|
3877e914c4 | ||
|
|
564ceb7d82 | ||
|
|
622b5133c2 |
@@ -49,21 +49,14 @@ jobs:
|
||||
node-version: "lts/*"
|
||||
cache: "yarn"
|
||||
cache-dependency-path: |
|
||||
cloudbeaver/common-*/yarn.lock
|
||||
cloudbeaver/webapp/yarn.lock
|
||||
|
||||
- name: restore cloudbeaver-common typescript cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: "cloudbeaver/common-*/**/*/lib"
|
||||
key: ${{ runner.os }}-common-dist-${{ hashFiles('cloudbeaver/common-*/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-common-dist-
|
||||
|
||||
- name: restore typescript cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: "cloudbeaver/webapp/**/packages/*/dist"
|
||||
path: |
|
||||
"cloudbeaver/webapp/common-*/**/*/lib"
|
||||
"cloudbeaver/webapp/**/packages/*/dist"
|
||||
key: ${{ runner.os }}-dist-${{ hashFiles('cloudbeaver/webapp/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-dist-
|
||||
@@ -71,23 +64,11 @@ jobs:
|
||||
- if: inputs.clean-cache
|
||||
run: yarn clear
|
||||
|
||||
- run: yarn install --immutable
|
||||
working-directory: ./cloudbeaver/common-typescript
|
||||
|
||||
- run: yarn install --immutable
|
||||
working-directory: ./cloudbeaver/common-react
|
||||
|
||||
- run: yarn install --immutable
|
||||
|
||||
- run: yarn bundle
|
||||
working-directory: ./cloudbeaver/webapp/packages/product-default
|
||||
|
||||
- run: yarn test
|
||||
working-directory: ./cloudbeaver/common-typescript
|
||||
|
||||
- run: yarn test
|
||||
working-directory: ./cloudbeaver/common-react
|
||||
|
||||
- run: yarn test
|
||||
|
||||
# - name: Archive build artifacts
|
||||
|
||||
@@ -30,10 +30,10 @@ jobs:
|
||||
cache-dependency-path: "./cloudbeaver/webapp/yarn.lock"
|
||||
|
||||
# - run: yarn install --immutable
|
||||
# working-directory: ./cloudbeaver/common-typescript
|
||||
# working-directory: ./cloudbeaver/webapp/common-typescript
|
||||
|
||||
# - run: yarn install --immutable
|
||||
# working-directory: ./cloudbeaver/common-react
|
||||
# working-directory: ./cloudbeaver/webapp/common-react
|
||||
|
||||
- run: |
|
||||
yarn install --immutable
|
||||
|
||||
Vendored
+1
-1
@@ -48,7 +48,7 @@
|
||||
"-Declipse.pde.launch=true",
|
||||
"-XstartOnFirstThread",
|
||||
"-Dfile.encoding=UTF-8",
|
||||
"--add-modules=ALL-SYSTEM",
|
||||
"--add-modules=ALL-DEFAULT",
|
||||
"--add-opens=java.base/java.io=ALL-UNNAMED",
|
||||
"--add-opens=java.base/java.lang=ALL-UNNAMED",
|
||||
"--add-opens=java.base/java.lang.reflect=ALL-UNNAMED",
|
||||
|
||||
Vendored
+12
-3
@@ -26,7 +26,7 @@
|
||||
"group": "test",
|
||||
"args": ["test"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/common-typescript"
|
||||
"cwd": "${workspaceFolder}/webapp/common-typescript"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -36,7 +36,7 @@
|
||||
"group": "test",
|
||||
"args": ["test"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/common-react"
|
||||
"cwd": "${workspaceFolder}/webapp/common-react"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -133,9 +133,18 @@
|
||||
"type": "shell",
|
||||
"command": "yarn",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/common-react/@dbeaver/ui-kit"
|
||||
"cwd": "${workspaceFolder}/webapp/common-react/@dbeaver/ui-kit"
|
||||
},
|
||||
"args": ["docs"]
|
||||
},
|
||||
{
|
||||
"label": "Run Storybook",
|
||||
"type": "shell",
|
||||
"command": "yarn",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/webapp/packages/storybook"
|
||||
},
|
||||
"args": ["storybook"]
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
|
||||
Vendored
Vendored
Vendored
+2
-2
@@ -2,6 +2,6 @@
|
||||
<?pde version="3.8"?>
|
||||
<target name="org.jkiss.cloudbeaver.tp">
|
||||
<locations>
|
||||
<location path="../../dbeaver-workspace/dependencies/plugins" type="Directory"/>
|
||||
<location path="../../../dbeaver-workspace/dependencies/plugins" type="Directory"/>
|
||||
</locations>
|
||||
</target>
|
||||
</target>
|
||||
@@ -25,67 +25,42 @@ You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
|
||||
|
||||
## Changelog
|
||||
|
||||
### 25.1.5 2025-08-18
|
||||
- Added support for multiple server URLs to accommodate different access policies for internal and external users, improving flexibility in network-restricted environments. Use the Allowed Server URLs field in the Server configuration within the Administration section to set it up;
|
||||
- Added the Force HTTPS mode setting in Server Configuration to enforce secure communication between the server and clients, recommended for production environments. This ensures all connections use HTTPS, improving security when the proxy is correctly configured. This setting is disabled by default;
|
||||
- Added the CLOUDBEAVER_BIND_SESSION_TO_IP option to improve session security by linking user sessions to their IP address. It is disabled by default. When enabled, this helps protect against certain types of session hijacking attacks, where an attacker could try to take over a user’s session. Note: If a user’s internet connection changes (e.g., switching networks or using mobile data), they will be automatically - Improved dark theme accessibility by enhancing color contrast across the app for better readability and compliance with accessibility standards;
|
||||
- The database navigator now automatically hides empty folders in shared projects, reducing visual clutter and speeding up the process of finding active connections.
|
||||
### 25.2.1 2025-09-22
|
||||
- Autocomplete values in SQL Editor are ordered alphabetically to simplify object search.
|
||||
- "Show all databases" setting was added on the connection page for MySQL and MariaDB.
|
||||
- Different bug fixes and enhancements have been made.
|
||||
|
||||
### 25.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 Editor;
|
||||
- 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;
|
||||
- Changed the data transfer mechanism to avoid intermediate file creation. The parameter dataExportFileSizeLimit was removed from the server configuration as deprecated;
|
||||
- PostgresSQL: fixed misplaced comment for table DDL generation.
|
||||
|
||||
### 25.1.1 2025-06-23
|
||||
### 25.2.0 2025-09-01
|
||||
### Changes since 25.1.0:
|
||||
- Added a search panel for SQL Editor and Value panel: press Ctrl+F to open a panel that allows searching and replacing by keyword or regular expression.
|
||||
- Added a new welcome screen for a freshly opened application. This screen contains shortcuts to create a new connection, open SQL editor or documentation.
|
||||
- Databend database support has been added.
|
||||
|
||||
### 25.1.0 2025-06-02
|
||||
### Changes since 25.0.0:
|
||||
- Important:
|
||||
- Connection templates were removed from the application. You can use the Connection Editor to create new connections.
|
||||
- Administration:
|
||||
- Log records now capture create, update, and delete actions for users, teams, and connections for improved tracking and transparency.
|
||||
- LDAP authorization:
|
||||
- Added support for secure [LDAP authentication over SSL](https://github.com/dbeaver/cloudbeaver/wiki/LDAP-Authentication);
|
||||
- Brute force protection is now supported for LDAP authentication. All authentication methods on the application level have this security mechanism.
|
||||
- Added support for multiple server URLs to accommodate different access policies for internal and external users, improving flexibility in network-restricted environments. To set it up, use the Allowed Server URLs field in the Server configuration within the Administration section;
|
||||
- Added the Force HTTPS mode setting in Server Configuration to enforce redirecting from HTTP to HTTPS. This setting helps avoid a potential man-in-the-middle attack. This setting is turned off by default. Please remember to configure your proxy before enabling;
|
||||
- Added the CLOUDBEAVER_BIND_SESSION_TO_IP option to improve session security by linking user sessions to their IP address. It is disabled by default. When enabled, this helps protect against certain types of session hijacking attacks, where an attacker could try to take over a user’s session. Note: Users will be logged out automatically on the IP address change (switching networks or using mobile data);
|
||||
- A password confirmation field has been added for administrators in the Easy Config section to prevent accidental misconfigurations.
|
||||
- SQL Editor:
|
||||
- A new setting to autosave scripts in the SQL editor appeared in global and users' preferences;
|
||||
- Auto-completion has been improved for aliases and camelCase entities.
|
||||
- Changed the default engine used for autocompletion in the SQL Editor. This Semantic engine offers improved suggestions for database objects, keywords, and functions. You can switch back to the Legacy engine in Preferences > SQL Editor;
|
||||
- A “Clear” button was added to the output panel.
|
||||
- Data Editor:
|
||||
- Improved table rendering to scale column width depending on the content;
|
||||
- Accessibility: improved keyboard navigation by sorting buttons in column headers.
|
||||
- Column descriptions were added in the Data Editor under column names to provide more metadata context. You can disable this in Preferences > Data Viewer;
|
||||
- Added an ability to review the script before execution when users edit tables without primary keys.
|
||||
- General:
|
||||
- Added the ability to configure the default database or schema in the connection configuration;
|
||||
- Enhanced initial configuration stage security: a server needs to be restarted if the initial setup time exceeds 1 hour;
|
||||
- Improved Navigation tree performance: lost connection does not lead to the application freezing anymore;
|
||||
- Data transfer: Improved export/import functionality performance by optimizing disk memory consumption;
|
||||
- Vietnamese localization has been added (thanks to [0xhanh](https://github.com/0xhanh)).
|
||||
- Added a new welcome screen for a freshly opened application. This screen contains shortcuts to create a new connection, open SQL Editor, or documentation;
|
||||
- Added a search panel for SQL Editor and Value panel: press Ctrl/Cmd + F to open a panel that allows searching and replacing by keyword or regular expression;
|
||||
- Changed the data transfer mechanism to avoid intermediate file creation. The parameter dataExportFileSizeLimit was removed from the server configuration as deprecated;
|
||||
- The CloudBeaver default theme matches the device theme by default now. You can change this behavior in user preferences under the Theme section;
|
||||
- Added the option to display tabs across multiple rows, allowing you to see all tabs without scrolling. You can enable this in Preferences > Interface;
|
||||
- Improved dark theme accessibility by enhancing color contrast across the app for better readability and compliance with accessibility standards;
|
||||
- The database navigator now automatically hides empty folders in shared projects, reducing visual clutter and speeding up the process of finding active connections.
|
||||
- Databases and drivers:
|
||||
- Clickhouse: driver has been updated to version 0.8.5;
|
||||
- DuckDB: expanded the list of system objects that can be hidden in the Navigation tree;
|
||||
- LibSQL: added the ability to connect using token authentication;
|
||||
- Oracle: users' DDL in Oracle is displayed in the metadata editor on the corresponding tab for users with Oracle administration permissions;
|
||||
- SQLite: added information about a table's Strict mode to the table metadata section (thanks to [eusebe-cda](https://github.com/eusebe-cda)).
|
||||
- Clickhouse: fixed the presentation of tuples and map data types in the Data Editor;
|
||||
- Databend database support has been added (thanks to @hantmac);
|
||||
- DuckDB: driver has been updated to version 1.3;
|
||||
- MySQL: Improved performance when retrieving foreign keys metadata;
|
||||
- PostgreSQL: fixed misplaced comment for table DDL generation.
|
||||
|
||||
## Contribution
|
||||
As a community-driven open-source project, we warmly welcome contributions through GitHub pull requests.
|
||||
|
||||
[We are happy to reward](https://dbeaver.com/help-dbeaver/) our most active contributors every major sprint.
|
||||
The most significant contribution to our code for the major release 25.1.0 was made by:
|
||||
1. [0xhanh](https://github.com/0xhanh) - for Vietnamese localization.
|
||||
The most significant contribution to our code for the major release 25.2.0 was made by:
|
||||
1. [hantmac](https://github.com/hantmac) - added support for Databend in CloudBeaver Community Edition.
|
||||
|
||||
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"search.exclude": {
|
||||
"**/.yarn": true,
|
||||
"**/.pnp.*": true
|
||||
},
|
||||
"typescript.tsdk": ".yarn/sdks/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"prettier.prettierPath": ".yarn/sdks/prettier/index.cjs"
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require prettier/bin/prettier.cjs
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real prettier/bin/prettier.cjs your application uses
|
||||
module.exports = wrapWithUserWrapper(absRequire(`prettier/bin/prettier.cjs`));
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require prettier
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real prettier your application uses
|
||||
module.exports = wrapWithUserWrapper(absRequire(`prettier`));
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "prettier",
|
||||
"version": "3.6.2-sdk",
|
||||
"main": "./index.cjs",
|
||||
"type": "commonjs",
|
||||
"bin": "./bin/prettier.cjs"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
-9
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"search.exclude": {
|
||||
"**/.yarn": true,
|
||||
"**/.pnp.*": true
|
||||
},
|
||||
"prettier.prettierPath": ".yarn/sdks/prettier/index.cjs",
|
||||
"typescript.tsdk": ".yarn/sdks/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
# This file is automatically generated by @yarnpkg/sdks.
|
||||
# Manual changes might be lost!
|
||||
|
||||
integrations:
|
||||
- vscode
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require prettier/bin/prettier.cjs
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real prettier/bin/prettier.cjs your application uses
|
||||
module.exports = wrapWithUserWrapper(absRequire(`prettier/bin/prettier.cjs`));
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require prettier
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real prettier your application uses
|
||||
module.exports = wrapWithUserWrapper(absRequire(`prettier`));
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "prettier",
|
||||
"version": "3.6.2-sdk",
|
||||
"main": "./index.cjs",
|
||||
"type": "commonjs",
|
||||
"bin": "./bin/prettier.cjs"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from './isDefined.js';
|
||||
export * from './isNotNullDefined.js';
|
||||
export * from './mutex.js';
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,12 +5,8 @@ echo "Build static content"
|
||||
|
||||
mkdir -p ./cloudbeaver/web
|
||||
|
||||
cd ../../cloudbeaver/common-typescript
|
||||
cd ../../cloudbeaver/webapp
|
||||
|
||||
yarn install --immutable
|
||||
cd ../common-react
|
||||
yarn install --immutable
|
||||
cd ../webapp
|
||||
yarn install --immutable
|
||||
cd ./packages/product-default
|
||||
yarn run bundle
|
||||
@@ -19,11 +15,7 @@ if [[ "$?" -ne 0 ]] ; then
|
||||
echo 'Application build failed'; exit $rc
|
||||
fi
|
||||
|
||||
cd ../../../common-typescript
|
||||
yarn test
|
||||
cd ../common-react
|
||||
yarn test
|
||||
cd ../webapp
|
||||
cd ../../
|
||||
yarn test
|
||||
|
||||
if [[ "$?" -ne 0 ]] ; then
|
||||
|
||||
@@ -17,7 +17,7 @@ If Not Defined JAVA_OPTS (
|
||||
|
||||
java %JAVA_OPTS% ^
|
||||
-Dfile.encoding=UTF-8 ^
|
||||
--add-modules=ALL-SYSTEM ^
|
||||
--add-modules=ALL-DEFAULT ^
|
||||
--add-opens=java.base/java.io=ALL-UNNAMED ^
|
||||
--add-opens=java.base/java.lang=ALL-UNNAMED ^
|
||||
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED ^
|
||||
|
||||
@@ -11,7 +11,7 @@ echo "Starting Cloudbeaver Server"
|
||||
|
||||
exec java ${JAVA_OPTS} \
|
||||
-Dfile.encoding=UTF-8 \
|
||||
--add-modules=ALL-SYSTEM \
|
||||
--add-modules=ALL-DEFAULT \
|
||||
--add-opens=java.base/java.io=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.lang=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED \
|
||||
@@ -36,6 +36,6 @@ exec java ${JAVA_OPTS} \
|
||||
-jar ${launcherJar} \
|
||||
-product io.cloudbeaver.product.ce.product \
|
||||
-web-config conf/cloudbeaver.conf \
|
||||
${CLI_OPTS} \
|
||||
"$@" \
|
||||
-nl en \
|
||||
-registryMultiLanguage
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -Eeo pipefail
|
||||
CLI_OPTS="$@"
|
||||
if [[ $# -eq 0 ]] ;
|
||||
then
|
||||
CLI_OPTS='-help'
|
||||
set -- "$@" -help
|
||||
fi
|
||||
source run-server.sh
|
||||
set -- "$@" -cli-mode
|
||||
|
||||
source run-server.sh "$@"
|
||||
@@ -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.84.qualifier
|
||||
Bundle-Release-Date: 20250901
|
||||
Bundle-Version: 1.0.86.qualifier
|
||||
Bundle-Release-Date: 20251006
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
|
||||
@@ -10,4 +10,10 @@ meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.deploymentType.n
|
||||
meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.smDbUrl.name=Security manager database URL
|
||||
meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.smDbDriverName.name=Security manager database driver name
|
||||
meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.smDbProductName.name=Security manager database product name
|
||||
meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.smDbProductVersion.name=Security manager database product version
|
||||
meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.smDbProductVersion.name=Security manager database product version
|
||||
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.readOnly.name = Read-only connection
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommit.name = Auto-commit
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.name = Keep-alive interval (seconds)
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.defaultSchema.name = Default schema
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.defaultCatalog.name = Default catalog
|
||||
@@ -0,0 +1,2 @@
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.name = Garder actif (en secondes)
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.description = Pas de déconnexion automatique
|
||||
@@ -0,0 +1,5 @@
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.readOnly.name = Только для чтения
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommit.name = Авто-коммит
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.name = Интервал Keep-alive (в секундах)
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.defaultSchema.name = Схема по умолчанию
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.defaultCatalog.name = Каталог по умолчанию
|
||||
@@ -0,0 +1,4 @@
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.readOnly.name = Kết nối chỉ đọc
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommit.name = Tự động commit
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.name = Giữ kết nối (tính bằng giây)
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.description = Không tự động ngắt kết nối
|
||||
@@ -0,0 +1,3 @@
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommit.name = 自动提交
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.name = 保持连接 (秒)
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.description = 无自动断开连接
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model</artifactId>
|
||||
<version>1.0.84-SNAPSHOT</version>
|
||||
<version>1.0.86-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+2
-1
@@ -35,6 +35,7 @@ import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
import org.jkiss.dbeaver.model.struct.DBSObjectFilter;
|
||||
import org.jkiss.dbeaver.registry.DataSourceConfigurationManager;
|
||||
import org.jkiss.dbeaver.registry.DataSourceParseResults;
|
||||
import org.jkiss.dbeaver.registry.DataSourcePersistentRegistry;
|
||||
import org.jkiss.dbeaver.registry.DataSourceRegistry;
|
||||
|
||||
@@ -377,7 +378,7 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadDataSources(
|
||||
public DataSourceParseResults loadDataSources(
|
||||
@NotNull List<DBPDataSourceConfigurationStorage> storages,
|
||||
@NotNull DataSourceConfigurationManager manager,
|
||||
@Nullable Collection<String> dataSourceIds, boolean refresh,
|
||||
|
||||
@@ -29,13 +29,16 @@ import org.jkiss.dbeaver.model.navigator.DBNModel;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.registry.DataSourceRegistry;
|
||||
import org.jkiss.dbeaver.runtime.jobs.DisconnectJob;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
private static final Log log = Log.getLog(WebSessionProjectImpl.class);
|
||||
@@ -194,40 +197,31 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
/**
|
||||
* updates data sources based on event in web session
|
||||
*
|
||||
* @param dataSourceIds list of updated connections
|
||||
* @param eventId id of event
|
||||
* @param event data source updated event
|
||||
*/
|
||||
public synchronized boolean updateProjectDataSources(@NotNull List<String> dataSourceIds, @NotNull String eventId) {
|
||||
public synchronized boolean updateProjectDataSources(@NotNull WSDataSourceEvent event) {
|
||||
var sendDataSourceUpdatedEvent = false;
|
||||
DBPDataSourceRegistry registry = getDataSourceRegistry();
|
||||
// save old connections
|
||||
var oldDataSources = dataSourceIds.stream()
|
||||
.map(registry::getDataSource)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(
|
||||
DBPDataSourceContainer::getId,
|
||||
registry::createDataSource)
|
||||
);
|
||||
if (WSDataSourceEvent.CREATED.equals(eventId) || WSDataSourceEvent.UPDATED.equals(eventId)) {
|
||||
registry.refreshConfig(dataSourceIds);
|
||||
if (WSDataSourceEvent.CREATED.equals(event.getId()) || WSDataSourceEvent.UPDATED.equals(event.getId())) {
|
||||
registry.refreshConfig(event.getDataSourceIds());
|
||||
}
|
||||
for (String dsId : dataSourceIds) {
|
||||
for (String dsId : event.getDataSourceIds()) {
|
||||
DataSourceDescriptor ds = (DataSourceDescriptor) registry.getDataSource(dsId);
|
||||
if (ds == null) {
|
||||
continue;
|
||||
}
|
||||
switch (eventId) {
|
||||
switch (event.getId()) {
|
||||
case WSDataSourceEvent.CREATED -> {
|
||||
addConnection(ds);
|
||||
sendDataSourceUpdatedEvent = true;
|
||||
}
|
||||
case WSDataSourceEvent.UPDATED -> {
|
||||
boolean connectionUpdated = !ds.equalSettings(oldDataSources.get(dsId));
|
||||
if (connectionUpdated) {
|
||||
sendDataSourceUpdatedEvent = true;
|
||||
if (event.getProperty() == WSDataSourceProperty.CONFIGURATION) {
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds);
|
||||
}
|
||||
// if settings were changed we need to send event
|
||||
if (event.getProperty() != WSDataSourceProperty.INTERNAL) {
|
||||
sendDataSourceUpdatedEvent = true;
|
||||
}
|
||||
}
|
||||
case WSDataSourceEvent.DELETED -> {
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds);
|
||||
|
||||
+24
-6
@@ -56,7 +56,9 @@ public class WebConnectionConfig {
|
||||
private boolean saveCredentials;
|
||||
private boolean sharedCredentials;
|
||||
private Map<String, Object> mainPropertyValues;
|
||||
private Map<String, Object> expertSettingsValues;
|
||||
private Map<String, Object> providerProperties;
|
||||
private Map<String, Object> externalParameters;
|
||||
private List<WebNetworkHandlerConfigInput> networkHandlersConfig;
|
||||
private DBPDriverConfigurationType configurationType;
|
||||
private String selectedSecretId;
|
||||
@@ -71,7 +73,6 @@ public class WebConnectionConfig {
|
||||
if (!CommonUtils.isEmpty(params)) {
|
||||
connectionId = JSONUtils.getString(params, "connectionId");
|
||||
driverId = JSONUtils.getString(params, "driverId");
|
||||
readOnly = JSONUtils.getBoolean(params, "readOnly");
|
||||
|
||||
host = JSONUtils.getString(params, "host");
|
||||
port = JSONUtils.getString(params, "port");
|
||||
@@ -79,9 +80,6 @@ public class WebConnectionConfig {
|
||||
databaseName = JSONUtils.getString(params, "databaseName");
|
||||
url = JSONUtils.getString(params, "url");
|
||||
|
||||
keepAliveInterval = JSONUtils.getInteger(params, "keepAliveInterval", -1);
|
||||
defaultAutoCommit = JSONUtils.getBoolean(params, "autocommit", true);
|
||||
|
||||
name = JSONUtils.getString(params, "name");
|
||||
description = JSONUtils.getString(params, "description");
|
||||
folder = JSONUtils.getString(params, "folder");
|
||||
@@ -98,11 +96,22 @@ public class WebConnectionConfig {
|
||||
|
||||
mainPropertyValues = JSONUtils.getObjectOrNull(params, "mainPropertyValues");
|
||||
providerProperties = JSONUtils.getObjectOrNull(params, "providerProperties");
|
||||
defaultCatalogName = JSONUtils.getString(params, "defaultCatalogName");
|
||||
defaultSchemaName = JSONUtils.getString(params, "defaultSchemaName");
|
||||
|
||||
expertSettingsValues = JSONUtils.getObjectOrNull(params, "expertSettingsValues");
|
||||
keepAliveInterval = JSONUtils.getInteger(
|
||||
expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_KEEP_ALIVE_INTERVAL, -1);
|
||||
readOnly = JSONUtils.getBoolean(
|
||||
expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_READ_ONLY);
|
||||
defaultAutoCommit = JSONUtils.getBoolean(
|
||||
expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_AUTO_COMMIT, true);
|
||||
defaultCatalogName = JSONUtils.getString(
|
||||
expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_DEFAULT_CATALOG);
|
||||
defaultSchemaName = JSONUtils.getString(
|
||||
expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_DEFAULT_SCHEMA);
|
||||
|
||||
String configType = JSONUtils.getString(params, "configurationType");
|
||||
configurationType = configType == null ? null : DBPDriverConfigurationType.valueOf(configType);
|
||||
externalParameters = JSONUtils.getObjectOrNull(params, "externalParameters");
|
||||
|
||||
networkHandlersConfig = new ArrayList<>();
|
||||
for (Map<String, Object> nhc : JSONUtils.getObjectList(params, "networkHandlersConfig")) {
|
||||
@@ -219,6 +228,11 @@ public class WebConnectionConfig {
|
||||
return mainPropertyValues;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getExpertSettingsValues() {
|
||||
return expertSettingsValues;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getProviderProperties() {
|
||||
return providerProperties;
|
||||
@@ -248,4 +262,8 @@ public class WebConnectionConfig {
|
||||
public String getDefaultSchemaName() {
|
||||
return defaultSchemaName;
|
||||
}
|
||||
|
||||
public Map<String, Object> getExternalParameters() {
|
||||
return externalParameters;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +417,17 @@ public class WebConnectionInfo {
|
||||
return mainProperties;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getExpertSettingsValues() {
|
||||
Map<String, Object> expertSettings = new LinkedHashMap<>();
|
||||
expertSettings.put(WebExpertSettingsProperties.PROP_AUTO_COMMIT, isAutocommit());
|
||||
expertSettings.put(WebExpertSettingsProperties.PROP_KEEP_ALIVE_INTERVAL, getKeepAliveInterval());
|
||||
expertSettings.put(WebExpertSettingsProperties.PROP_READ_ONLY, isReadOnly());
|
||||
expertSettings.put(WebExpertSettingsProperties.PROP_DEFAULT_CATALOG, getDefaultCatalogName());
|
||||
expertSettings.put(WebExpertSettingsProperties.PROP_DEFAULT_SCHEMA, getDefaultSchemaName());
|
||||
return expertSettings;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, String> getProviderProperties() {
|
||||
return dataSourceContainer.getConnectionConfiguration().getProviderProperties();
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.DBPObject;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriverConstants;
|
||||
import org.jkiss.dbeaver.model.meta.IPropertyValueValidator;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
/**
|
||||
* Web expert settings properties. Class for returning filtered expert settings properties in web interface.
|
||||
*/
|
||||
public class WebExpertSettingsProperties implements DBPObject {
|
||||
public static final String PROP_READ_ONLY = "readOnly";
|
||||
public static final String PROP_AUTO_COMMIT = "autocommit";
|
||||
public static final String PROP_KEEP_ALIVE_INTERVAL = "keepAliveInterval";
|
||||
public static final String PROP_DEFAULT_CATALOG = "defaultCatalogName";
|
||||
public static final String PROP_DEFAULT_SCHEMA = "defaultSchemaName";
|
||||
|
||||
private final DBPDriver driver;
|
||||
|
||||
public WebExpertSettingsProperties(@NotNull DBPDriver driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
@Property(order = 1, id = PROP_KEEP_ALIVE_INTERVAL, visibleIf = KeepAliveIntervalFieldValidator.class)
|
||||
public int getKeepAliveInterval() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Property(order = 2, id = PROP_AUTO_COMMIT, visibleIf = AutoCommitFieldValidator.class)
|
||||
public boolean isAutoCommit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Property(order = 3, id = PROP_READ_ONLY, visibleIf = ReadOnlyFieldValidator.class)
|
||||
public boolean isReadOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Property(order = 4, id = PROP_DEFAULT_CATALOG, visibleIf = DefaultCatalogFieldVisibleValidator.class)
|
||||
public String getDefaultCatalog() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Property(order = 5, id = PROP_DEFAULT_SCHEMA, visibleIf = DefaultSchemaFieldVisibleValidator.class)
|
||||
public String getDefaultSchema() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static class KeepAliveIntervalFieldValidator implements IPropertyValueValidator<WebExpertSettingsProperties, Object> {
|
||||
@Override
|
||||
public boolean isValidValue(WebExpertSettingsProperties object, Object value) throws IllegalArgumentException {
|
||||
return CommonUtils.toBoolean(!object.driver.isEmbedded(), true);
|
||||
}
|
||||
}
|
||||
|
||||
public static class AutoCommitFieldValidator implements IPropertyValueValidator<WebExpertSettingsProperties, Object> {
|
||||
@Override
|
||||
public boolean isValidValue(WebExpertSettingsProperties object, Object value) throws IllegalArgumentException {
|
||||
return CommonUtils.toBoolean(object.driver.getDriverParameter(DBPDriverConstants.PARAM_SUPPORTS_TRANSACTIONS), true);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ReadOnlyFieldValidator implements IPropertyValueValidator<WebExpertSettingsProperties, Object> {
|
||||
@Override
|
||||
public boolean isValidValue(WebExpertSettingsProperties object, Object value) throws IllegalArgumentException {
|
||||
return CommonUtils.toBoolean(object.driver.getDriverParameter(DBPDriverConstants.PARAM_SUPPORTS_READ_ONLY_MODE), true);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DefaultCatalogFieldVisibleValidator implements IPropertyValueValidator<WebExpertSettingsProperties, Object> {
|
||||
@Override
|
||||
public boolean isValidValue(WebExpertSettingsProperties object, Object value) throws IllegalArgumentException {
|
||||
return CommonUtils.toBoolean(object.driver.getDriverParameter(DBPDriverConstants.PARAM_SUPPORTS_CATALOG_SELECTION), true);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DefaultSchemaFieldVisibleValidator implements IPropertyValueValidator<WebExpertSettingsProperties, Object> {
|
||||
@Override
|
||||
public boolean isValidValue(WebExpertSettingsProperties object, Object value) throws IllegalArgumentException {
|
||||
return CommonUtils.toBoolean(object.driver.getDriverParameter(DBPDriverConstants.PARAM_SUPPORTS_SCHEMA_SELECTION), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -198,6 +198,7 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object start(IApplicationContext context) {
|
||||
initializeApplicationServices();
|
||||
@@ -210,8 +211,9 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
startServer();
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
return EXIT_ERROR_UNSPECIFIED;
|
||||
}
|
||||
return null;
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
protected abstract void startServer() throws DBException;
|
||||
|
||||
+3
-3
@@ -20,7 +20,7 @@ import org.apache.commons.cli.CommandLine;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
|
||||
import org.jkiss.dbeaver.model.cli.ApplicationInstanceServer;
|
||||
import org.jkiss.dbeaver.model.cli.CmdProcessResult;
|
||||
import org.jkiss.dbeaver.model.cli.CLIProcessResult;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -31,7 +31,7 @@ public class CloudBeaverInstanceServer extends ApplicationInstanceServer<Applica
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CmdProcessResult handleCommandLine(@NotNull String[] args) {
|
||||
public CLIProcessResult handleCommandLine(@NotNull String[] args) {
|
||||
CommandLine cmd = CloudBeaverCommandLine.getInstance().getCommandLine(args);
|
||||
try {
|
||||
return CloudBeaverCommandLine.getInstance().executeCommandLineCommands(
|
||||
@@ -40,7 +40,7 @@ public class CloudBeaverInstanceServer extends ApplicationInstanceServer<Applica
|
||||
false
|
||||
);
|
||||
} catch (Exception e) {
|
||||
return new CmdProcessResult(CmdProcessResult.PostAction.ERROR, "Error executing command: " + e.getMessage());
|
||||
return new CLIProcessResult(CLIProcessResult.PostAction.ERROR, "Error executing command: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.cloudbeaver.model.log;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a field or method as sensitive, so its value won't be logged.
|
||||
*/
|
||||
@Target(value = {ElementType.METHOD, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Sensitive {
|
||||
}
|
||||
+17
-4
@@ -149,27 +149,37 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String projectId,
|
||||
@NotNull String configuration,
|
||||
@Nullable List<String> dataSourceIds
|
||||
) throws DBException {
|
||||
return updateProjectDataSourcesConfig(projectId, configuration, dataSourceIds) != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected DataSourceParseResults updateProjectDataSourcesConfig(
|
||||
@NotNull String projectId,
|
||||
@NotNull String configuration,
|
||||
@Nullable List<String> dataSourceIds
|
||||
) throws DBException {
|
||||
try (var lock = lockController.lock(projectId, "updateProjectDataSources")) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
return doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
return doFileWriteOperation(
|
||||
projectId, project.getMetadataFolder(false),
|
||||
() -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
DBPDataSourceConfigurationStorage storage = new DataSourceMemoryStorage(configuration.getBytes(
|
||||
StandardCharsets.UTF_8));
|
||||
DataSourceConfigurationManager manager = new DataSourceConfigurationManagerBuffer();
|
||||
var configChanged = ((DataSourcePersistentRegistry) registry).loadDataSources(
|
||||
final DataSourceParseResults parseResults = ((DataSourcePersistentRegistry) registry).loadDataSources(
|
||||
List.of(storage),
|
||||
manager,
|
||||
dataSourceIds,
|
||||
true,
|
||||
false
|
||||
dataSourceIds == null
|
||||
);
|
||||
registry.checkForErrors();
|
||||
log.debug("Save data sources configuration in project '" + projectId + "'");
|
||||
((DataSourcePersistentRegistry) registry).saveDataSources();
|
||||
registry.checkForErrors();
|
||||
return configChanged;
|
||||
return parseResults;
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -217,6 +227,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
var parent = result.getParent();
|
||||
var parentFolder = parent == null ? null : registry.getFolder(parent.toString().replace("\\", "/"));
|
||||
DBPDataSourceFolder newFolder = registry.addFolder(parentFolder, newName);
|
||||
((DataSourcePersistentRegistry) registry).saveDataSources();
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
}
|
||||
@@ -244,6 +255,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
log.warn("Can not find folder by path [" + folderPath + "] for deletion");
|
||||
}
|
||||
}
|
||||
((DataSourcePersistentRegistry) registry).saveDataSources();
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
}
|
||||
@@ -265,6 +277,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
registry.moveFolder(oldPath, newPath);
|
||||
registry.checkForErrors();
|
||||
((DataSourcePersistentRegistry) registry).saveDataSources();
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
+241
@@ -28,18 +28,26 @@ import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentials;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
|
||||
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNLocalFolder;
|
||||
import org.jkiss.dbeaver.model.rm.*;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.sql.DBQuotaException;
|
||||
import org.jkiss.dbeaver.model.websocket.event.MessageType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDatasourceFolderEvent;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.registry.DataSourceParseResults;
|
||||
import org.jkiss.dbeaver.registry.ResourceTypeDescriptor;
|
||||
import org.jkiss.dbeaver.registry.ResourceTypeRegistry;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
@@ -334,6 +342,239 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
return project;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createProjectDataSources(
|
||||
@NotNull String projectId,
|
||||
@NotNull String configuration,
|
||||
@Nullable List<String> dataSourceIds
|
||||
) throws DBException {
|
||||
super.createProjectDataSources(projectId, configuration, dataSourceIds);
|
||||
if (credentialsProvider.getActiveUserCredentials() != null && dataSourceIds != null) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDataSourceEvent.create(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
projectId,
|
||||
dataSourceIds,
|
||||
WSDataSourceProperty.CONFIGURATION
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateProjectDataSources(
|
||||
@NotNull String projectId,
|
||||
@NotNull String configuration,
|
||||
@Nullable List<String> dataSourceIds
|
||||
) throws DBException {
|
||||
DBPDataSourceRegistry registry = getWebProject(projectId, false).getDataSourceRegistry();
|
||||
Map<String, DataSourceDescriptor> oldDataSources = registry.getDataSources().stream()
|
||||
.filter(ds -> dataSourceIds == null || dataSourceIds.contains(ds.getId()))
|
||||
.collect(Collectors.toMap(
|
||||
DBPDataSourceContainer::getId,
|
||||
registry::createDataSource
|
||||
)
|
||||
);
|
||||
DataSourceParseResults parseResults = super.updateProjectDataSourcesConfig(projectId, configuration, dataSourceIds);
|
||||
sendDataSourcesConfigUpdatedEvent(registry, oldDataSources, parseResults);
|
||||
return parseResults != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProjectDataSources(@NotNull String projectId, @NotNull String[] dataSourceIds) throws DBException {
|
||||
super.deleteProjectDataSources(projectId, dataSourceIds);
|
||||
if (credentialsProvider.getActiveUserCredentials() != null && dataSourceIds.length > 0) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDataSourceEvent.delete(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
projectId,
|
||||
Arrays.asList(dataSourceIds),
|
||||
WSDataSourceProperty.CONFIGURATION
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createProjectDataSourceFolder(@NotNull String projectId, @NotNull String folderPath) throws DBException {
|
||||
super.createProjectDataSourceFolder(projectId, folderPath);
|
||||
if (credentialsProvider.getActiveUserCredentials() != null) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDatasourceFolderEvent.create(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
projectId,
|
||||
List.of(createNodePathFromFolderPath(projectId, folderPath))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveProjectDataSourceFolder(
|
||||
@NotNull String projectId,
|
||||
@NotNull String oldPath,
|
||||
@NotNull String newPath
|
||||
) throws DBException {
|
||||
super.moveProjectDataSourceFolder(projectId, oldPath, newPath);
|
||||
if (credentialsProvider.getActiveUserCredentials() != null) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDatasourceFolderEvent.delete(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
projectId,
|
||||
List.of(createNodePathFromFolderPath(projectId, oldPath))
|
||||
)
|
||||
);
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDatasourceFolderEvent.create(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
projectId,
|
||||
List.of(createNodePathFromFolderPath(projectId, newPath))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProjectDataSourceFolders(@NotNull String projectId, @NotNull String[] folderPaths, boolean dropContents)
|
||||
throws DBException {
|
||||
super.deleteProjectDataSourceFolders(projectId, folderPaths, dropContents);
|
||||
if (credentialsProvider.getActiveUserCredentials() != null) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDatasourceFolderEvent.create(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
projectId,
|
||||
Arrays.stream(folderPaths).map(
|
||||
p -> createNodePathFromFolderPath(projectId, p)
|
||||
).collect(Collectors.toList())
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private String createNodePathFromFolderPath(String projectId, String folderPath) {
|
||||
return DBNLocalFolder.makeLocalFolderItemPath(projectId, folderPath);
|
||||
}
|
||||
|
||||
private void sendDataSourcesConfigUpdatedEvent(
|
||||
@NotNull DBPDataSourceRegistry registry,
|
||||
@NotNull Map<String, DataSourceDescriptor> oldDataSources,
|
||||
@Nullable DataSourceParseResults parseResults
|
||||
) {
|
||||
if (parseResults == null || credentialsProvider.getActiveUserCredentials() == null || oldDataSources.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> updatedConfigurationDataSourceIds = new ArrayList<>();
|
||||
List<String> updatedNameDataSourceIds = new ArrayList<>();
|
||||
List<String> updatedInternalConfigurationDataSourceIds = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, DataSourceDescriptor> entry : oldDataSources.entrySet()) {
|
||||
String dsId = entry.getKey();
|
||||
DataSourceDescriptor oldDs = entry.getValue();
|
||||
DataSourceDescriptor newDs = (DataSourceDescriptor) registry.getDataSource(dsId);
|
||||
if (newDs == null) {
|
||||
continue;
|
||||
}
|
||||
if (!oldDs.equalConfiguration(newDs)) {
|
||||
updatedConfigurationDataSourceIds.add(dsId);
|
||||
} else if (!oldDs.isLooselyEqualTo(newDs)) {
|
||||
updatedNameDataSourceIds.add(dsId);
|
||||
} else if (!oldDs.equalInternalConfiguration(newDs)) {
|
||||
updatedInternalConfigurationDataSourceIds.add(dsId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!updatedConfigurationDataSourceIds.isEmpty()) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDataSourceEvent.update(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
registry.getProject().getId(),
|
||||
updatedConfigurationDataSourceIds,
|
||||
WSDataSourceProperty.CONFIGURATION
|
||||
)
|
||||
);
|
||||
}
|
||||
if (!updatedNameDataSourceIds.isEmpty()) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDataSourceEvent.update(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
registry.getProject().getId(),
|
||||
updatedNameDataSourceIds,
|
||||
WSDataSourceProperty.NAME
|
||||
)
|
||||
);
|
||||
}
|
||||
if (!updatedInternalConfigurationDataSourceIds.isEmpty()) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDataSourceEvent.update(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
registry.getProject().getId(),
|
||||
updatedInternalConfigurationDataSourceIds,
|
||||
WSDataSourceProperty.INTERNAL
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!parseResults.addedDataSources.isEmpty()) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDataSourceEvent.create(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
registry.getProject().getId(),
|
||||
updatedNameDataSourceIds,
|
||||
WSDataSourceProperty.CONFIGURATION
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!parseResults.removedDataSources.isEmpty()) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDataSourceEvent.delete(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
registry.getProject().getId(),
|
||||
updatedNameDataSourceIds,
|
||||
WSDataSourceProperty.CONFIGURATION
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!parseResults.addedFolders.isEmpty()) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDatasourceFolderEvent.create(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
registry.getProject().getId(),
|
||||
parseResults.addedFolders.stream().map(
|
||||
f -> createNodePathFromFolderPath(registry.getProject().getId(), f.getFolderPath())
|
||||
).toList()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!parseResults.removedFolders.isEmpty()) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDatasourceFolderEvent.delete(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
registry.getProject().getId(),
|
||||
parseResults.removedFolders.stream().map(
|
||||
f -> createNodePathFromFolderPath(registry.getProject().getId(), f.getFolderPath())
|
||||
).toList()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public RMResource[] listResources(
|
||||
|
||||
+2
-1
@@ -18,9 +18,10 @@ package io.cloudbeaver.model.session;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.utils.HttpConstants;
|
||||
|
||||
public class WebHttpRequestInfo {
|
||||
public static final String USER_AGENT = "User-Agent";
|
||||
public static final String USER_AGENT = HttpConstants.HEADER_USER_AGENT;
|
||||
|
||||
@Nullable
|
||||
private final String id;
|
||||
|
||||
+3
-1
@@ -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.
|
||||
@@ -118,6 +118,8 @@ public class WebSessionAuthProcessor {
|
||||
// We may need to associate new credentials with active user
|
||||
if (linkWithActiveUser) {
|
||||
securityController.setCurrentUserCredentials(authProviderDescriptor.getId(), authAttrs);
|
||||
//link only first(main) credentials
|
||||
alreadyLoggedIn = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -62,12 +62,12 @@ public class WebSessionPreferenceStore extends AbstractUserPreferenceStore {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultString(String name) {
|
||||
public String getDefaultString(@NotNull String name) {
|
||||
return parentStore.getDefaultString(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefault(String name) {
|
||||
public boolean isDefault(@NotNull String name) {
|
||||
return !userPreferences.containsKey(name) && parentStore.isDefault(name);
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class WebSessionPreferenceStore extends AbstractUserPreferenceStore {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToDefault(String name) {
|
||||
public void setToDefault(@NotNull String name) {
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
|
||||
@@ -98,4 +98,5 @@ public class CBConstants {
|
||||
|
||||
public static final String BIND_SESSION_ENABLE = "enable";
|
||||
public static final String BIND_SESSION_DISABLE = "disable";
|
||||
public static final String DEFAULT_CLOUD_PROJECT_NAME = "GlobalConfiguration";
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
public interface HttpConstants {
|
||||
String CONTENT_TYPE = "Content-Type";
|
||||
|
||||
String TYPE_JSON = "application/json";
|
||||
String HEADER_AUTHORIZATION = "Authorization";
|
||||
}
|
||||
+28
-27
@@ -18,6 +18,7 @@ package io.cloudbeaver.server;
|
||||
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.impl.preferences.AbstractPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
|
||||
@@ -34,37 +35,37 @@ public class WebServerPreferenceStore extends AbstractPreferenceStore {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String name) {
|
||||
public boolean contains(@NotNull String name) {
|
||||
return productConf().containsKey(name) || parentStore.contains(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String name) {
|
||||
public boolean getBoolean(@NotNull String name) {
|
||||
return toBoolean(getString(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(String name) {
|
||||
public double getDouble(@NotNull String name) {
|
||||
return toDouble(getString(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(String name) {
|
||||
public float getFloat(@NotNull String name) {
|
||||
return toFloat(getString(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String name) {
|
||||
public int getInt(@NotNull String name) {
|
||||
return toInt(getString(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(String name) {
|
||||
public long getLong(@NotNull String name) {
|
||||
return toLong(getString(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String name) {
|
||||
public String getString(@NotNull String name) {
|
||||
Object value = productConf().get(name);
|
||||
if (value == null) {
|
||||
return parentStore.getString(name);
|
||||
@@ -73,38 +74,38 @@ public class WebServerPreferenceStore extends AbstractPreferenceStore {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDefaultBoolean(String name) {
|
||||
public boolean getDefaultBoolean(@NotNull String name) {
|
||||
return getBoolean(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDefaultDouble(String name) {
|
||||
public double getDefaultDouble(@NotNull String name) {
|
||||
return getDouble(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDefaultFloat(String name) {
|
||||
public float getDefaultFloat(@NotNull String name) {
|
||||
return getFloat(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultInt(String name) {
|
||||
public int getDefaultInt(@NotNull String name) {
|
||||
return getInt(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDefaultLong(String name) {
|
||||
public long getDefaultLong(@NotNull String name) {
|
||||
return getLong(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultString(String name) {
|
||||
public String getDefaultString(@NotNull String name) {
|
||||
// TODO: split product.conf and runtime.product.conf
|
||||
return getString(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefault(String name) {
|
||||
public boolean isDefault(@NotNull String name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -114,68 +115,68 @@ public class WebServerPreferenceStore extends AbstractPreferenceStore {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefault(String name, double value) {
|
||||
public void setDefault(@NotNull String name, double value) {
|
||||
setDefault(name, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefault(String name, float value) {
|
||||
public void setDefault(@NotNull String name, float value) {
|
||||
setDefault(name, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefault(String name, int value) {
|
||||
public void setDefault(@NotNull String name, int value) {
|
||||
setDefault(name, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefault(String name, long value) {
|
||||
public void setDefault(@NotNull String name, long value) {
|
||||
setDefault(name, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefault(String name, String defaultObject) {
|
||||
public void setDefault(@NotNull String name, @Nullable String defaultObject) {
|
||||
// do not store global default properties in product.conf
|
||||
this.parentStore.setDefault(name, defaultObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefault(String name, boolean value) {
|
||||
public void setDefault(@NotNull String name, boolean value) {
|
||||
setDefault(name, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToDefault(String name) {
|
||||
public void setToDefault(@NotNull String name) {
|
||||
parentStore.setToDefault(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String name, double value) {
|
||||
public void setValue(@NotNull String name, double value) {
|
||||
parentStore.setValue(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String name, float value) {
|
||||
public void setValue(@NotNull String name, float value) {
|
||||
parentStore.setValue(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String name, int value) {
|
||||
public void setValue(@NotNull String name, int value) {
|
||||
parentStore.setValue(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String name, long value) {
|
||||
public void setValue(@NotNull String name, long value) {
|
||||
parentStore.setValue(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String name, String value) {
|
||||
public void setValue(@NotNull String name, @Nullable String value) {
|
||||
parentStore.setValue(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String name, boolean value) {
|
||||
public void setValue(@NotNull String name, boolean value) {
|
||||
parentStore.setValue(name, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ public class ServletAppUtils {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getOriginFromRequestOrThrow(HttpServletRequest request) throws DBWebException {
|
||||
public static String getOriginFromRequest(@NotNull HttpServletRequest request) {
|
||||
String origin = request.getHeader(HEADER_ORIGIN);
|
||||
if (CommonUtils.isEmpty(origin)) {
|
||||
origin = request.getHeader(HEADER_X_ORIGIN);
|
||||
@@ -353,7 +353,22 @@ public class ServletAppUtils {
|
||||
if (CommonUtils.isEmpty(rootUri)) {
|
||||
return uri;
|
||||
}
|
||||
String[] split = uri.split(rootUri);
|
||||
return split[0];
|
||||
rootUri = "/" + rootUri;
|
||||
if (!uri.endsWith(rootUri)) {
|
||||
rootUri = rootUri + "/";
|
||||
}
|
||||
return substringBeforeLast(uri, rootUri);
|
||||
}
|
||||
|
||||
public static String substringBeforeLast(
|
||||
@NotNull String string,
|
||||
@NotNull String separator
|
||||
) {
|
||||
if (CommonUtils.isNotEmpty(string) && CommonUtils.isNotEmpty(separator)) {
|
||||
int pos = string.lastIndexOf(separator);
|
||||
return pos == -1 ? string : string.substring(0, pos);
|
||||
} else {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-6
@@ -21,7 +21,6 @@ import io.cloudbeaver.model.WebConnectionFolderInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceFolder;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
public class WebConnectionFolderUtils {
|
||||
@@ -45,9 +44,4 @@ public class WebConnectionFolderUtils {
|
||||
throw new DBWebException("Folder name '" + folderName + "' contains illegal characters: /");
|
||||
}
|
||||
}
|
||||
|
||||
public static DBPDataSourceFolder createFolder(WebConnectionFolderInfo parentFolder, String newName, DBPDataSourceRegistry registry) throws DBWebException {
|
||||
DBPDataSourceFolder folder = registry.addFolder(parentFolder == null ? null : parentFolder.getDataSourceFolder(), newName);
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Community Product
|
||||
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
|
||||
Bundle-Version: 25.2.0.qualifier
|
||||
Bundle-Release-Date: 20250901
|
||||
Bundle-Version: 25.2.2.qualifier
|
||||
Bundle-Release-Date: 20251006
|
||||
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.2.0-SNAPSHOT</version>
|
||||
<version>25.2.2-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Cloudbeaver Base JDBC drivers
|
||||
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
|
||||
Bundle-Version: 1.0.129.qualifier
|
||||
Bundle-Release-Date: 20250901
|
||||
Bundle-Version: 1.0.131.qualifier
|
||||
Bundle-Release-Date: 20251006
|
||||
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.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 CE Server
|
||||
Bundle-SymbolicName: io.cloudbeaver.server.ce;singleton:=true
|
||||
Bundle-Version: 25.2.0.qualifier
|
||||
Bundle-Release-Date: 20250901
|
||||
Bundle-Version: 25.2.2.qualifier
|
||||
Bundle-Release-Date: 20251006
|
||||
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.2.0-SNAPSHOT</version>
|
||||
<version>25.2.2-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+3
-12
@@ -103,7 +103,6 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
// Persistence
|
||||
protected SMAdminController securityController;
|
||||
private boolean configurationMode = false;
|
||||
private String localHostAddress;
|
||||
protected String containerId;
|
||||
private final List<InetAddress> localInetAddresses = new ArrayList<>();
|
||||
|
||||
@@ -226,15 +225,6 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
|
||||
eventController.setForceSkipEvents(isConfigurationMode()); // do not send events if configuration mode is on
|
||||
|
||||
// Determine address for local host
|
||||
localHostAddress = System.getenv(CBConstants.VAR_CB_LOCAL_HOST_ADDR);
|
||||
if (CommonUtils.isEmpty(localHostAddress)) {
|
||||
localHostAddress = System.getProperty(CBConstants.VAR_CB_LOCAL_HOST_ADDR);
|
||||
}
|
||||
if (CommonUtils.isEmpty(localHostAddress) || CBConstants.HOST_127_0_0_1.equals(localHostAddress) || "::0".equals(
|
||||
localHostAddress)) {
|
||||
localHostAddress = CBConstants.HOST_LOCALHOST;
|
||||
}
|
||||
|
||||
Location instanceLoc = Platform.getInstanceLocation();
|
||||
try {
|
||||
@@ -515,9 +505,10 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getDefaultProjectName() {
|
||||
return "GlobalConfiguration";
|
||||
return CBConstants.DEFAULT_CLOUD_PROJECT_NAME;
|
||||
}
|
||||
|
||||
public boolean isDevelMode() {
|
||||
@@ -529,7 +520,7 @@ public abstract class CBApplication<T extends CBServerConfig>
|
||||
}
|
||||
|
||||
public String getLocalHostAddress() {
|
||||
return localHostAddress;
|
||||
return getServerConfigurationController().getLocalHostAddress();
|
||||
}
|
||||
|
||||
public List<InetAddress> getLocalInetAddresses() {
|
||||
|
||||
+15
-8
@@ -44,8 +44,6 @@ import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -66,6 +64,7 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
|
||||
protected final Path homeDirectory;
|
||||
private final Map<String, String> externalProperties = new LinkedHashMap<>();
|
||||
private final Map<String, Object> originalConfigurationProperties = new LinkedHashMap<>();
|
||||
private String localHostAddress;
|
||||
|
||||
protected CBServerConfigurationController(@NotNull T serverConfiguration, @NotNull Path homeDirectory) {
|
||||
super(homeDirectory);
|
||||
@@ -80,6 +79,15 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
|
||||
@Override
|
||||
public void loadServerConfiguration(Path configPath) throws DBException {
|
||||
log.debug("Using configuration [" + configPath + "]");
|
||||
// Determine address for local host
|
||||
localHostAddress = System.getenv(CBConstants.VAR_CB_LOCAL_HOST_ADDR);
|
||||
if (CommonUtils.isEmpty(localHostAddress)) {
|
||||
localHostAddress = System.getProperty(CBConstants.VAR_CB_LOCAL_HOST_ADDR);
|
||||
}
|
||||
if (CommonUtils.isEmpty(localHostAddress) || CBConstants.HOST_127_0_0_1.equals(localHostAddress) || "::0".equals(
|
||||
localHostAddress)) {
|
||||
localHostAddress = CBConstants.HOST_LOCALHOST;
|
||||
}
|
||||
|
||||
if (!Files.exists(configPath)) {
|
||||
log.error("Configuration file " + configPath + " doesn't exist. Use defaults.");
|
||||
@@ -162,12 +170,7 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
|
||||
if (config.getServerURL() == null) {
|
||||
String hostName = config.getServerHost();
|
||||
if (CommonUtils.isEmpty(hostName)) {
|
||||
try {
|
||||
hostName = InetAddress.getLocalHost().getHostName();
|
||||
} catch (UnknownHostException e) {
|
||||
log.debug("Error resolving localhost address: " + e.getMessage());
|
||||
hostName = CBConstants.HOST_LOCALHOST;
|
||||
}
|
||||
hostName = getLocalHostAddress();
|
||||
}
|
||||
config.setServerURL("http://" + hostName + ":" + config.getServerPort());
|
||||
}
|
||||
@@ -669,4 +672,8 @@ public abstract class CBServerConfigurationController<T extends CBServerConfig>
|
||||
public void validateFinalServerConfiguration() throws DBException {
|
||||
|
||||
}
|
||||
|
||||
public String getLocalHostAddress() {
|
||||
return localHostAddress;
|
||||
}
|
||||
}
|
||||
-25
@@ -41,30 +41,5 @@ public class WSUserEventHandler<EVENT extends WSAbstractEvent> implements WSEven
|
||||
case WSUserDisabledEvent e -> sessionManager.closeUserSession(e);
|
||||
default -> { }
|
||||
}
|
||||
|
||||
switch (event.getId()) {
|
||||
case WSUserCloseSessionsEvent.ID:
|
||||
if (event instanceof WSUserCloseSessionsEvent closeSessionsEvent) {
|
||||
if (closeSessionsEvent.getSessionIds().isEmpty()) {
|
||||
sessionManager.closeAllSessions(closeSessionsEvent.getSessionId());
|
||||
} else {
|
||||
sessionManager.closeSessions(closeSessionsEvent.getSessionIds());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WSUserDeletedEvent.ID:
|
||||
if (event instanceof WSUserDeletedEvent userDeletedEvent) {
|
||||
sessionManager.closeUserSession(userDeletedEvent);
|
||||
}
|
||||
break;
|
||||
case WSUserDisabledEvent.ID:
|
||||
if (event instanceof WSUserDisabledEvent userDisabledEvent) {
|
||||
sessionManager.closeUserSession(userDisabledEvent);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+7
-2
@@ -120,12 +120,13 @@ public class CBJettyServer {
|
||||
|
||||
GraphQLEndpoint endpoint = new GraphQLEndpoint(new ServerConfigurationTimeLimitFilter(application));
|
||||
application.addApplicationContextValue(GraphQL.class.getName(), endpoint.getGraphQL());
|
||||
String gqlServletPath = serverConfiguration.getServicesURI() + "gql/*";
|
||||
servletContextHandler.addServlet(
|
||||
new ServletHolder(
|
||||
"graphql",
|
||||
endpoint
|
||||
),
|
||||
serverConfiguration.getServicesURI() + "gql/*"
|
||||
gqlServletPath
|
||||
);
|
||||
servletContextHandler.addEventListener(new CBServerContextListener(application));
|
||||
|
||||
@@ -157,7 +158,11 @@ public class CBJettyServer {
|
||||
}
|
||||
}
|
||||
}
|
||||
FilterHolder hostsFilter = new FilterHolder(new RequestHostFilter(application, excludedFilterPaths));
|
||||
FilterHolder hostsFilter = new FilterHolder(new RequestHostFilter(
|
||||
application,
|
||||
excludedFilterPaths,
|
||||
Set.of(gqlServletPath)
|
||||
));
|
||||
servletContextHandler.addFilter(hostsFilter, "/*", null);
|
||||
|
||||
|
||||
|
||||
+45
-14
@@ -39,23 +39,35 @@ public class RequestHostFilter implements Filter {
|
||||
@NotNull
|
||||
private final CBApplication<?> application;
|
||||
private final Set<String> excludedPaths = new HashSet<>();
|
||||
private final Set<String> errorPaths = new HashSet<>();
|
||||
|
||||
public RequestHostFilter(@NotNull CBApplication<?> application, @NotNull Set<String> excludedPaths) {
|
||||
public RequestHostFilter(
|
||||
@NotNull CBApplication<?> application,
|
||||
@NotNull Set<String> excludedPaths,
|
||||
@NotNull Set<String> errorPaths
|
||||
) {
|
||||
this.application = application;
|
||||
this.excludedPaths.addAll(
|
||||
excludedPaths.stream()
|
||||
.map(path -> ServletAppUtils.removeSideSlashes(path.replace("*", "")))
|
||||
.toList()
|
||||
);
|
||||
this.errorPaths.addAll(
|
||||
errorPaths.stream()
|
||||
.map(path -> ServletAppUtils.removeSideSlashes(path.replace("*", "")))
|
||||
.toList()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
boolean requestAllowed = true;
|
||||
|
||||
if (request instanceof HttpServletRequest httpRequest) {
|
||||
CBServerConfig serverConfig = application.getServerConfiguration();
|
||||
URI originUri;
|
||||
try {
|
||||
String origin = ServletAppUtils.getOriginFromRequestOrThrow(httpRequest);
|
||||
String origin = ServletAppUtils.getOriginFromRequest(httpRequest);
|
||||
originUri = URI.create(origin);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to get origin from request", e);
|
||||
@@ -64,7 +76,8 @@ public class RequestHostFilter implements Filter {
|
||||
}
|
||||
boolean isIpAddress = InetAddresses.isInetAddress(originUri.getHost());
|
||||
String servletPath = httpRequest.getServletPath();
|
||||
if (!isIpAddress) {
|
||||
requestAllowed = isIpAddress;
|
||||
if (!requestAllowed) {
|
||||
if (CommonUtils.isNotEmpty(servletPath)) {
|
||||
for (String excludedPath : excludedPaths) {
|
||||
if (servletPath.contains(excludedPath)) {
|
||||
@@ -73,17 +86,21 @@ public class RequestHostFilter implements Filter {
|
||||
}
|
||||
}
|
||||
}
|
||||
validateHosts(serverConfig, httpRequest, response, originUri);
|
||||
requestAllowed = validateHosts(serverConfig, httpRequest, (HttpServletResponse) response, originUri);
|
||||
}
|
||||
if (requestAllowed) {
|
||||
requestAllowed = validateSchema(serverConfig, httpRequest, (HttpServletResponse) response, originUri);
|
||||
}
|
||||
validateSchema(serverConfig, httpRequest, response, originUri);
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
if (requestAllowed) {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateSchema(
|
||||
private boolean validateSchema(
|
||||
@NotNull CBServerConfig serverConfig,
|
||||
@NotNull HttpServletRequest httpRequest,
|
||||
@NotNull ServletResponse response,
|
||||
@NotNull HttpServletResponse response,
|
||||
@NotNull URI originUri
|
||||
) {
|
||||
boolean httpsExpected = serverConfig.isForceHttps();
|
||||
@@ -100,22 +117,24 @@ public class RequestHostFilter implements Filter {
|
||||
redirectUrlBuilder.append("?")
|
||||
.append(httpRequest.getQueryString());
|
||||
}
|
||||
((HttpServletResponse) response).sendRedirect(redirectUrlBuilder.toString());
|
||||
response.sendRedirect(redirectUrlBuilder.toString());
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to redirect to HTTPS", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void validateHosts(
|
||||
private boolean validateHosts(
|
||||
@NotNull CBServerConfig serverConfig,
|
||||
@NotNull HttpServletRequest httpRequest,
|
||||
@NotNull ServletResponse response,
|
||||
@NotNull HttpServletResponse response,
|
||||
URI originUri
|
||||
) throws IOException {
|
||||
List<String> availableHosts = serverConfig.getSupportedHosts();
|
||||
if (CommonUtils.isEmpty(availableHosts)) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
var requestHostBuilder = new StringBuilder(originUri.getHost());
|
||||
@@ -124,13 +143,25 @@ public class RequestHostFilter implements Filter {
|
||||
}
|
||||
String requestHost = requestHostBuilder.toString();
|
||||
if (!availableHosts.contains(requestHost)) {
|
||||
for (String errorPath : errorPaths) {
|
||||
if (httpRequest.getServletPath().contains(errorPath)) {
|
||||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||
response.getWriter().write(
|
||||
"Request host '" + requestHost + "' is not allowed. Available hosts: " + availableHosts
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
log.warn("Request host '" + requestHost + "' is not allowed. Redirect to default: " + availableHosts);
|
||||
redirectToDefaultHost((HttpServletResponse) response, httpRequest, availableHosts);
|
||||
redirectToDefaultHost(response, httpRequest, availableHosts);
|
||||
return false;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.error(e.getMessage(), e);
|
||||
redirectToDefaultHost((HttpServletResponse) response, httpRequest, availableHosts);
|
||||
redirectToDefaultHost(response, httpRequest, availableHosts);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void redirectToDefaultHost(
|
||||
|
||||
@@ -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.2.0.qualifier
|
||||
Bundle-Release-Date: 20250901
|
||||
Bundle-Version: 25.2.2.qualifier
|
||||
Bundle-Release-Date: 20251006
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
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
|
||||
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 =Логировать GraphQL-запросы в консоль
|
||||
log.api.graphql.debug.description = Включите подробное логирование запросов GraphQL в журнале сервера, включая все предоставленные переменные
|
||||
log.api.graphql.debug.description = Включите подробное логирование запросов GraphQL в журнале сервера, включая все предоставленные переменные
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server</artifactId>
|
||||
<version>25.2.0-SNAPSHOT</version>
|
||||
<version>25.2.2-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -351,6 +351,9 @@ type DriverInfo {
|
||||
"""
|
||||
providerProperties: [ObjectPropertyInfo!]!
|
||||
|
||||
"Expert driver settings properties. Returns properties (like keep-alive interval) that are not often used and can be hidden in UI"
|
||||
expertSettingsProperties: [ObjectPropertyInfo!]! @since(version: "25.2.1")
|
||||
|
||||
"False for drivers which do not support authentication."
|
||||
anonymousAccess: Boolean
|
||||
"Default auth model that is used for this driver (see authModels)"
|
||||
@@ -486,6 +489,9 @@ type ConnectionInfo {
|
||||
"Main connection properties. Contains host, port, database, server name fields"
|
||||
mainPropertyValues: Object @since(version: "24.1.2")
|
||||
|
||||
"Expert connection settings. Contains expert settings properties (like keep-alive interval or auto-commit) that are not often used"
|
||||
expertSettingsValues: Object @since(version: "25.2.1")
|
||||
|
||||
"Connection keep-alive interval in seconds"
|
||||
keepAliveInterval: Int!
|
||||
"Defines if the connection is in auto-commit mode"
|
||||
@@ -672,6 +678,9 @@ input ConnectionConfig {
|
||||
"Host, port, serverName, databaseName are also stored in mainPropertyValues for custom pages"
|
||||
mainPropertyValues: Object @since(version: "24.1.2")
|
||||
|
||||
"Keep-alive, auto-commit, read-only and other expert settings are stored in expertSettingsValues"
|
||||
expertSettingsValues: Object @since(version: "25.2.1")
|
||||
|
||||
"Sets connection URL jdbc:{driver}://{host}[:{port}]/[{database}]"
|
||||
url: String
|
||||
|
||||
@@ -679,13 +688,13 @@ input ConnectionConfig {
|
||||
properties: Object
|
||||
|
||||
"Set keep-alive interval"
|
||||
keepAliveInterval: Int
|
||||
keepAliveInterval: Int @deprecated(reason: "25.2.1 use expertPropertyValues instead")
|
||||
|
||||
"Sets auto-commit connection state"
|
||||
autocommit: Boolean
|
||||
autocommit: Boolean @deprecated(reason: "25.2.1 use expertPropertyValues instead")
|
||||
|
||||
"Sets read-only connection state"
|
||||
readOnly: Boolean
|
||||
readOnly: Boolean @deprecated(reason: "25.2.1 use expertPropertyValues instead")
|
||||
|
||||
# User credentials
|
||||
|
||||
@@ -721,9 +730,9 @@ input ConnectionConfig {
|
||||
"Configuration type (MANUAL, URL)"
|
||||
configurationType: DriverConfigurationType
|
||||
"Sets catalog name for the connection"
|
||||
defaultCatalogName: String @since(version: "25.0.5")
|
||||
defaultCatalogName: String @since(version: "25.0.5") @deprecated(reason: "25.2.1 use expertPropertyValues instead")
|
||||
"Sets schema name for the connection"
|
||||
defaultSchemaName: String @since(version: "25.0.5")
|
||||
defaultSchemaName: String @since(version: "25.0.5") @deprecated(reason: "25.2.1 use expertPropertyValues instead")
|
||||
}
|
||||
|
||||
####################################################
|
||||
|
||||
@@ -41,7 +41,6 @@ import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBConstants;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBPObject;
|
||||
import org.jkiss.dbeaver.model.access.DBAAuthCredentials;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
@@ -318,9 +317,11 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
return container.getName() + " [" + container.getId() + "]";
|
||||
}
|
||||
|
||||
public static void updateConfigAndRefreshDatabases(WebSession session, String projectId) throws DBWebException {
|
||||
public static void refreshDatabases(WebSession session, String projectId) throws DBWebException {
|
||||
DBNProject projectNode = session.getNavigatorModelOrThrow().getRoot().getProjectNode(session.getProjectById(projectId));
|
||||
DBNModel.updateConfigAndRefreshDatabases(projectNode.getDatabases());
|
||||
if (projectNode != null) {
|
||||
projectNode.getDatabases().refreshChildren();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isGlobalProject(DBPProject project) {
|
||||
@@ -363,7 +364,7 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
@NotNull
|
||||
public static WebPropertyInfo[] getObjectFilteredProperties(
|
||||
@NotNull WebSession session,
|
||||
@NotNull DBPObject object,
|
||||
@NotNull Object object,
|
||||
@Nullable WebPropertyFilter filter
|
||||
) {
|
||||
PropertyCollector propertyCollector = new PropertyCollector(object, true);
|
||||
|
||||
+5
-21
@@ -18,10 +18,7 @@ package io.cloudbeaver.model;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.model.app.WebAppConfiguration;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.utils.ConfigurationUtils;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBConstants;
|
||||
@@ -39,8 +36,6 @@ import org.jkiss.utils.CommonUtils;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Web driver configuration
|
||||
@@ -257,29 +252,18 @@ public class WebDatabaseDriverInfo {
|
||||
|
||||
@Property
|
||||
public WebPropertyInfo[] getProviderProperties() {
|
||||
WebPropertyInfo[] additionalWebProperty = Optional.of(WebAppUtils.getWebApplication())
|
||||
.filter(app -> app.getAppConfiguration().isSecretManagerEnabled())
|
||||
.map(app -> app.getConnectionController().getExternalInfo(webSession))
|
||||
.orElse(new WebPropertyInfo[0]);
|
||||
|
||||
WebPropertyInfo[] providerProperties = Arrays.stream(driver.getProviderPropertyDescriptors())
|
||||
return Arrays.stream(driver.getProviderPropertyDescriptors())
|
||||
.map(p -> new WebPropertyInfo(webSession, p, null))
|
||||
.toArray(WebPropertyInfo[]::new);
|
||||
}
|
||||
|
||||
return Stream.concat(
|
||||
Arrays.stream(additionalWebProperty),
|
||||
Arrays.stream(providerProperties)
|
||||
).toArray(WebPropertyInfo[]::new);
|
||||
public WebPropertyInfo[] getExpertSettingsProperties() {
|
||||
return WebServiceUtils.getObjectFilteredProperties(webSession, new WebExpertSettingsProperties(driver), null);
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isEnabled() {
|
||||
WebAppConfiguration config = WebAppUtils.getWebApplication().getAppConfiguration();
|
||||
return ConfigurationUtils.isDriverEnabled(
|
||||
driver,
|
||||
config.getEnabledDrivers(),
|
||||
config.getDisabledDrivers()
|
||||
);
|
||||
return WebServiceUtils.isDriverEnabled(driver);
|
||||
}
|
||||
|
||||
@Property
|
||||
|
||||
+1
-4
@@ -39,10 +39,7 @@ public class WSDataSourceUpdatedEventHandlerImpl extends WSAbstractProjectEventH
|
||||
log.debug("Project " + event.getProjectId() + " is not found in session " + webSession.getSessionId());
|
||||
return;
|
||||
}
|
||||
sendEvent = project.updateProjectDataSources(
|
||||
event.getDataSourceIds(),
|
||||
event.getId()
|
||||
);
|
||||
sendEvent = project.updateProjectDataSources(event);
|
||||
}
|
||||
if (sendEvent) {
|
||||
activeUserSession.addSessionEvent(event);
|
||||
|
||||
+9
-9
@@ -32,7 +32,6 @@ import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.model.apilog.ApiCallInterceptor;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.registry.WebServiceRegistry;
|
||||
import io.cloudbeaver.server.HttpConstants;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.service.DBWBindingContext;
|
||||
import io.cloudbeaver.service.DBWServiceBindingGraphQL;
|
||||
@@ -46,6 +45,7 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.HttpConstants;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -146,7 +146,7 @@ public class GraphQLEndpoint extends HttpServlet {
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
String contentType = request.getContentType();
|
||||
if (CommonUtils.isEmpty(contentType) || !contentType.startsWith(HttpConstants.TYPE_JSON)) {
|
||||
if (CommonUtils.isEmpty(contentType) || !contentType.startsWith(HttpConstants.CONTENT_TYPE_JSON)) {
|
||||
String error = "Bad request," + (CommonUtils.isEmpty(contentType)
|
||||
? " content type is missing"
|
||||
: " incorrect content type:" + contentType);
|
||||
@@ -246,14 +246,14 @@ public class GraphQLEndpoint extends HttpServlet {
|
||||
if (operationName != null) {
|
||||
contextBuilder.operationName(operationName);
|
||||
}
|
||||
String sessionId = GraphQLLoggerUtil.getSmSessionId(request);
|
||||
// String sessionId = GraphQLLoggerUtil.getSmSessionId(request);
|
||||
String userId = GraphQLLoggerUtil.getUserId(request);
|
||||
String loggerMessage = GraphQLLoggerUtil.buildLoggerMessage(sessionId, userId, variables);
|
||||
if (operationName != null) {
|
||||
log.debug("API > " + operationName + loggerMessage);
|
||||
} else if (DEBUG) {
|
||||
log.debug("API > " + query + loggerMessage);
|
||||
}
|
||||
// String loggerMessage = GraphQLLoggerUtil.buildLoggerMessage(sessionId, userId, variables);
|
||||
// if (operationName != null) {
|
||||
// log.debug("API > " + operationName + loggerMessage);
|
||||
// } else if (DEBUG) {
|
||||
// log.debug("API > " + query + loggerMessage);
|
||||
// }
|
||||
LocalDateTime startTime = LocalDateTime.now();
|
||||
ExecutionInput executionInput = contextBuilder.build();
|
||||
ExecutionResult executionResult = null;
|
||||
|
||||
+45
-22
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.server.graphql;
|
||||
|
||||
import io.cloudbeaver.model.log.Sensitive;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.server.WebApplication;
|
||||
@@ -23,7 +24,9 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.Map;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.Set;
|
||||
|
||||
public class GraphQLLoggerUtil {
|
||||
@@ -63,43 +66,63 @@ public class GraphQLLoggerUtil {
|
||||
.findWebSession(request);
|
||||
}
|
||||
|
||||
public static String buildLoggerMessage(String sessionId, String userId, Map<String, Object> variables) {
|
||||
public static String buildLoggerMessage(String sessionId, String userId, Method method, Object[] args) {
|
||||
StringBuilder loggerMessage = new StringBuilder(" [user: ").append(userId)
|
||||
.append(", sessionId: ").append(sessionId).append("]");
|
||||
|
||||
if (WebAppUtils.getWebPlatform().getPreferenceStore().getBoolean(LOG_API_GRAPHQL_DEBUG_PARAMETER)
|
||||
&& variables != null
|
||||
// if (WebAppUtils.getWebPlatform().getPreferenceStore().getBoolean(LOG_API_GRAPHQL_DEBUG_PARAMETER)
|
||||
if (true
|
||||
) {
|
||||
loggerMessage.append(" [variables] ");
|
||||
String parsedVariables = parseVarialbes(variables);
|
||||
String parsedVariables = maskArgsToString(method, args);
|
||||
if (CommonUtils.isNotEmpty(parsedVariables)) {
|
||||
loggerMessage.append(parseVarialbes(variables));
|
||||
loggerMessage.append(parsedVariables);
|
||||
}
|
||||
}
|
||||
return loggerMessage.toString();
|
||||
}
|
||||
|
||||
private static String parseVarialbes(Map<String, Object> map) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
public static String maskArgsToString(Method method, Object[] args) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Parameter[] params = method.getParameters();
|
||||
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
//fixme can't get real parameter names without -parameters compiler option
|
||||
String name = params[i].getName();
|
||||
Object value = (args != null && i < args.length) ? args[i] : null;
|
||||
|
||||
boolean isProhibited = PROHIBITED_VARIABLES.stream()
|
||||
.anyMatch(prohibitedKey -> key.toLowerCase().contains(prohibitedKey.toLowerCase()));
|
||||
sb.append(name).append(": ");
|
||||
|
||||
if (isProhibited) {
|
||||
result.append(key).append(": ").append("******** ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value instanceof Map) {
|
||||
result.append(parseVarialbes((Map<String, Object>) value));
|
||||
if (params[i].isAnnotationPresent(Sensitive.class)) {
|
||||
sb.append("**** ");
|
||||
} else if (value != null && !isSimple(value.getClass())) {
|
||||
sb.append("{ ");
|
||||
for (Field field : value.getClass().getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
sb.append(field.getName()).append(": ");
|
||||
try {
|
||||
if (field.isAnnotationPresent(Sensitive.class)) {
|
||||
sb.append("**** ");
|
||||
} else {
|
||||
sb.append(field.get(value)).append(" ");
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
sb.append("<err> ");
|
||||
}
|
||||
}
|
||||
sb.append("} ");
|
||||
} else {
|
||||
result.append(key).append(": ").append(value).append(" ");
|
||||
sb.append(value).append(" ");
|
||||
}
|
||||
}
|
||||
return result.toString().trim();
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
private static boolean isSimple(Class<?> cls) {
|
||||
return cls.isPrimitive()
|
||||
|| Number.class.isAssignableFrom(cls)
|
||||
|| CharSequence.class.isAssignableFrom(cls)
|
||||
|| Boolean.class.equals(cls)
|
||||
|| Enum.class.isAssignableFrom(cls);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,6 @@ package io.cloudbeaver.server.websockets;
|
||||
|
||||
import io.cloudbeaver.model.session.WebHeadlessSession;
|
||||
import io.cloudbeaver.model.session.WebHttpRequestInfo;
|
||||
import io.cloudbeaver.server.HttpConstants;
|
||||
import io.cloudbeaver.server.WebAppSessionManager;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import jakarta.websocket.HandshakeResponse;
|
||||
@@ -33,6 +32,7 @@ import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.security.exception.SMAccessTokenExpiredException;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.HttpConstants;
|
||||
import org.jkiss.utils.WSClientUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
-3
@@ -21,11 +21,9 @@ import io.cloudbeaver.WebObjectId;
|
||||
import io.cloudbeaver.model.WebConnectionConfig;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.WebNetworkHandlerConfigInput;
|
||||
import io.cloudbeaver.model.WebPropertyInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
@@ -98,5 +96,4 @@ public interface ConnectionController {
|
||||
|
||||
void validateConnection(DBPDataSourceContainer dataSourceContainer) throws DBWebException;
|
||||
|
||||
WebPropertyInfo[] getExternalInfo(WebSession webSession);
|
||||
}
|
||||
|
||||
-52
@@ -20,11 +20,9 @@ import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.model.WebConnectionConfig;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.WebNetworkHandlerConfigInput;
|
||||
import io.cloudbeaver.model.WebPropertyInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
@@ -38,8 +36,6 @@ import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretValue;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.dbeaver.runtime.jobs.ConnectionTestJob;
|
||||
@@ -100,13 +96,6 @@ public class ConnectionControllerCE implements ConnectionController {
|
||||
WebConnectionInfo connectionInfo = project.addConnection(newDataSource);
|
||||
webSession.addInfoMessage("New connection was created - " + WebServiceUtils.getConnectionContainerInfo(
|
||||
newDataSource));
|
||||
WebEventUtils.addDataSourceUpdatedEvent(
|
||||
webSession.getProjectById(projectId),
|
||||
webSession,
|
||||
connectionInfo.getId(),
|
||||
WSConstants.EventAction.CREATE,
|
||||
WSDataSourceProperty.CONFIGURATION
|
||||
);
|
||||
log.info(String.format(
|
||||
"New connection was created: [info=%s, user=%s]",
|
||||
WebServiceUtils.getConnectionContainerInfo(newDataSource),
|
||||
@@ -190,7 +179,6 @@ public class ConnectionControllerCE implements ConnectionController {
|
||||
DBPDataSourceRegistry sessionRegistry
|
||||
) throws DBWebException {
|
||||
WebConnectionInfo connectionInfo = WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, config.getConnectionId());
|
||||
boolean sendEvent = !((DataSourceDescriptor) dataSource).equalSettings(getOldDataSource(dataSource));
|
||||
if (!isSharedCredentials(dataSource)) {
|
||||
// secret controller is responsible for notification, password changes applied after checks
|
||||
WebServiceUtils.saveAuthProperties(
|
||||
@@ -201,24 +189,12 @@ public class ConnectionControllerCE implements ConnectionController {
|
||||
config.isSharedCredentials()
|
||||
);
|
||||
}
|
||||
|
||||
WSDataSourceProperty property = getDatasourceEventProperty(getOldDataSource(dataSource), dataSource);
|
||||
|
||||
try {
|
||||
sessionRegistry.updateDataSource(dataSource);
|
||||
sessionRegistry.checkForErrors();
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Failed to update connection", e);
|
||||
}
|
||||
if (sendEvent) {
|
||||
WebEventUtils.addDataSourceUpdatedEvent(
|
||||
webSession.getProjectById(projectId),
|
||||
webSession,
|
||||
connectionInfo.getId(),
|
||||
WSConstants.EventAction.UPDATE,
|
||||
property
|
||||
);
|
||||
}
|
||||
log.info(String.format(
|
||||
"Connection updated: [info=%s, userId=%s]",
|
||||
WebServiceUtils.getConnectionContainerInfo(dataSource),
|
||||
@@ -238,13 +214,6 @@ public class ConnectionControllerCE implements ConnectionController {
|
||||
webSession.addInfoMessage("Delete connection - " +
|
||||
WebServiceUtils.getConnectionContainerInfo(connectionInfo.getDataSourceContainer()));
|
||||
closeAndDeleteConnection(webSession, projectId, connectionId, true);
|
||||
WebEventUtils.addDataSourceUpdatedEvent(
|
||||
webSession.getProjectById(projectId),
|
||||
webSession,
|
||||
connectionId,
|
||||
WSConstants.EventAction.DELETE,
|
||||
WSDataSourceProperty.CONFIGURATION
|
||||
);
|
||||
|
||||
log.info(String.format(
|
||||
"Connection deleted: [info=%s, userId=%s]",
|
||||
@@ -361,23 +330,6 @@ public class ConnectionControllerCE implements ConnectionController {
|
||||
return project;
|
||||
}
|
||||
|
||||
private WSDataSourceProperty getDatasourceEventProperty(
|
||||
DataSourceDescriptor oldDataSource,
|
||||
DBPDataSourceContainer dataSource
|
||||
) {
|
||||
if (!oldDataSource.equalConfiguration((DataSourceDescriptor) dataSource)) {
|
||||
return WSDataSourceProperty.CONFIGURATION;
|
||||
}
|
||||
|
||||
var nameChanged = !CommonUtils.equalObjects(oldDataSource.getName(), dataSource.getName());
|
||||
var descriptionChanged = !CommonUtils.equalObjects(oldDataSource.getDescription(), dataSource.getDescription());
|
||||
if (nameChanged && descriptionChanged) {
|
||||
return WSDataSourceProperty.CONFIGURATION;
|
||||
}
|
||||
|
||||
return nameChanged ? WSDataSourceProperty.NAME : WSDataSourceProperty.CONFIGURATION;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private WebConnectionInfo closeAndDeleteConnection(
|
||||
@NotNull WebSession webSession,
|
||||
@@ -529,10 +481,6 @@ public class ConnectionControllerCE implements ConnectionController {
|
||||
public void validateConnection(DBPDataSourceContainer dataSourceContainer) throws DBWebException {
|
||||
}
|
||||
|
||||
public WebPropertyInfo[] getExternalInfo(WebSession session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private void validateDriverLibrariesPresence(@NotNull DBPDataSourceContainer container) throws DBWebException {
|
||||
if (!DBWorkbench.isDistributed() && container.getDriver().getDriverLoader(container).needsExternalDependencies()) {
|
||||
|
||||
+19
-7
@@ -27,9 +27,11 @@ import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.session.WebSessionProvider;
|
||||
import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
|
||||
import io.cloudbeaver.server.graphql.GraphQLLoggerUtil;
|
||||
import io.cloudbeaver.service.security.SMUtils;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
@@ -327,16 +329,26 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
}
|
||||
}
|
||||
}
|
||||
// Perform any checks before action call
|
||||
protected void beforeWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
|
||||
|
||||
}
|
||||
HttpServletRequest request = this.env.getGraphQlContext().get("request");
|
||||
String sessionId = GraphQLLoggerUtil.getSmSessionId(request);
|
||||
String userId = GraphQLLoggerUtil.getUserId(request);
|
||||
|
||||
// Perform any checks before action call
|
||||
protected void beforeWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
|
||||
setLogContext(method, args);
|
||||
}
|
||||
String loggerMessage = GraphQLLoggerUtil.buildLoggerMessage(sessionId, userId, method, args);
|
||||
|
||||
if (method.getName() != null) {
|
||||
log.debug("API > " + method.getName() + loggerMessage);
|
||||
}
|
||||
|
||||
setLogContext(method, args);
|
||||
}
|
||||
|
||||
protected void afterWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
|
||||
Log.setContext(null);
|
||||
}
|
||||
|
||||
protected void afterWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
|
||||
Log.setContext(null);
|
||||
}
|
||||
|
||||
protected void setLogContext(Method method, Object[] args) {
|
||||
|
||||
+21
-71
@@ -27,7 +27,10 @@ import io.cloudbeaver.server.WebAppUtils;
|
||||
import io.cloudbeaver.server.WebApplication;
|
||||
import io.cloudbeaver.service.core.DBWServiceCore;
|
||||
import io.cloudbeaver.service.security.SMUtils;
|
||||
import io.cloudbeaver.utils.*;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import io.cloudbeaver.utils.WebCommonUtils;
|
||||
import io.cloudbeaver.utils.WebConnectionFolderUtils;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jkiss.code.NotNull;
|
||||
@@ -41,14 +44,16 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.navigator.*;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNDataSource;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNModel;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNNode;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration;
|
||||
import org.jkiss.dbeaver.model.net.DBWNetworkHandler;
|
||||
import org.jkiss.dbeaver.model.net.DBWTunnel;
|
||||
import org.jkiss.dbeaver.model.net.ssh.SSHSession;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.registry.DataSourceProviderRegistry;
|
||||
@@ -56,7 +61,6 @@ 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;
|
||||
|
||||
import java.util.*;
|
||||
@@ -393,15 +397,7 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
dataSourceRegistry.addDataSource(newDataSource);
|
||||
|
||||
dataSourceRegistry.checkForErrors();
|
||||
WebConnectionInfo connectionInfo = project.addConnection(newDataSource);
|
||||
WebEventUtils.addDataSourceUpdatedEvent(
|
||||
webSession.getProjectById(projectId),
|
||||
webSession,
|
||||
connectionInfo.getId(),
|
||||
WSConstants.EventAction.CREATE,
|
||||
WSDataSourceProperty.CONFIGURATION
|
||||
);
|
||||
return connectionInfo;
|
||||
return project.addConnection(newDataSource);
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error copying connection", e);
|
||||
}
|
||||
@@ -536,25 +532,18 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
) throws DBWebException {
|
||||
WebConnectionFolderUtils.validateConnectionFolder(folderName);
|
||||
session.addInfoMessage("Create new folder");
|
||||
WebConnectionFolderInfo parentNode = null;
|
||||
WebConnectionFolderInfo parentFolder = null;
|
||||
try {
|
||||
if (parentPath != null) {
|
||||
parentNode = WebConnectionFolderUtils.getFolderInfo(session, projectId, parentPath);
|
||||
parentFolder = WebConnectionFolderUtils.getFolderInfo(session, projectId, parentPath);
|
||||
}
|
||||
WebProjectImpl project = getProjectById(session, projectId);
|
||||
DBPDataSourceRegistry sessionRegistry = project.getDataSourceRegistry();
|
||||
DBPDataSourceFolder newFolder = WebConnectionFolderUtils.createFolder(parentNode,
|
||||
folderName,
|
||||
sessionRegistry);
|
||||
WebConnectionFolderInfo folderInfo = new WebConnectionFolderInfo(session, newFolder);
|
||||
WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId);
|
||||
WebEventUtils.addNavigatorNodeUpdatedEvent(
|
||||
session.getProjectById(projectId),
|
||||
session,
|
||||
DBNLocalFolder.makeLocalFolderItemPath(newFolder),
|
||||
WSConstants.EventAction.CREATE
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
DBPDataSourceFolder newFolder = registry.addFolder(
|
||||
parentFolder == null ? null : parentFolder.getDataSourceFolder(), folderName
|
||||
);
|
||||
return folderInfo;
|
||||
WebServiceUtils.refreshDatabases(session, projectId);
|
||||
return new WebConnectionFolderInfo(session, newFolder);
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException(e.getMessage(), e);
|
||||
}
|
||||
@@ -565,26 +554,12 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
@NotNull WebSession session,
|
||||
@Nullable String projectId,
|
||||
@NotNull String folderPath,
|
||||
@NotNull String newName
|
||||
@NotNull String newPath
|
||||
) throws DBWebException {
|
||||
WebConnectionFolderUtils.validateConnectionFolder(newName);
|
||||
WebConnectionFolderUtils.validateConnectionFolder(newPath);
|
||||
WebConnectionFolderInfo folderInfo = WebConnectionFolderUtils.getFolderInfo(session, projectId, folderPath);
|
||||
var oldFolderNode = DBNLocalFolder.makeLocalFolderItemPath(folderInfo.getDataSourceFolder());
|
||||
folderInfo.getDataSourceFolder().setName(newName);
|
||||
var newFolderNode = DBNLocalFolder.makeLocalFolderItemPath(folderInfo.getDataSourceFolder());
|
||||
WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId);
|
||||
WebEventUtils.addNavigatorNodeUpdatedEvent(
|
||||
session.getProjectById(projectId),
|
||||
session,
|
||||
oldFolderNode,
|
||||
WSConstants.EventAction.DELETE
|
||||
);
|
||||
WebEventUtils.addNavigatorNodeUpdatedEvent(
|
||||
session.getProjectById(projectId),
|
||||
session,
|
||||
newFolderNode,
|
||||
WSConstants.EventAction.CREATE
|
||||
);
|
||||
folderInfo.getDataSourceFolder().setName(newPath);
|
||||
WebServiceUtils.refreshDatabases(session, projectId);
|
||||
return folderInfo;
|
||||
}
|
||||
|
||||
@@ -599,17 +574,10 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
if (folder.getDataSourceRegistry().getProject() != project) {
|
||||
throw new DBWebException("Global folder '" + folderInfo.getId() + "' cannot be deleted");
|
||||
}
|
||||
var folderNode = DBNLocalFolder.makeLocalFolderItemPath(folderInfo.getDataSourceFolder());
|
||||
session.addInfoMessage("Delete folder");
|
||||
DBPDataSourceRegistry sessionRegistry = project.getDataSourceRegistry();
|
||||
sessionRegistry.removeFolder(folderInfo.getDataSourceFolder(), false);
|
||||
WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId);
|
||||
WebEventUtils.addNavigatorNodeUpdatedEvent(
|
||||
session.getProjectById(projectId),
|
||||
session,
|
||||
folderNode,
|
||||
WSConstants.EventAction.DELETE
|
||||
);
|
||||
WebServiceUtils.refreshDatabases(session, projectId);
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException(e.getMessage(), e);
|
||||
}
|
||||
@@ -624,12 +592,6 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
DataSourceDescriptor dataSourceDescriptor = ((DataSourceDescriptor) connectionInfo.getDataSourceContainer());
|
||||
dataSourceDescriptor.setNavigatorSettings(settings);
|
||||
dataSourceDescriptor.persistConfiguration();
|
||||
WebEventUtils.addDataSourceUpdatedEvent(
|
||||
webSession.getProjectById(projectId),
|
||||
webSession,
|
||||
id,
|
||||
WSConstants.EventAction.UPDATE,
|
||||
WSDataSourceProperty.CONFIGURATION);
|
||||
return connectionInfo;
|
||||
}
|
||||
|
||||
@@ -656,16 +618,4 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
}
|
||||
return project;
|
||||
}
|
||||
|
||||
private void validateDriverLibrariesPresence(@NotNull DBPDataSourceContainer container) throws DBWebException {
|
||||
if (!DBWorkbench.isDistributed() && container.getDriver().getDriverLoader(container).needsExternalDependencies()) {
|
||||
throwDriverNotFoundException(container);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String throwDriverNotFoundException(@NotNull DBPDataSourceContainer container) throws DBWebException {
|
||||
throw new DBWebException("Driver files for %s are not found. Please ask the administrator to download it."
|
||||
.formatted(container.getDriver().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-40
@@ -55,7 +55,6 @@ import org.jkiss.dbeaver.model.struct.DBSObjectFilter;
|
||||
import org.jkiss.dbeaver.model.struct.rdb.DBSCatalog;
|
||||
import org.jkiss.dbeaver.model.struct.rdb.DBSSchema;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
|
||||
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceProperty;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
@@ -461,28 +460,10 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
if (siblings.contains(newName)) {
|
||||
throw new DBWebException("Name " + newName + " is unavailable or invalid");
|
||||
}
|
||||
var oldNodePath = node.getNodeItemPath();
|
||||
node.rename(session.getProgressMonitor(), newName);
|
||||
var newNodePath = node.getNodeItemPath();
|
||||
addNavigatorNodeMoveEvent(session, node, oldNodePath, newNodePath);
|
||||
return node.getName();
|
||||
}
|
||||
|
||||
private void addNavigatorNodeMoveEvent(@NotNull WebSession session, DBNNode node, String oldNodePath, String newNodePath) {
|
||||
WebEventUtils.addNavigatorNodeUpdatedEvent(
|
||||
node.getOwnerProject(),
|
||||
session,
|
||||
oldNodePath,
|
||||
WSConstants.EventAction.DELETE
|
||||
);
|
||||
WebEventUtils.addNavigatorNodeUpdatedEvent(
|
||||
node.getOwnerProject(),
|
||||
session,
|
||||
newNodePath,
|
||||
WSConstants.EventAction.CREATE
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String renameRmResourceNode(@NotNull WebSession session, DBNNode node, @NotNull String newName) throws DBException {
|
||||
if (newName.contains("/") || newName.contains("\\")) {
|
||||
@@ -570,14 +551,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
throw e;
|
||||
}
|
||||
} else if (node instanceof DBNLocalFolder) {
|
||||
var nodePath = node.getNodeItemPath();
|
||||
node.getOwnerProject().getDataSourceRegistry().removeFolder(((DBNLocalFolder) node).getFolder(), false);
|
||||
WebEventUtils.addNavigatorNodeUpdatedEvent(
|
||||
session.getProjectById(projectId),
|
||||
session,
|
||||
nodePath,
|
||||
WSConstants.EventAction.DELETE
|
||||
);
|
||||
} else if (node instanceof DBNResourceManagerResource) {
|
||||
DBNResourceManagerResource rmResource = ((DBNResourceManagerResource) node);
|
||||
String resourceProjectId = rmResource.getResourceProject().getId();
|
||||
@@ -592,7 +566,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
}
|
||||
}
|
||||
if (containsFolderNodes) {
|
||||
WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId);
|
||||
WebServiceUtils.refreshDatabases(session, projectId);
|
||||
}
|
||||
return nodes.size();
|
||||
|
||||
@@ -645,15 +619,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
folder = localFolderNode.getFolder();
|
||||
}
|
||||
dataSourceNode.moveToFolder(folderNode.getOwnerProject(), folder);
|
||||
node.getOwnerProject().getDataSourceRegistry().updateDataSource(
|
||||
dataSourceNode.getDataSourceContainer());
|
||||
WebEventUtils.addDataSourceUpdatedEvent(
|
||||
node.getOwnerProject(),
|
||||
session,
|
||||
dataSourceNode.getDataSourceContainer().getId(),
|
||||
WSConstants.EventAction.UPDATE,
|
||||
WSDataSourceProperty.CONFIGURATION
|
||||
);
|
||||
node.getOwnerProject().getDataSourceRegistry().updateDataSource(dataSourceNode.getDataSourceContainer());
|
||||
} else if (node instanceof DBNLocalFolder dbnLocalFolder) {
|
||||
DBPDataSourceFolder parentFolder = null;
|
||||
if (folderNode instanceof DBNLocalFolder parentFolderNode) {
|
||||
@@ -667,15 +633,12 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
throw new DBWebException("Node " + folderNodePath + " contains folder with name '" + node.getName() + "'");
|
||||
}
|
||||
}
|
||||
var oldNodePath = node.getNodeItemPath();
|
||||
node.getOwnerProject().getDataSourceRegistry().moveFolder(
|
||||
dbnLocalFolder.getFolder().getFolderPath(),
|
||||
dbnLocalFolder.generateNewFolderPath(parentFolder, dbnLocalFolder.getNodeDisplayName())
|
||||
);
|
||||
node.getOwnerProject().getDataSourceRegistry().checkForErrors();
|
||||
var newNodePath = node.getNodeItemPath();
|
||||
WebServiceUtils.updateConfigAndRefreshDatabases(session, node.getOwnerProject().getId());
|
||||
addNavigatorNodeMoveEvent(session, node, oldNodePath, newNodePath);
|
||||
WebServiceUtils.refreshDatabases(session, node.getOwnerProject().getId());
|
||||
} else if (node instanceof DBNResourceManagerResource) {
|
||||
boolean rmNewNode = folderNode instanceof DBNAbstractResourceManagerNode;
|
||||
DBNResourceManagerResource rmOldNode = (DBNResourceManagerResource) node;
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@ public class WebSQLCompletionContext implements SQLCompletionContext {
|
||||
|
||||
@Override
|
||||
public boolean isSortAlphabetically() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
@@ -67,4 +67,8 @@ public class WebSQLCompletionProposal {
|
||||
return null;
|
||||
}
|
||||
|
||||
public CompletionProposalBase getProposal() {
|
||||
return proposal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
@@ -55,6 +55,7 @@ import org.jkiss.dbeaver.model.sql.parser.SQLParserContext;
|
||||
import org.jkiss.dbeaver.model.sql.parser.SQLScriptParser;
|
||||
import org.jkiss.dbeaver.model.sql.registry.SQLGeneratorConfigurationRegistry;
|
||||
import org.jkiss.dbeaver.model.sql.registry.SQLGeneratorDescriptor;
|
||||
import org.jkiss.dbeaver.model.sql.semantics.completion.SQLCompletionProposalComparator;
|
||||
import org.jkiss.dbeaver.model.sql.semantics.completion.SQLQueryCompletionAnalyzer;
|
||||
import org.jkiss.dbeaver.model.struct.DBSDataContainer;
|
||||
import org.jkiss.dbeaver.model.struct.DBSObject;
|
||||
@@ -200,6 +201,11 @@ public class WebServiceSQL implements DBWServiceSQL {
|
||||
for (int i = 0; i < proposals.size(); i++) {
|
||||
result[i] = new WebSQLCompletionProposal(proposals.get(i));
|
||||
}
|
||||
SQLCompletionProposalComparator sqlCompletionProposalComparator = new SQLCompletionProposalComparator(
|
||||
completionContext.isSortAlphabetically(),
|
||||
completionContext.isSearchInsideNames()
|
||||
);
|
||||
Arrays.sort(result, (o1, o2) -> sqlCompletionProposalComparator.compare(o1.getProposal(), o2.getProposal()));
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error processing SQL proposals", e);
|
||||
|
||||
@@ -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.128.qualifier
|
||||
Bundle-Release-Date: 20250901
|
||||
Bundle-Version: 1.0.130.qualifier
|
||||
Bundle-Release-Date: 20251006
|
||||
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.128-SNAPSHOT</version>
|
||||
<version>1.0.130-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+2
-2
@@ -494,7 +494,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
@NotNull WebSession webSession,
|
||||
@Nullable String providerId
|
||||
) throws DBWebException {
|
||||
String origin = ServletAppUtils.getOriginFromRequestOrThrow(request);
|
||||
String origin = ServletAppUtils.getOriginFromRequest(request);
|
||||
List<WebAuthProviderConfiguration> result = new ArrayList<>();
|
||||
for (SMAuthProviderCustomConfiguration cfg : CBApplication.getInstance().getAppConfiguration().getAuthCustomConfigurations()) {
|
||||
if (providerId != null && !providerId.equals(cfg.getProvider())) {
|
||||
@@ -544,7 +544,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
providerConfig.getProvider(),
|
||||
webSession.getUserId()
|
||||
));
|
||||
return new WebAuthProviderConfiguration(authProvider, providerConfig, ServletAppUtils.getOriginFromRequestOrThrow(request));
|
||||
return new WebAuthProviderConfiguration(authProvider, providerConfig, ServletAppUtils.getOriginFromRequest(request));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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.128.qualifier
|
||||
Bundle-Release-Date: 20250901
|
||||
Bundle-Version: 1.0.130.qualifier
|
||||
Bundle-Release-Date: 20251006
|
||||
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.128-SNAPSHOT</version>
|
||||
<version>1.0.130-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+3
-3
@@ -111,7 +111,7 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
|
||||
}
|
||||
try {
|
||||
Map<String, Object> authParameters = new HashMap<>();
|
||||
authParameters.put(SMConstants.USER_ORIGIN, ServletAppUtils.getOriginFromRequestOrThrow(httpRequest));
|
||||
authParameters.put(SMConstants.USER_ORIGIN, ServletAppUtils.getOriginFromRequest(httpRequest));
|
||||
|
||||
var smAuthInfo = initiateAuthentication(webSession, providerId, providerConfigurationId, authParameters, forceSessionsLogout);
|
||||
if (smAuthInfo.getAuthStatus() != SMAuthStatus.IN_PROGRESS) {
|
||||
@@ -227,7 +227,7 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
|
||||
List<WebAuthInfo> removedInfos = webSession.removeAuthInfo(providerId);
|
||||
List<String> logoutUrls = new ArrayList<>();
|
||||
var cbApp = CBApplication.getInstance();
|
||||
String origin = ServletAppUtils.getOriginFromRequestOrThrow(httpRequest);
|
||||
String origin = ServletAppUtils.getOriginFromRequest(httpRequest);
|
||||
for (WebAuthInfo removedInfo : removedInfos) {
|
||||
if (removedInfo.getAuthProviderDescriptor()
|
||||
.getInstance() instanceof SMSignOutLinkProvider provider
|
||||
@@ -297,7 +297,7 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
|
||||
|
||||
@Override
|
||||
public WebAuthProviderInfo[] getAuthProviders(@NotNull HttpServletRequest request) throws DBWebException {
|
||||
String origin = ServletAppUtils.getOriginFromRequestOrThrow(request);
|
||||
String origin = ServletAppUtils.getOriginFromRequest(request);
|
||||
return WebAuthProviderRegistry.getInstance().getAuthProviders()
|
||||
.stream()
|
||||
.map(descriptor -> new WebAuthProviderInfo(descriptor, origin))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user